如何在使用Spring Security的Java代码中检查“hasRole”?

133

如何在Java代码中检查用户权限或权限?例如,我想根据用户角色显示或隐藏按钮。有注释如:

@PreAuthorize("hasRole('ROLE_USER')")

如何在Java代码中实现?类似这样:

if(somethingHere.hasRole("ROLE_MANAGER")) {
   layout.addComponent(new Button("Edit users"));
}
21个回答

150
你可以使用HttpServletRequest对象的isUserInRole方法。 类似这样:
public String createForm(HttpSession session, HttpServletRequest request,  ModelMap   modelMap) {


    if (request.isUserInRole("ROLE_ADMIN")) {
        // code here
    }
}

我认为更容易测试 - fego
1
但是如果我没有请求呢? - gstackoverflow
使用((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()获取请求怎么样? :) - Petr Újezdský
4
@Autowired HttpServletRequest request; - Pascal
这甚至不是Spring API,只是普通的Servlet规范!可惜那不是被选中的答案。 - gregfqt

76

58
根据你的回答,似乎这个方法是静态的,但是你需要一个 SecurityContextHolderAwareRequestWrapper 实例。你可以改进一下,解释一下如何获取它,并对答案本身进行更清晰的澄清。 - Aritz
4
我该如何在控制器中获取包装器(wrapper)? - Alfonso Tienda
4
如何获取SecurityContextHolderAwareRequestWrapper的实例? - gstackoverflow
6
如果这是一个 Web 应用程序,看起来它不是,你可以将 SecurityContextHolderAwareRequestWrapper 添加为参数。如果它是一个 Web 应用程序,你可以声明 HttpServletRequest 作为参数并调用 isUserInRole 方法。 - David Bradley
5
由于人们不清楚如何利用这个功能,控制器中的代码如下:public String sampleMethod(SecurityContextHolderAwareRequestWrapper requestWrapper, Model model){ //方法代码 requestWrapper. isUserInRole("role"); } - Dot Batch
显示剩余3条评论

71

你可以不使用循环来从UserDetails中查找权限,而是可以这样做:

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean authorized = authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"));

2
一个更好的答案,但是 ROLE_ADMIN 应该用双引号括起来。 - Erica Kane
8
这非常危险。请注意,切换到另一种GrantedAuthority实现(例如JAAS,通过添加另一种授权可能性)将使此代码发生故障。请参见SimpleGrantedAuthority中的equals()实现。 - Petr Újezdský

53

您可以检索安全上下文,然后使用它:

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.context.SecurityContext;
    import org.springframework.security.core.context.SecurityContextHolder;

    protected boolean hasRole(String role) {
        // get security context from thread local
        SecurityContext context = SecurityContextHolder.getContext();
        if (context == null)
            return false;

        Authentication authentication = context.getAuthentication();
        if (authentication == null)
            return false;

        for (GrantedAuthority auth : authentication.getAuthorities()) {
            if (role.equals(auth.getAuthority()))
                return true;
        }

        return false;
    }

SecurityContextHolder.getContext() 永远不会是 NULL,请查看文档。因此,您可以避免检查上下文是否为 NULL - Imtiaz Shakil Siddique

14
你可以实现一个hasRole()方法,如下所示 -(这在Spring Security 3.0.x上进行了测试,对其他版本不确定。)
  protected final boolean hasRole(String role) {
    boolean hasRole = false;
    UserDetails userDetails = getUserDetails();
    if (userDetails != null) {
      Collection<GrantedAuthority> authorities = userDetails.getAuthorities();
      if (isRolePresent(authorities, role)) {
        hasRole = true;
      }
    } 
    return hasRole;
  }
  /**
   * Get info about currently logged in user
   * @return UserDetails if found in the context, null otherwise
   */
  protected UserDetails getUserDetails() {
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    UserDetails userDetails = null;
    if (principal instanceof UserDetails) {
      userDetails = (UserDetails) principal;
    }
    return userDetails;
  }
  /**
   * Check if a role is present in the authorities of current user
   * @param authorities all authorities assigned to current user
   * @param role required authority
   * @return true if role is present in list of authorities assigned to current user, false otherwise
   */
  private boolean isRolePresent(Collection<GrantedAuthority> authorities, String role) {
    boolean isRolePresent = false;
    for (GrantedAuthority grantedAuthority : authorities) {
      isRolePresent = grantedAuthority.getAuthority().equals(role);
      if (isRolePresent) break;
    }
    return isRolePresent;
  }

1
SecurityContextHolder.getContext().getAuthentication() 可能会检索到 null。你可以添加一些检查吗? - Mrusful

10

我正在使用这个:

@RequestMapping(method = RequestMethod.GET)
public void welcome(SecurityContextHolderAwareRequestWrapper request) {
    boolean b = request.isUserInRole("ROLE_ADMIN");
    System.out.println("ROLE_ADMIN=" + b);

    boolean c = request.isUserInRole("ROLE_USER");
    System.out.println("ROLE_USER=" + c);
}

8

您可以从AuthorityUtils类中获取一些帮助。 检查角色只需要一行代码:

if (AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities()).contains("ROLE_MANAGER")) {
    /* ... */
}

注意:此处未检查角色层次结构,如果存在的话。

这是最简单的解决方案,因为我需要多次检查列表,并且使用某些“神奇”的简单例程一次获取它非常好! - LeO

7
大部分答案都缺少一些要点:
  1. 在Spring中,角色和权限不是同一件事情。详见 这里 了解更多细节。

  2. 角色名称等于 rolePrefix + authority

  3. 默认的角色前缀是 ROLE_,然而,它是可以配置的。请参考这里

因此,如果已经配置了角色前缀,则合适的角色检查需要尊重该前缀。

不幸的是,Spring 中的角色前缀自定义有点复杂,在许多地方,默认前缀 ROLE_ 是硬编码的,但除此之外,Spring 上下文中会检查类型为 GrantedAuthorityDefaults 的 Bean,如果存在,它就会尊重它所具有的自定义角色前缀。

将所有这些信息汇集起来,更好的角色检查实现应该像这样:

@Component
public class RoleChecker {

    @Autowired(required = false)
    private GrantedAuthorityDefaults grantedAuthorityDefaults;

    public boolean hasRole(String role) {
        String rolePrefix = grantedAuthorityDefaults != null ? grantedAuthorityDefaults.getRolePrefix() : "ROLE_";
        return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
                .map(Authentication::getAuthorities)
                .map(Collection::stream)
                .orElse(Stream.empty())
                .map(GrantedAuthority::getAuthority)
                .map(authority -> rolePrefix + authority)
                .anyMatch(role::equals);
    }
}

7
下面两个注解是相同的,“hasRole”会自动添加前缀“ROLE_”。请确保您使用了正确的注解。该角色在UserDetailsService#loadUserByUsername中设置。
@PreAuthorize("hasAuthority('ROLE_user')")
@PreAuthorize("hasRole('user')")

然后,您可以在Java代码中获得该角色。

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_user"))){
    System.out.println("user role2");
}

6

JoseK的答案不能用于服务层,因为您不希望通过引用HTTP请求来引入与Web层的耦合。如果您想在服务层解决角色问题,那么Gopi的答案是正确的方法。

然而,这有点冗长。权限可以直接从身份验证中访问。因此,如果您可以假设已经有用户登录,那么可以使用以下代码:

/**
 * @return true if the user has one of the specified roles.
 */
protected boolean hasRole(String[] roles) {
    boolean result = false;
    for (GrantedAuthority authority : SecurityContextHolder.getContext().getAuthentication().getAuthorities()) {
        String userRole = authority.getAuthority();
        for (String role : roles) {
            if (role.equals(userRole)) {
                result = true;
                break;
            }
        }

        if (result) {
            break;
        }
    }

    return result;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接