Spring Security:hasAuthority、hasRole 和 PreAuthorize 不起作用

Posted

技术标签:

【中文标题】Spring Security:hasAuthority、hasRole 和 PreAuthorize 不起作用【英文标题】:Spring Security: hasAuthority,hasRole and PreAuthorize does not work 【发布时间】:2020-06-24 02:50:45 【问题描述】:

我正在尝试使用标准 Spring Security 注释和方法测试我的 web api。 我查看了网站上的所有选项,没有任何帮助,这是代码。没有角色,一切正常。我已经为这个问题苦恼了好几天。感谢您的帮助,谢谢。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,
        securedEnabled = true,
        jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private UserDetailsServiceImp userDetailsService;

    @Autowired
    JwtFilter jwtFilter;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST,"/authenticate").permitAll()
                .antMatchers(HttpMethod.GET,"/userData").permitAll()
                .antMatchers(HttpMethod.GET,"/allUsers").hasAuthority("ROLE_ADMIN")
                .anyRequest().authenticated()
                .and().exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint())
                .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
                http.addFilterAfter(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
        return super.authenticationManagerBean();
    

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

控制器类

@RestController
public class AuthenticationController 

    @Autowired
    public AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsServiceImp userDetailsService;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserRepository userRepository;


    @RequestMapping(value = "/userData", method = RequestMethod.GET)
    public String hello()
        return "Hello new User";
    

    @RequestMapping(value = "/allUsers", method = RequestMethod.GET)
    public List<UserD> findAll()
        return userRepository.findAll();
    

    @RequestMapping(value = "/authenticate", method = RequestMethod.POST)
    public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest jwtRequest) throws Exception
        try 
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(jwtRequest.getName(), jwtRequest.getPassword()));
        catch(BadCredentialsException e)
            throw new Exception("Incorrect username and password", e);
            
        final UserD userD = (UserD)userDetailsService.loadUserByUsername(jwtRequest.getName());

        final String token = jwtUtil.generateToken(userD.getName(), userD.getRole());

        Map<Object, Object> model = new HashMap<>();
        model.put("username", jwtRequest.getName());
        model.put("token", token);

        return ResponseEntity.ok(model);
    

如果antMatchers(HttpMethod.GET,"/allUsers").permitAll() ,然后它会返回应有的用户

UserDetailsS​​erviceImp

@Service
public class UserDetailsServiceImp implements UserDetailsService 

    @Autowired
    UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
        return userRepository.findByName(username).get(0);
    

以及添加角色的用户数据

@Component
public class DataInitializer implements CommandLineRunner 

    @Autowired
    UserRepository userRepository;

    @Autowired
    PasswordEncoder passwordEncoder;

    @Override
    public void run(String... args) throws Exception 
        UserD user = new UserD();
        user.setName("user");
        user.setPassword(passwordEncoder.encode("password"));
        user.setRole("ROLE_USER");
        userRepository.save(user);

        UserD admin = new UserD();
        admin.setName("admin");
        admin.setPassword(passwordEncoder.encode("password"));
        admin.setRole("ROLE_ADMIN");
        userRepository.save(admin);
     

这是数据库返回的内容

【问题讨论】:

【参考方案1】:

我想先澄清一点,这可能有助于您查明问题:

您的 UserDetailsS​​erviceImp 是否从数据库或 LDAP 或其他存储库中检索用户名和角色? 从数据库或 LDAP 或其他存储库中检索到什么角色? 有没有前缀“ROLE_”?

如果从数据库中检索到的角色是“ADMIN”,则在调用hasAuthority()时不要自己添加ROLE_。

.antMatchers(HttpMethod.GET,"/allUsers").hasAuthority("ADMIN")

如果不是这样,请在日志中启用调试,以查看 /allUsers 请求到底发生了什么。


更新: 我怀疑的一件事是您对 UserDetailsS​​erviceImp 的实施。

您必须确保将角色设置为用户详细信息。 在您的实现中,您似乎直接从数据库中查询出来,不确定您是否将角色设置为 userDetails。

return userRepository.findByName(username).get(0);

这是应该发生的事情:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 

    //assume, there is only one user with the username
    UserD user = userRepository.findByUsername(username);
    if (user == null) 
        throw new UsernameNotFoundException("User not found with username: " + username);
    
    List<GrantedAuthority> roles = new ArrayList<>();
    //assume, there is only one role for the user
    roles.add(new SimpleGrantedAuthority(user.getRole()));
    return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                roles);
    

【讨论】:

我认为您的 UserDetailsS​​erviceImp 实现可能有问题,请确保在您的“loadUserByUsername”方法中将授予的权限设置为用户。 我还更新了答案以显示如何设置授予的权限。 我添加了截图

以上是关于Spring Security:hasAuthority、hasRole 和 PreAuthorize 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security 'Roles' 和 'Privileges' 和 Thymeleaf 'hasRole' 和 'hasAuthority'

Spring security 中的hasAuthority()和hasRole() 有啥区别?

Spring Security 保护路由 hasRole,hasAuthority 不适用于 CORS http 请求

五 spring security 其他权限检验及自定义校验方法

Spring Security 权限管理注解写法

Spring security jdbauthentication authority via groups