Spring 安全 hasRole() 不工作

Spring Security hasRole() not working

我在使用 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>

Role.java

@Entity
@Table(name = "roles")

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

角色类型

public enum RoleType {

    ADMIN 
}

并且User有一组Role

为什么 hasRole() 不起作用?

感谢您的帮助,谢谢

解决方法

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

参考官方文档。 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>

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

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

希望对大家有所帮助。

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

  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 class 或 xml 中确保您正在为 thymeleaf SpringSecurity 添加方言: 我正在使用 java class 进行配置。

    @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

希望这对你有用! 您好!

尝试在 HTML-tag 中使用 hasAuthority 而不是 hasRole

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

我遇到了同样的问题,这是因为 spring-security 4.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.RELEASE 并将 thymeleaf verison 提升到 3.0

或者如果您正在使用 spring 启动应用程序,那么情况会变得更加棘手,那么剩下的唯一选择就是降级 spring-security 或升级 spring 启动版本为 1.4.x(仍处于测试阶段)

在您的具体情况下,进行以下 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>

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

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

你错过了一个概念:

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

在spring安全中,hasRole()hasAuthority()相同,但hasRole()功能映射与Authority没有ROLE_前缀。

您可以在此 post 中找到已接受的答案:Difference between Role and GrantedAuthority in Spring Security

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

根据https://mvnrepository.com/

升级到最新版本

Spring 引导程序 Thymeleaf Extras Spring 安全 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 日)它对我有用。

希望对大家有所帮助

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

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

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

我使用以下实体

用户实体


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

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

@组件 public class 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);
    }

}

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

我发现这个控制器在@RestController之后有@RequestMapping

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

我更改了顺序,hasRole 正在工作:

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