【问题标题】:Spring Security/Spring Boot - How to set ROLES for usersSpring Security/Spring Boot - 如何为用户设置角色
【发布时间】:2016-10-03 13:24:41
【问题描述】:

当我使用安全登录时,我无法使用request.isUserInRole() 方法。我认为用户的角色没有设置。

这是我的安全配置:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled=true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter  {

@Autowired
private DataSource dataSource;

@Autowired
private UserDetailsServiceImplementation userDetailsService;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/signup").permitAll()
            .antMatchers("/").permitAll()
            //.antMatchers("/first").hasAuthority("Service_Center")
            .antMatchers("/login").permitAll()
            .anyRequest().fullyAuthenticated()
    .and().formLogin()
            .loginPage("/login")
            .usernameParameter("email")
            .passwordParameter("password")
            .defaultSuccessUrl("/default")
            .failureUrl("/login?error").permitAll()
    .and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/login?logout")
            .deleteCookies("JSESSIONID")
            .invalidateHttpSession(true).permitAll();
}

@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth)
        throws Exception {
    auth.userDetailsService(userDetailsService);

}

}

这是我的User 实体:

 @Entity
 @Table(name="user")
 public class User  implements Serializable{
/**
 * 
 */
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="user_id")
private Long userID;

@Column(name="email_address", nullable = false, unique = true)
private String emailAddress;

@Column(name="password")
private String password;

@Column(name = "role", nullable = false)
@Enumerated(EnumType.STRING)
private Role role;

public User() {
    super();
}

public User(String emailAddress, String password) {
    this.emailAddress = emailAddress;
    this.password = password;
}

public Long getUserID() {
    return userID;
}

public void setUserID(Long userID) {
    this.userID = userID;
}

public String getEmailAddress() {
    return emailAddress;
}

public void setEmailAddress(String emailAddress) {
    this.emailAddress = emailAddress;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

public Role getRole() {
    return role;
}

public void setRole(Role role) {
    this.role = role;
}

@Override
public String toString() {
    return "User [userID=" + userID + ", emailAddress=" + emailAddress
            + ", password=" + password + ", role=" + role + "]";
}

public UserDetails toCurrentUserDetails() {
    return CurrentUserDetails.create(this);
}
}

这是我的枚举 Role:

public enum Role {

Fleet_Company, Service_Center, Admin

}

这是我的UserDetailsServiceImplementation

@Component
public class UserDetailsServiceImplementation implements UserDetailsService    {

@Autowired
private UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException {
    if ( username == null || username.isEmpty() ){
        throw new UsernameNotFoundException("username is empty");
    }

    User foundUser = userRepository.findByEmailAddress(username);
    if( foundUser != null ){
        System.out.println("FOUND");
        return foundUser.toCurrentUserDetails();

    }
    throw new UsernameNotFoundException( username + "is not found");
}
}

这是实现UserDetails的类:

public class CurrentUserDetails implements UserDetails {
private Long userID;
private String emailAddress;
private String password;
private Role role;


public CurrentUserDetails(Long userID, String emailAddress, String password, Role role) {
    super();
    this.userID = userID;
    this.emailAddress = emailAddress;
    this.password = password;
    this.role = role;
}


  /*    public static UserDetails create(Users entity) {
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    for(Authorities auth: entity.getAuthorities()){
        authorities.add(new SimpleGrantedAuthority(auth.getId().getAuthority()));
    }
    return new MyUserDetail(entity.getUserId(), entity.getLoginId(), entity.getPassword(), entity.getDisplayName(), authorities);
}*/



public Long getUserID(){
    return this.userID;
}


public Role getRole(){
    return this.role;
}




@Override
public String getPassword() {
    return this.password;
}


public String getEmailAddress() {
    return this.emailAddress;
}


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

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


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


@Override
public boolean isEnabled() {
    return true;
}

public static UserDetails create(User entity) {
    System.out.println(entity.getUserID()+ entity.getEmailAddress()+ entity.getPassword()+ entity.getRole());
    return new CurrentUserDetails(entity.getUserID(), entity.getEmailAddress(), entity.getPassword(), entity.getRole());
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public String getUsername() {
    // TODO Auto-generated method stub
    return null;
}
}

所以基本上,我们可以看到我的 MySQL 数据库中只有一张表,它有四列,其中一列是“角色”。

但就像我说的那样,当我使用request.isUserInRole("Service_Center") 时,它返回 FALSE。而.antMatchers("/first").hasAuthority("Service_Center") 也不起作用。

【问题讨论】:

  • UserDetailsServiceImplementation 是什么?我相信这就是应该将您的实体链接到请求主体的地方。
  • 好的,所以跟踪问题会导致CurrentUserDetails.create(this)。那有什么作用?
  • @zapl 是实现 UserDetails 的类。我编辑了我的帖子。你现在可以看到了。这基本上就是我所有的 Spring Security。
  • 您需要通过getAuthorities() - stackoverflow.com/questions/19525380/… 返回角色 - spring 不会查看您自己的getRole 方法。
  • 是否通过getAuthorities()自动设置?

标签: java spring spring-mvc spring-security spring-boot


【解决方案1】:

创建 UserDetails 时需要自己填写角色的内容:

public class SecurityUser implements UserDetails{
    String ROLE_PREFIX = "ROLE_";

    String userName;
    String password;
    String role;

    public SecurityUser(String username, String password, String role){
        this.userName = username;
        this.password = password;
        this.role = role;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();

        list.add(new SimpleGrantedAuthority(ROLE_PREFIX + role));

        return list;
    }

基本上,您需要做的是覆盖方法:getAuthorities,并将您的角色字段的内容填写到GrantedAuthority列表中。

【讨论】:

  • 在 getAuthorities 方法上添加新角色?看起来这不是一个好主意...您应该使用 SecurityContextHolder 并在单独的服务中进行。
  • 角色应该包含ROLE_前缀吗?而如果角色是ROLE_ADMIN,hasRole应该指定'ADMIN'还是'ROLE_ADMIN'?
  • 为什么我们需要添加 ROLE_PREFIX 任何具体原因。我开发了一个应用程序,在该应用程序中,我将返回来自数据库的内容,只需用逗号将其拆分,然后将其放入具有 GrantedAuthority 列表的变量中。但在数据库中,我们必须指定 ROLE_PREFIX 。我想了解原因
  • Spring 将它用于一些内部目的。如您所见,在 User.UserBuilder 角色中,spring 创建了带有 ROLE 前缀的 SimpleGrantedAuthority,例如 authority.add(new SimpleGrantedAuthority("ROLE_" + role));只要您使用构建器,就不需要提供前缀。
【解决方案2】:

Divelnto、zapl 和 thorinkor 说的是对的。但问题应该是关于“角色”而不是“角色”。或者,如果您将用户和角色放在一个表中,那么这是一个糟糕的设计。您可能想重新审视您的设计方法。您应该有一个单独的角色实体。在您的 UserService 中,您可以执行以下操作:

AppUser user = userRepository.findByUsername(username);

Set<GrantedAuthority> grantedAuthorities = new HashSet<>(); // use list if you wish
for (AppRole role : user.getRoles()) {
    grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
}
return new org.springframework.security.core.userdetails.User(
        user.getUsername(),
        user.getPassword(),
        grantedAuthorities
);

样本:sample1sample2sample3

在 DB 中,您可以在数据库中将角色名称存储为 -(例如)ADMIN/EDITOR/VIEWER,​​或者将角色存储为 ROLE_ADMIN/ROLE_...,然后您可能想要使用 hasRole/hasAuthoriy。希望对您有所帮助。

供参考,请看这里:

Spring Security Related 1

Spring Security Related 2

【讨论】:

  • 您说在一个实体中拥有用户和角色是一种糟糕的设计,但您的示例代码似乎正是这样做的……我认为如果您的数据库允许将它们放在一起会更简单。
  • 别误会我的意思。!您是否在示例中的任何用户实体中看到“列”-“角色”?此外,如果一个用户应该只有一个角色,为什么你首先需要一个角色?它是一个工资的想法。您可以使用 if-else 条件将标志设置为真/假,而不是使用“角色”(而不是“角色”),这将解决 In-My-Humble-Opinion 的目的。谢谢。
【解决方案3】:

要添加角色,您需要有一个包含用户名及其对应角色的表。
假设一个用户有两个角色,即 ADMIN 和 USER

一个用户可以有多个角色。

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    final List<SimpleGrantedAuthority> authorities = new LinkedList<>();
    if (enabled) {
        if (this.getUser().isAdmin()) {
            authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        }
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
    }
        return authorities;
}

这可以称为,

private UsernamePasswordAuthenticationToken getAuthentication(
final String token, final HttpServletRequest req,
final HttpServletResponse res){
    return new UsernamePasswordAuthenticationToken(userAccount, null,
    userAccount.getAuthorities());
}

【讨论】:

    【解决方案4】:

    您也可以通过这种方式使用 User.builder 对象加载 ROLE 或 Authorities。此示例从 http 请求接收令牌,并获取 TOKEN 中的角色列表。您可以有一个简单的字符串列表,其中包含您需要的角色,并且刚刚加载到 UserDetail 实现中:

    private UserDetails userDetails(final Claims claims) {
        
        UserDetails userDetails = User.builder()
        .username(resolveUserName(claims))
        .password(resolveUserPassword())
        .roles(userRoles(claims))
        .build();
        
        
        return userDetails;
    }
    

    userRoles(claims) 只是返回一个字符串数组,其中包含您需要的所有角色。

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 2014-09-14
      • 2014-12-26
      • 2018-06-22
      • 2017-09-08
      • 2016-10-16
      • 2015-10-22
      • 2011-07-21
      • 2021-07-22
      • 2022-06-12
      相关资源
      最近更新 更多