通过使用spring security

时间:2017-08-07 11:55:43

标签: spring spring-security thymeleaf

我在项目中使用 thymeleaf-extras-springsecurity4 和弹簧安全保护。问题是我无法获得用户的额外字段(这意味着除username之外的passwordenabledUserDetails等数据库的用户信息使用<span sec:authentication="principal.something" />

Heres是我的简单代码:

UserEntity(实现UserDetails)

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
@Entity
@Table(name = "users", schema = "myschema")
public class UserEntity implements UserDetails {
  @Id
  @GeneratedValue
  @Column(name = "id", nullable = false)
  private int id;

  @Basic
  @Column(name = "username", nullable = false, unique = true, length = 64)
  private String username;

  @Basic
  @Column(name = "password", nullable = false, columnDefinition = "TEXT")
  private String password;

  @Basic
  @Column(name = "enabled", nullable = false, columnDefinition = "BIT")
  private boolean enabled;

  @Basic
  @Column(name = "phone", nullable = false, length = 16)
  private String phone;

  @OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
  private List<AuthorityEntity> authorities;

  @Override
  public boolean isAccountNonExpired() {
    return enabled;
  }

  @Override
  public boolean isAccountNonLocked() {
    return enabled;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return enabled;
  }

  @Override
  public String toString() {
    return username;
  }
}

AuthorityEntity(实现GrantedAuthority)

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "authorities", schema = "myschema",
    uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "authority"}))
public class AuthorityEntity implements GrantedAuthority {
  @Id
  @GeneratedValue
  @Column(name = "id", nullable = false)
  private int id;

  @Basic
  @Column(name = "authority", nullable = false, length = 24)
  private String authority;

  @ManyToOne
  @JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)
  private UserEntity user;
}

UserRepository

@Repository
public interface UserRepository extends JpaRepository<UserEntity, Integer> {
  UserEntity findOneByUsernameAndEnabledTrue(String username);
}

UserService

@Service
public class UserService {
  private UserRepository userRepository;

  @Autowired
  public UserService(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  public UserEntity loadUserByUsername(String username) {
    return userRepository.findOneByUsernameAndEnabledTrue(username);
  }
}

SecurityService(扩展UserDetailService)

@Service
public class SecurityService implements UserDetailsService {
  private UserService userService;

  @Autowired
  public SecurityService(UserService userService) {
    this.userService = userService;
  }

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    UserDetails user = userService.loadUserByUsername(username);
    if (user == null) {
      throw new UsernameNotFoundException(username);
    }
    return user;
  }
}

SecurityConfig(扩展WebSecurityConfigurerAdapter)

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  private SecurityService securityService;

  @Autowired
  public SecurityConfig(SecurityService securityService) {
    this.securityService = securityService;
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
          .antMatchers("/").permitAll()
          .antMatchers("/user/login").anonymous()
          .antMatchers("/**").hasAnyRole("ADMIN", "USER")
          .and()
        .formLogin()
          .loginPage("/user/login")
          .defaultSuccessUrl("/")
          .and()
        .logout()
          .logoutUrl("/user/logout")
          .logoutSuccessUrl("/")
          .and()
        .exceptionHandling()
          .accessDeniedPage("/error/403");
  }

  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    auth.userDetailsService(securityService).passwordEncoder(passwordEncoder);
  }
}

index.html(使用thymeleaf-extras-springsecurity)

<!DOCTYPE html>
<html lang="ko"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"
      layout:decorator="layout/base">

<th:block layout:fragment="content">
    <h1>Main Page</h1>
    <p sec:authentication="principal.username">Username</p>
    <p sec:authentication="principal.phone">Phone</p>
</th:block>

问题

index.html中,sec:authentication="principal.username"按预期工作,但sec:authentication="principal.phone"尽管我的UserDetailsService实施存储UserEntry实现了UserDetails,但却没有字段phone

问题

  • 有没有办法让sec:authentication="principal.phone"运作良好? (或分别为"princiapl.getPhone()"
  • 如果没有,我是否可以在我的百万富翁中获取当前用户的电话号码而不通过控制器?
  • 如果没有,我怎样才能通过当前用户的UserEntry对象而不显式插入模型,例如通过每个控制器方法的mav? AOP是否处理此问题?

(附加)在应用Spring安全性的许多其他示例中,他们不会在UserDetails(或类似的类)上实现UserEntry,而是创建一个新{ {1}}实施中的{1}}实例,如

UserDetails

from here)。我认为我的结构不是一个好的设计,但我不确切知道为什么。我的班级设计有什么评论吗?

谢谢!

如果我的问题太模糊,请告诉我,然后我会更新更具体。

1 个答案:

答案 0 :(得分:0)

为了使用Thymeleaf中用户数据中包含的其他字段,您必须执行以下步骤。

  1. 实现您自己的Spring Security的用户。
  2. 覆盖loadUserByUsername,以便它返回您的自定义用户。
  3. 添加Spring Security的Thymeleaf Extras依赖项。
  4. 使用${#authentication.getPrincipal()}代替sec

STEP 1

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.Collection;

// Our own implementation of the Spring Security User.

public class MyUser extends User {

    // Here we add the extra fields of our users.
    private String phone;
    private static final long serialVersionUID = 1L;

    public MyUser(String username,
                      String password,
                      Collection<GrantedAuthority> authorities,
                      String phone) {
        super(username, password, authorities);
        this.phone = phone;
    }

    public String getPhone() {
        return realName;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

}

第2步

@Override
public MyUser loadUserByUsername(String userName)
        throws AuthenticationException {

    // Fetch the user.
    UserDetails user = userService.loadUserByUsername(username);

    // For each user's authority, add it into our authorities' collection.
    Collection<GrantedAuthority> grantedAuthorities = new LinkedList<GrantedAuthority>(); 
    if (user.getAuthorities().size() > 0){
        for (Authority authority : user.getAuthorities()) {
            // Add a new GrantedAuthority for each user's authorities.
            grantedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
         }
    }

    return new MyUser(user.getUsername(), user.getPassword(), grantedAuthorities, user.getPhone());
}

第3步

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

第4步

<th:block th:with="auth=${#authentication.getPrincipal()}">
    <p th:text="${auth ? auth.phone : 'NULL'}">Phone</p>
</th:block>