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


118

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

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

如何用Java代码制作?就像是 :

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

Answers:


70

Spring Security 3.0具有此API

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

在使用包装纸之前,必须先注入包装纸。

SecurityContextHolderAwareRequestWrapper


53
如您的回答所述,该方法看起来是静态的,但是您需要一个SecurityContextHolderAwareRequestWrapper实例。您可以改进它,解释如何获得它,并进一步澄清答案本身。
Xtreme Biker

3
如何在Controller中检索包装器?
阿方索·蒂恩达

3
如何获取SecurityContextHolderAwareRequestWrapper的实例?
gstackoverflow

2
Xtreme Biker是正确的,如何获得SecurityContextHolderAwareRequestWrapper类?它不是静态对象。
试试它

5
如果这是一个Web应用程序(看起来好像不是),则可以只添加SecurityContextHolderAwareRequestWrapper作为参数。如果它是一个Web应用程序,则可以将HttpServletRequest声明为参数,然后调用isUserInRole
David Bradley

144

您可以使用HttpServletRequest对象的isUserInRole方法。

就像是:

public String createForm(HttpSession session, HttpServletRequest request,  ModelMap   modelMap) {


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

我认为测试起来更容易
fego 2014年

1
但是如果我没有要求?
gstackoverflow

怎样((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()获得请求?:)
Petrrjezdský16年

4
@Autowired HttpServletRequest请求;?
帕斯卡

而且这甚至不是Spring API的普通Servlet规范!遗憾的不是选择的答案
gregfqt

67

您可以执行以下操作,而不是使用循环从UserDetails中查找授权:

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

2
更好的答案,但是ROLE_ADMIN应该用双引号引起来。
埃里卡·凯恩

6
这是非常冒险的。请注意,切换到GrantedAuthority实现的另一个实现(例如,通过添加另一个授权可能性的JAAS)将使该代码出现故障。请参见SimpleGrantedAuthority中的equals()实现
PetrÚjezdský16年

47

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

    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。也许您添加一些支票?
2013年

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")) {
    /* ... */
}

警告:如果存在角色层次结构,则不会检查角色层次结构。


这是最简单的解决方案,因为我需要多次检查List并使用一些神奇的简单例程将其取回一次,所以很棒!
LeO

6

在服务层中,您不想从引用到HTTP请求引入与Web层的耦合时,将无法使用JoseK的答案。如果您想在服务层中解决角色,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;
}

6

大多数答案缺少一些要点:

  1. 角色和权限在Spring中不是一回事。有关更多详细信息,请参见此处。

  2. 角色名称等于rolePrefix+ authority

  3. 默认角色前缀是ROLE_,但是它是可配置的。看这里

因此,正确的角色检查需要尊重角色前缀(如果已配置)。

不幸的是,Spring中的角色前缀定制有点麻烦,在很多地方默认前缀ROLE_都是硬编码的,但是除此之外,还会GrantedAuthorityDefaults在Spring上下文中检查类型为Bean的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);
    }
}

3

奇怪的是,我不认为该问题有标准的解决方案,因为spring-security访问控制是基于表达式的,而不是基于Java的。您可能会检查DefaultMethodSecurityExpressionHandler的源代码 以查看是否可以重用他们在此处所做的事情


因此,您的解决方案是将DefaultMethodSecurityExpressionHandler用作bean并获取表达式解析器并在EL中对其进行检查?
Piotr Gwiazda

这可能不起作用,因为处理程序对方法调用进行操作(您在上下文中没有此方法)。您可能需要创建执行类似操作但不使用methodinvocation上下文的自己的bean
Sean Patrick Floyd 2010年

2

最好不要迟到,再也不要,让我投入2美分的价值。

在JSF世界中,在我的托管bean中,我执行了以下操作:


HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
SecurityContextHolderAwareRequestWrapper sc = new SecurityContextHolderAwareRequestWrapper(req, "");

如上所述,我的理解是可以按照如下漫长的方式完成此操作:


Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDetails userDetails = null;
if (principal instanceof UserDetails) {
    userDetails = (UserDetails) principal;
    Collection  authorities = userDetails.getAuthorities();
}

2

这是从另一端来的一个问题,但是我想把它扔进去,因为我真的必须在互联网上挖掘出来才能找到答案。

关于如何检查角色,有很多东西,但是当您说hasRole(“ blah”)时,并没有太多说出您实际检查的内容

HasRole检查授予的权限以获取当前经过身份验证的主体

因此,当您看到hasRole(“ blah”)时,实际上意味着hasAuthority(“ blah”)

在我所看到的情况下,您可以使用实现UserDetails的类进行此操作,该类定义了一个名为getAuthorities的方法。在这里,您将基本上添加一些new SimpleGrantedAuthority("some name")根据一些逻辑到列表中。此列表中的名称是hasRole语句检查的内容。

我想在这种情况下,UserDetails对象是当前经过身份验证的主体。身份验证提供程序内部和周围发生了一些魔术,更具体地说,是发生这种情况的身份验证管理器。


2
从Spring Security 4.0开始hasRole("bla"),现在等于hasAuthority("ROLE_bla")
lanoxx

2

@gouki的答案是最好的!

只是春天真正如何做到这一点的提示。

有一个名为class的类SecurityContextHolderAwareRequestWrapper实现ServletRequestWrapper该类。

SecurityContextHolderAwareRequestWrapper覆盖的isUserInRole和搜索用户Authentication(这是由Spring管理)找到,如果用户有一定的作用或没有。

SecurityContextHolderAwareRequestWrapper 代码为:

    @Override
    public boolean isUserInRole(String role) {
        return isGranted(role);
    }

 private boolean isGranted(String role) {
        Authentication auth = getAuthentication();

        if( rolePrefix != null ) {
            role = rolePrefix + role;
        }

        if ((auth == null) || (auth.getPrincipal() == null)) {
            return false;
        }

        Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();

        if (authorities == null) {
            return false;
        }

        //This is the loop which do actual search
        for (GrantedAuthority grantedAuthority : authorities) {
            if (role.equals(grantedAuthority.getAuthority())) {
                return true;
            }
        }

        return false;
    }

2

下面的这两个注释相等,“ hasRole”将自动添加前缀“ ROLE_”。确保您具有正确的注释。此角色在UserDetailsS​​ervice#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");
}

1

在我们的项目中,我们使用的是角色层次结构,而以上大多数答案仅针对检查特定角色,即仅检查给定的角色,而不检查该角色以及上层层次结构。

一个解决方案:

@Component
public class SpringRoleEvaluator {

@Resource(name="roleHierarchy")
private RoleHierarchy roleHierarchy;

public boolean hasRole(String role) {
    UserDetails dt = AuthenticationUtils.getSessionUserDetails();

    for (GrantedAuthority auth: roleHierarchy.getReachableGrantedAuthorities(dt.getAuthorities())) {
        if (auth.toString().equals("ROLE_"+role)) {
            return true;
        }
    }
    return false;
}

RoleHierarchy在spring-security.xml中定义为bean。


1
或者,您可以正确地填充您的角色: github.com/spring-projects/spring-security/issues/…–
arctica

1

在您的用户模型上,只需添加一个“ hasRole”方法,如下所示

public boolean hasRole(String auth) {
    for (Role role : roles) {
        if (role.getName().equals(auth)) { return true; }
    }
    return false;
}

我通常使用它来检查经过身份验证的用户是否具有admin角色,如下所示

Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // This gets the authentication
User authUser = (User) authentication.getPrincipal(); // This gets the logged in user
authUser.hasRole("ROLE_ADMIN") // This returns true or false

1

可以使用以下方式检查用户角色:

  1. 在SecurityContextHolder中使用静态调用方法:

    Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.getAuthorities().stream().anyMatch(role -> role.getAuthority().equals("ROLE_NAME"))) { //do something}

  2. 使用HttpServletRequest

@GetMapping("/users")
public String getUsers(HttpServletRequest request) {
    if (request.isUserInRole("ROLE_NAME")) {
      
    }


0

我的方法借助Java8传递逗号分隔的角色将为您带来真假

    public static Boolean hasAnyPermission(String permissions){
    Boolean result = false;
    if(permissions != null && !permissions.isEmpty()){
        String[] rolesArray = permissions.split(",");
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        for (String role : rolesArray) {
            boolean hasUserRole = authentication.getAuthorities().stream().anyMatch(r -> r.getAuthority().equals(role));
            if (hasUserRole) {
                result = true;
                break;
            }
        }
    }
    return result;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.