如何正确使用 Spring Security 中的 hasRole?

Posted

技术标签:

【中文标题】如何正确使用 Spring Security 中的 hasRole?【英文标题】:How to use hasRole in Spring Security properly? 【发布时间】:2021-04-07 20:19:16 【问题描述】:

嗨,我试图实现的是保护一个只有一个角色可以访问它的 url,当我尝试添加 .hasRole("USER") 时,其他角色仍然可以访问它。这是我的做法:

这是我的控制器:

@RestController
@RequestMapping("/couponapi")
public class CouponController 

    @Autowired
    CouponRepository couponRepository;

    @PostMapping("/coupons")
    public Coupon save(@RequestBody Coupon coupon) 
        return couponRepository.save(coupon);
    

    @GetMapping("/coupons/code")
    public Coupon findByCode(@PathVariable("code") String code) 
        return couponRepository.findByCode(code);
    

    @GetMapping("/something")
    public Coupon findByCodeX() 
        return couponRepository.findByCode("SUPERSALE");
    


我只想为 ROLE_ADMIN 保护@GetMapping("/something"),这是我的 Spring 安全配置的样子:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    UserDetailServiceImpl userDetailService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.userDetailsService(userDetailService);
    

@Override
protected void configure(HttpSecurity http) throws Exception 
    http.httpBasic();
    http.authorizeRequests()
            .antMatchers(HttpMethod.GET,"/couponapi/coupons/**").hasRole("USER")
            .antMatchers(HttpMethod.POST,"/couponapi/coupons/**").hasRole("USER")
            .antMatchers("/couponapi/something").hasRole("ADMIN")
            .antMatchers("/**").authenticated()
            .and().httpBasic().and().csrf().disable();


    @Bean
    public PasswordEncoder passwordEncoder() 
        return new BCryptPasswordEncoder();
    

这是我的角色类:

@Data
@EqualsAndHashCode(of = "id")
@ToString(of =  "id" )
@Entity
public class Roles implements GrantedAuthority 

    private static final long serialVersionUID = -7314956574144971210L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToMany(mappedBy = "roles")
    private Set<Users> users;

    @Override
    public String getAuthority() 
        return null;
    

这是我实现 UserDetailsS​​ervice 类的服务:

@Service
public class UserDetailServiceImpl implements UserDetailsService 

    @Autowired
    UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException 
        Users users = userRepository.findByEmail(s);


        if(users == null) 
            throw new UsernameNotFoundException("Username Not Found");
        

        return new User(users.getEmail(), users.getPassword(), users.getRoles());
    

这是我的数据库角色数据:

如你所见,我有 ROLE_USER 和 ROLE_ADMIN

这是我加入的数据库

** 我刚刚更新了我的问题,我已经回答了一半的问题,请阅读下面的答案以查看最新问题

【问题讨论】:

你可以尝试将hasRole 更改为hasAuthority,没有任何意义,但如果你可以尝试 当然,我试试这个 .hasAuthority("ROLE_USER") 还是一样的结果,不工作 好的,请在CouponController 中添加一个参数Authentication 并检查其中的所有字段是什么 你的意思是这样吗? @GetMapping("/something") public Coupon findByCodeX(Authentication auth) return couponRepository.findByCode("SUPERSALE"); 我如何检查其中的所有字段人口? 请这样 【参考方案1】:

感谢您对问题的良好介绍。

这就是我所理解的,您只想向具有 Admin 角色的用户授予访问此 URL /couponapi/something 的权限,并为所有经过身份验证的用户授予访问此 URL /couponapi/coupons/** 的访问权限,无论其角色如何(管理员或用户)

尝试使用 hasAuthority 而不是 hasRole 并删除第一行 http.httpBasic() ,它对我有用。

所以在 WebSecurityConfig.java 文件中:

 @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.authorizeRequests()
                .antMatchers("/couponapi/something").hasAuthority("ROLE_ADMIN")
                .antMatchers("/**").authenticated()
                .and().httpBasic().and().csrf().disable();
    

那么在上面的代码中,你只给具有ADMIN权限的用户访问这个url/couponapi/something,所以具有USER权限的用户不能访问它。

由于您已经声明了密码编码器 bean,您可以将其与 AuthenticationManagerBuilder 一起使用

 @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    

【讨论】:

【参考方案2】:

在 Spring Security 中,最严格的规则首先定义,因此您的配置应该如下所示

@Override
protected void configure(HttpSecurity http) throws Exception 
    http.httpBasic();
    http.authorizeRequests()
            .antMatchers(HttpMethod.GET,"/**/something").hasRole("USER")
            .antMatchers("/**").authenticated()               
            .and().httpBasic().and().csrf().disable();

【讨论】:

嗨,是的,我想出了一个,但我仍然有关于 HttpMethod.GET 等的问题,我仍然无法让它工作【参考方案3】:

我在这里找到了罪魁祸首,但不完全是我仍然缺少 HttpMethod 一的功能。这是我修复角色的方法,在我的 Role 类中,我犯了一个错误,我试图实现 GrantedAuthority 类,导致这个麻烦的事情(没有 HttpMethod 问题)。这就是我修复它的方法

首先,删除 impements 并将角色转换为通常的@Entity 类:

@Data
@EqualsAndHashCode(of = "id")
@ToString(of =  "id" )
@Entity
public class Roles 

    private static final long serialVersionUID = -7314956574144971210L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToMany(mappedBy = "roles")
    private Set<Users> users;


然后在实现 UserDetailsS​​ervice 的类中,添加以下代码:

List<GrantedAuthority> grantedAuthorities = new ArrayList<>();

users.getRoles().forEach(d -> 
    grantedAuthorities.add(new SimpleGrantedAuthority(d.getName()));
);

我无法详细解释,但我认为 List 只需要 1 个字符串,即字符串是角色名称。那么这里是完整的代码:

@Service
public class UserDetailServiceImpl implements UserDetailsService 

    @Autowired
    UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException 
        Users users = userRepository.findByEmail(s);
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();

        users.getRoles().forEach(d -> 
            grantedAuthorities.add(new SimpleGrantedAuthority(d.getName()));
        );

        if(users == null) 
            throw new UsernameNotFoundException("Username Not Found");
        

        return new User(users.getEmail(), users.getPassword(), grantedAuthorities);
    

如果有人可以找到此行.antMatchers(HttpMethod.GET,"/**/something").hasRole("USER") 的修复方法,我想使用 HttpMethod 来区分具有相同 url 的每个方法,如果有人有答案,我会接受你的答案。等着呢

【讨论】:

以上是关于如何正确使用 Spring Security 中的 hasRole?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用spring-security,oauth2调整实体的正确登录?

使用 Spring Security 成功登录后如何正确更新登录日期时间?

如何正确使用配置器类实现 Spring Security Ldap 身份验证?

如何正确设置 Spring Security 以使其使用提供的 SSLSocketFactory

如何使用 Spring Security / Spring MVC 处理表单登录

如何正确编写“重定向”(Spring MVC + Security)