【问题标题】:How to check "hasRole" in Java Code with Spring Security?如何使用 Spring Security 检查 Java 代码中的“hasRole”?
【发布时间】:2011-03-02 13:28:48
【问题描述】:

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

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

如何在 Java 代码中实现?类似的东西:

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

【问题讨论】:

    标签: java spring-security user-roles


    【解决方案1】:

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

    类似:

    public String createForm(HttpSession session, HttpServletRequest request,  ModelMap   modelMap) {
    
    
        if (request.isUserInRole("ROLE_ADMIN")) {
            // code here
        }
    }
    

    【讨论】:

    • 我认为更容易测试
    • 但如果我没有要求?
    • ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest() 得到请求呢? :)
    • @Autowired HttpServletRequest 请求; ?
    • 这甚至不是 Spring API,纯 Servlet 规范!可惜不是选择的答案
    【解决方案2】:

    Spring Security 3.0 有这个 API

    SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)
    

    您必须在使用之前注入包装器。

    SecurityContextHolderAwareRequestWrapper

    【讨论】:

    • 正如您所说的那样,该方法看起来是静态的,但是您需要一个 SecurityContextHolderAwareRequestWrapper 实例。您可以改进它,解释如何获得它并进一步澄清答案本身。
    • 如何在 Controller 中检索包装器?
    • 如何获取 SecurityContextHolderAwareRequestWrapper 的实例?
    • 如果这是一个看起来不是的网络应用程序,您可以添加 SecurityContextHolderAwareRequestWrapper 作为参数。如果它是一个网络应用程序,您可以将 HttpServletRequest 声明为参数并调用 isUserInRole
    • 由于人们对如何利用这一点感到困惑。控制器中的代码如下所示。 @RequestMapping(value = "/path/to/controller") public String sampleMethod(SecurityContextHolderAwareRequestWrapper requestWrapper, Model model){ //method code requestWrapper. isUserInRole("role"); }
    【解决方案3】:

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

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

    【讨论】:

    • 一个更好的答案,但是 ROLE_ADMIN 应该用双引号引起来。
    • 这是非常危险的。请注意,切换到 GrantedAuthority 实现的另一个实现(例如 JAAS,通过添加另一个授权可能性)将使此代码出现故障。参见 SimpleGrantedAuthority 中的 equals() 实现
    【解决方案4】:

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

        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
    【解决方案5】:

    您可以如下实现 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;
      }
    

    【讨论】:

    • SecurityContextHolder.getContext().getAuthentication() 可以检索到null。也许你加了一些支票?
    【解决方案6】:

    我正在使用这个:

    @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);
    }
    

    【讨论】:

      【解决方案7】:

      您可以从AuthorityUtils 班级获得一些帮助。检查角色作为单线:

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

      警告:这不会检查角色层次结构(如果存在)。

      【讨论】:

      • 这是最简单的解决方案,因为我需要多次检查列表并使用一些 magic 简单的例程来获取它一次很棒!
      【解决方案8】:

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

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

      2. 角色名称等于rolePrefix + authority

      3. 默认角色前缀是ROLE_,但是,它是可配置的。见here

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

      不幸的是,在 Spring 中自定义角色前缀有点 hacky,在很多地方默认前缀 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);
          }
      }
      

      【讨论】:

        【解决方案9】:

        当您在服务层中时,无法使用 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;
        }
        

        【讨论】:

          【解决方案10】:

          下面这两个注解是相等的,“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");
          }
          

          【讨论】:

            【解决方案11】:

            奇怪的是,我认为这个问题没有标准的解决方案,因为 spring-security 访问控制是expression based,而不是基于 java 的。你可以检查源代码 DefaultMethodSecurityExpressionHandler 看看你是否可以重复使用他们在那里做的事情

            【讨论】:

            • 所以您的解决方案是使用 DefaultMethodSecurityExpressionHandler 作为 bean 并获取表达式解析器并在 EL 中检查它?
            • 这可能不起作用,因为处理程序对方法调用进行操作(您的上下文中没有)。您可能需要创建自己的 bean 来做类似的事情,但不使用方法调用上下文
            【解决方案12】:

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

            有很多关于如何检查角色的内容,但没有太多说明当你说 hasRole("blah") 时你实际检查的是什么

            HasRole 检查当前已验证主体的授予权限

            所以当你看到 hasRole("blah") 真的意味着 hasAuthority("blah")

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

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

            【讨论】:

            • 从 Spring Security 4.0 开始,此 hasRole("bla") 现在等于 hasAuthority("ROLE_bla")
            【解决方案13】:

            迟到总比不好,让我投入我的 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();
            }
            

            【讨论】:

              【解决方案14】:

              @gouki 的答案是最好的!

              这只是 spring 真正做到这一点的一个提示。

              有一个名为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;
                  }
              

              【讨论】:

                【解决方案15】:

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

                解决方案:

                @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。

                【讨论】:

                【解决方案16】:

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

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

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

                2. 使用 HttpServletRequest

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

                【讨论】:

                  【解决方案17】:

                  在您的用户模型上,只需添加一个 '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
                  

                  【讨论】:

                    【解决方案18】:

                    我的方法在 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;
                    }
                    

                    【讨论】:

                      【解决方案19】:

                      您可以在 Spring Security 中使用注解 @Secured@RolesAllowed@PreAuthorise / @PostAuthorise

                      记住:你需要添加这段代码

                      @Configuration
                      @EnableGlobalMethodSecurity(
                          securedEnabled = true, 
                          jsr250Enabled = true, 
                          prePostEnabled = true
                      )
                      public class MyConfig extends WebSecurityConfigurerAdapter{
                      }
                      

                      在你的配置类前面。您不需要使用所有 3 个参数 securedEnabledjsr250EnabledprePostEnabled。您只需要一个,具体取决于您要使用的注释。

                      然后将角色检查注解放在你的控制器类中。

                      @Secured("ROLE_admin")
                      @GetMapping("/hello")
                      public String hello(){
                          return "hello";
                      }
                      

                      @RolesAllowed("ROLE_admin")
                      @GetMapping("/hello")
                      public String hello(){
                          return "hello";
                      }
                      

                      @PreAuthorize("hasRole('ROLE_user')")
                      @GetMapping("/hello")
                      public String hello(){
                          return "hello";
                      }
                      

                      这里有教程https://www.baeldung.com/spring-security-method-security

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2021-04-07
                        • 2013-10-07
                        • 2022-01-01
                        • 2016-02-22
                        • 2015-08-27
                        • 2015-05-02
                        • 2017-06-16
                        相关资源
                        最近更新 更多