【问题标题】:Spring Security hasRole() not workingSpring Security hasRole() 不起作用
【发布时间】:2015-08-27 13:55:43
【问题描述】:

我在使用 Spring Security && Thymeleaf 时遇到了一个问题,特别是在尝试使用 hasRole 表达式时。 'admin' 用户有一个角色 'ADMIN' 但hasRole('ADMIN') 解析为 false 无论如何我尝试一下

我的html:

1.<div sec:authentication="name"></div> <!-- works fine -->
2.<div sec:authentication="principal.authorities"></div> <!-- works fine -->

3.<div  sec:authorize="isAuthenticated()" >true</div> <!-- works fine -->
4.<span th:text="${#authorization.expression('isAuthenticated()')}"></span> <!-- works fine -->

5.<div th:text="${#vars.role_admin}"></div> <!--Works fine -->
6.<div  sec:authorize="${hasRole('ADMIN')}" > IS ADMIN </div> <!-- Doesnt work -->
7.<div  sec:authorize="${hasRole(#vars.role_admin)}" > IS ADMIN </div> <!-- Doesnt work -->
8.<div th:text="${#authorization.expression('hasRole(''ADMIN'')')} "></div> <!-- Doesnt work -->
9.<div th:text="${#authorization.expression('hasRole(#vars.role_admin)')}"></div> <!-- Doesnt work -->

结果:

1.admin
2.[ADMIN]
3.true
4.true
5.ADMIN
6."prints nothing because hasRole('ADMIN') resolves to false"
7."prints nothing because hasRole(#vars.role_admin) resolves to false"
8.false
9.false

我在我的 security.xml 文件中启用了 use-expressions

<security:http auto-config="true" use-expressions="true">

并且还在我的配置中包含了 SpringSecurityDialect

<bean id="templateEngine"
      class="org.thymeleaf.spring4.SpringTemplateEngine">
    <property name="templateResolver" ref="templateResolver" />  
    <property name="additionalDialects">
        <set>
            <bean class="org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect" />
        </set>
    </property>      
</bean>

我的 pom.xml 文件中所有必要的依赖项

<!--Spring security--> 
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core</artifactId>
        <version>4.0.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>4.0.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>4.0.1.RELEASE</version>
    </dependency>        
    
    <!--Thymeleaf Spring Security-->
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity4</artifactId>
        <version>2.1.2.RELEASE</version>
        <scope>compile</scope>
    </dependency>

角色.java

@Entity
@Table(name = "roles")

    public class Role implements Serializable {
    
        @Id
        @Enumerated(EnumType.STRING)
        private RoleType name;
        //... getters, setters
    }

角色类型

public enum RoleType {

    ADMIN 
}

User有一组Roles

为什么hasRole() 不起作用?

感谢您的帮助,谢谢

解决方法

th:if="${#strings.contains(#authentication.principal.authorities,'ADMIN')}"

【问题讨论】:

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


【解决方案1】:

参考官方文档。 http://www.thymeleaf.org/doc/articles/springsecurity.html

<div sec:authorize="hasRole('ROLE_ADMIN')">
  This content is only shown to administrators.
</div>
<div sec:authorize="hasRole('ROLE_USER')">
  This content is only shown to users.
</div>

你可以简单地尝试如下没有${ ... }

<div sec:authorize="hasRole('ADMIN')">IS ADMIN</div>

我相信你没有在角色前面加上ROLE_。如果是这样,请确保添加前缀,如下所示

<div sec:authorize="hasRole('ROLE_ADMIN')">IS ADMIN</div>

【讨论】:

  • 使用安全表达式时不需要前缀(在 Spring Security 3 中引入)...当访问条件基于 config 属性时,必须使用 ROLE_ 前缀命名角色AccessDecisionVoter 评估。允许区分角色和其他条件的前缀(例如IS_AUTHENTICATED_FULLY)。
  • 我在没有 ${ ... } 的情况下尝试过,但仍然无法正常工作。我对该问题的临时解决方案是使用th:if="${#strings.contains(#authentication.principal.authorities,'ADMIN')}"
【解决方案2】:

我必须在需要验证用户角色的地方做类似的事情。我在下面做了

<div th:if="${ #authorization.expression('isAuthenticated()') and #strings.contains(#authentication.principal.authorities,'ADMIN')}">          
    <a th:href="@{/somelink}">ADMIN LINK</a>
 </div>

希望它对某人有所帮助。

【讨论】:

    【解决方案3】:

    我最近遇到了同样的问题。 你需要做的是:

    1. 在您的 html 中添加以下语句:

      <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"   xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
      

    (您可以在 springsecurity4 或 springsecurity3 之间切换,具体取决于您使用的是什么)。

    1. 确保您已将此资源添加到您的库中。我正在使用 gradle,但你可以对 Maven 做同样的事情。

      compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity4:2.1.2.RELEASE'
      
    2. 在您的 SpringWebConfiguration 类或 xml 中,确保您正在为 thymeleaf SpringSecurity 添加方言: 我正在使用 java 类进行配置。

      @Bean
      public SpringTemplateEngine templateEngine() {
      SpringTemplateEngine templateEngine = new SpringTemplateEngine();
      templateEngine.setTemplateResolver(templateResolver());
      templateEngine.addDialect(new SpringSecurityDialect());
      return templateEngine;
      }
      

    但你也可以像 alexsource 所说的那样定义: Spring security and Thymeleaf doesn't work

    我希望这对你有用! 问候!

    【讨论】:

      【解决方案4】:

      尝试在 HTML 标记中使用 hasAuthority 而不是 hasRole

      sec:authorize="hasAuthority('ADMIN')"
      

      【讨论】:

      • 我遇到了同样的问题。从 Spring 3 迁移到 Spring 4 时,hasRole() 似乎不起作用。使用 hasAuthority() 对我有用。 @Xipo 你能确认它是否适合你并验证这个答案吗?
      • 我也可以确认这种行为。目前没有找到解释。
      • 这对我不起作用 - &lt;span sec:authentication="principal.authorities"&gt;No Authorities Captured&lt;/span&gt; 提供 [ROLE_USER, ROLE_ADMIN] 但 &lt;div sec:authorize="hasAuthority(' ROLE_ADMIN')"&gt; This content is only shown to Authority - Admin. &lt;/div&gt; 不起作用。
      【解决方案5】:

      我遇到了同样的问题,因为spring-security4.0。由于某种原因thymeleaf-extras-springsecurity4spring-security 4.0 和thymeleaf 2.x 不兼容。 所以我将spring-security 版本降级为3.2.9.RELEASE 并开始工作。 如果您仍然想使用 spring-security 4.0,那么您可以尝试将 thymeleaf-extras-springsecurity4 提升到 3.0.0.RELEASEthymeleaf verison 到 3.0

      或者如果您使用的是 spring boot 应用程序,那么情况会变得更加棘手,那么剩下的唯一选择就是降级 spring-security 或将 spring boot 版本升级到 1.4.x(仍处于 BETA 中)

      在您的特定场景中,进行以下 pom 更改应该使 hasRole 工作

      <!--Spring security--> 
          <dependency>
              <groupId>org.springframework.security</groupId>
              <artifactId>spring-security-core</artifactId>
              <version>3.2.9.RELEASE</version>
          </dependency>
          <dependency>
              <groupId>org.springframework.security</groupId>
              <artifactId>spring-security-web</artifactId>
              <version>3.2.9.RELEASE</version>
          </dependency>
          <dependency>
              <groupId>org.springframework.security</groupId>
              <artifactId>spring-security-config</artifactId>
              <version>3.2.9.RELEASE</version>
          </dependency>        
      
          <!--Thymeleaf Spring Security-->
          <dependency>
              <groupId>org.thymeleaf.extras</groupId>
              <artifactId>thymeleaf-extras-springsecurity4</artifactId>
              <version>2.1.2.RELEASE</version>
              <scope>compile</scope>
          </dependency>
      

      【讨论】:

        【解决方案6】:

        我从 Spring Security 3.x 升级到 4.x 时遇到了同样的问题。将 hasRole() 更改为 hasAuthority() 对我有用。

        http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#el-common-built-in

        【讨论】:

          【解决方案7】:

          你错过了一个概念:

          • 如果您使用hasRole('ADMIN'),则在您的ADMIN Enum 中必须是ROLE_ADMIN 而不是ADMIN
          • 如果你使用hasAuthority('ADMIN'),你的ADMIN Enum必须是ADMIN

          在 Spring Security 中,hasRole()hasAuthority() 相同,但 hasRole() 函数映射带有 Authority 不带 ROLE_ 前缀。

          您可以在这篇文章中找到接受的答案:Difference between Role and GrantedAuthority in Spring Security

          【讨论】:

            【解决方案8】:

            经过数周的反复试验,这对我有用:

            根据https://mvnrepository.com/升级到最新版本

            Spring Boot 启动器 Thymeleaf Extras Spring Security 5 用于 Spring Boot 的 Thymeleaf

            <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-thymeleaf</artifactId>
                    <version>2.3.0.RELEASE</version>
                </dependency>
            

            真的不知道哪个版本的依赖项与其他版本配合得很好,但现在(2020 年 5 月 19 日)它对我有用。

            希望对大家有所帮助

            【讨论】:

              【解决方案9】:

              在 Spring Boot 2 中, 您可以使用 hasRole() 或 hasAuthority()。不同之处在于,您必须使用 ROLE_ 才能使用 hasAusthority() 方法。所以对于 ROLE_ADMIN,

               @PreAuthorize("hasRole('ADMIN')") == @PreAuthorize("hasAuthority('ROLE_ADMIN')")
              

              【讨论】:

                【解决方案10】:

                我遇到了类似的问题,我已经解决了。

                我使用以下实体

                用户实体

                
                    @Setter
                    @Getter
                    @AllArgsConstructor
                    @NoArgsConstructor
                    @Builder
                    @Entity
                    public class User implements UserDetails, CredentialsContainer {
                    
                        @Id
                        @GeneratedValue(strategy = GenerationType.IDENTITY)
                        private Long id;
                    
                        @Column(nullable = false,unique = true)
                        private String username;
                    
                        @Column(nullable = false,unique = true)
                        private String email;
                    
                        private String password;
                    
                        @Builder.Default
                        private Boolean accountNonExpired = true;
                    
                        @Builder.Default
                        private Boolean accountNonLocked = true;
                    
                        @Builder.Default
                        private Boolean credentialsNonExpired = true;
                    
                        @Builder.Default
                        private Boolean enabled = true;
                    
                        @CreationTimestamp
                        @Column(updatable = false)
                        private Timestamp createdDate;
                    
                        @UpdateTimestamp
                        private Timestamp lastModifiedDate;
                    
                        @Singular
                        @ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
                        @JoinTable(
                                name = "user_role",
                                joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
                                inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")
                        )
                        private Set<Role> roles = new HashSet<>();
                    
                        @Override
                        public void eraseCredentials() {
                            this.password = null;
                        }
                    
                        @Override
                        @Transient
                        public Collection<? extends GrantedAuthority> getAuthorities() {
                            Set<SimpleGrantedAuthority> authorities =
                                    this.roles.stream().
                                    map(Role::getAuthorities).
                                    flatMap(Set::stream).
                                    map(authority -> new SimpleGrantedAuthority(authority.getPermission())).
                                    collect(Collectors.toSet());
                    
                            roles.stream().map(Role::getName).map(SimpleGrantedAuthority::new).forEach(authorities::add);//WE NEED IT FOR hasRole() functionality
                            return authorities;
                        }
                    
                        @Override
                        public boolean isAccountNonExpired() {
                            return accountNonExpired;
                        }
                    
                        @Override
                        public boolean isAccountNonLocked() {
                            return accountNonLocked;
                        }
                    
                        @Override
                        public boolean isCredentialsNonExpired() {
                            return credentialsNonExpired;
                        }
                    
                        @Override
                        public boolean isEnabled() {
                            return enabled;
                        }
                    }
                
                

                角色实体

                    @Setter
                    @Getter
                    @AllArgsConstructor
                    @NoArgsConstructor
                    @Builder
                    @Entity
                    public class Role  {
                        @Id
                        @GeneratedValue(strategy = GenerationType.IDENTITY)
                        private Long id;
                    
                        private String name;
                    
                        @ManyToMany(mappedBy = "roles")
                        private Set<User> users;
                    
                        @Singular
                        @ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
                        @JoinTable(
                                name = "role_authority",
                                joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"),
                                inverseJoinColumns = @JoinColumn(name = "authority_id", referencedColumnName = "id")
                        )
                        private Set<Authority> authorities = new HashSet<>();
                    
                    
                    }
                
                

                权威实体

                
                    @Setter
                    @Getter
                    @NoArgsConstructor
                    @AllArgsConstructor
                    @Builder
                    @Entity
                    public class Authority  {
                    
                        @Id
                        @GeneratedValue(strategy = GenerationType.IDENTITY)
                        Long id;
                    
                        private String permission;
                    
                        @Singular
                        @ManyToMany(mappedBy = "authorities")
                        private Set<Role> roles = new HashSet<>();
                    
                    
                    }
                
                

                引导

                        var storeItemCreate = authorityRepository.save(Authority.builder().permission("store.item.create").build());
                        var storeItemRead = authorityRepository.save(Authority.builder().permission("store.item.read").build());
                        var storeItemUpdate = authorityRepository.save(Authority.builder().permission("store.item.update").build());
                        var storeItemDelete = authorityRepository.save(Authority.builder().permission("store.item.delete").build());
                
                
                
                        var admin = roleRepository.save(Role.builder().
                                authority(storeItemCreate).
                                authority(storeItemRead).
                                authority(storeItemUpdate).
                                authority(storeItemDelete).
                                name("ROLE_ADMIN").build());
                
                        var customer = roleRepository.save(Role.builder().
                            authority(storeItemRead).
                            name("ROLE_CUSTOMER").
                            build());
                
                        userRepository.save(User.builder().
                                role(admin).
                                username("admin").
                                password(passwordEncoder.encode("admin")).
                                email("admin@admin.com").
                                build()
                        );
                
                
                        userRepository.save(User.builder().
                                role(customer).
                                username("user").
                                password(passwordEncoder.encode("user")).
                                email("user@user.com").
                                build()
                        );
                

                为什么我工作 hasAuthority() 和 hasRole() 是 getAuthorities 方法中用户实体的片段

                        Set<SimpleGrantedAuthority> authorities =
                                this.roles.stream().
                                map(Role::getAuthorities).
                                flatMap(Set::stream).
                                map(authority -> new SimpleGrantedAuthority(authority.getPermission())).
                                collect(Collectors.toSet());
                
                        roles.stream().map(Role::getName).map(SimpleGrantedAuthority::new).forEach(authorities::add);//WE NEED IT FOR hasRole() functionality
                        return authorities;
                

                当你拥有名为 ROLE_NAMEOFROLE 的权限时,spring 将其视为角色 当前缀不存在时,spring 将其视为权威。

                记住也要拥有权限:“ROLE_ADMIN”

                我不确定这是不是正确的方法!!!

                【讨论】:

                  【解决方案11】:

                  @组件 公共类 LoginSuccessHandler 扩展 SavedRequestAwareAuthenticationSuccessHandler {

                      @Override
                      public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                                          Authentication authentication) throws ServletException, IOException {
                          CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
                  
                          String redirectURL = request.getContextPath();
                          if (userDetails.hasRole("ROLE_ADMIN")) {
                              redirectURL = "admin-dashboard";
                          } else if (userDetails.hasRole("ROLE_EMPLOYEE")) {
                              redirectURL = "dashboard";
                          } else if (userDetails.hasRole("ROLE_TRAINEE")) {
                              redirectURL = "dashboard";
                          }
                          response.sendRedirect(redirectURL);
                      }
                  
                  }
                  

                  【讨论】:

                    【解决方案12】:

                    在我的例子中,hasRole 不适用于特定控制器的端点,但它适用于其他控制器端点。

                    我意识到这个控制器在@RestController 之后有@RequestMapping

                    @RestController
                    @RequestMapping("/test/v1")
                    public class TestController {
                    }
                    

                    我更改了订单,hasRole 现在正在工作:

                    @RequestMapping("/test/v1")
                    @RestController
                    public class TestController {
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 2017-06-16
                      • 2015-12-09
                      • 2020-07-25
                      • 1970-01-01
                      • 2011-07-10
                      • 2018-05-30
                      • 2014-12-12
                      相关资源
                      最近更新 更多