Spring Security角色匹配但方法仍未授权问题排查
代码实现
SecurityConfig
@Configuration @EnableMethodSecurity(securedEnabled = true, prePostEnabled = true) public class WebSecurityConfig { @Autowired UserDetailsServiceImpl userDetailsService; @Autowired private AuthEntryPointJwt unauthorizedHandler; @Bean public AuthTokenFilter authenticationJwtTokenFilter() { return new AuthTokenFilter(); } @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userDetailsService); authProvider.setPasswordEncoder(passwordEncoder()); return authProvider; } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } @Bean public PasswordEncoder passwordEncoder() { return new ShaPasswordEncoder(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable) .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth.antMatchers("/authorizedAccess/**").permitAll() .antMatchers("/api/v1/**").permitAll() .antMatchers("/access/**").permitAll() .anyRequest().authenticated() ); http.authenticationProvider(authenticationProvider()); http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } }
接口方法
@RestController @RequestMapping(value = "/authorizedAccess") public class AuthorizedAccessControl { @Autowired someService someService; private static final Logger LOG = LogManager.getLogger(AuthorizedAccessController.class); /** * * @return */ @Secured("hasAnyAuthority('ROLE_ADMIN')") @GetMapping(value = "/environments", produces = MediaType.APPLICATION_JSON_VALUE) public List<String> getAllEnvironments() { LOG.debug(">>>>Environments"); return someService.getEnvironments(); } }
AuthTokenFilter
public class AuthTokenFilter extends OncePerRequestFilter { @Autowired private JwtUtils jwtUtils; @Autowired private UserDetailsServiceImpl userDetailsService; private static final Logger LOG = LogManager.getLogger(AuthTokenFilter.class); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String jwt = parseJwt(request); if (jwt != null && jwtUtils.validateJwtToken(jwt)) { String username = jwtUtils.getUserNameFromJwtToken(jwt); UserDetails userDetails = userDetailsService.loadUserByUsername(username); LOG.info(userDetails.getAuthorities()); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); List<GrantedAuthority> authorities = new ArrayList<>(authentication.getAuthorities()); LOG.info(authorities); } } catch (Exception e) { LOG.error("Cannot set user authentication: {}", e); } filterChain.doFilter(request, response); } private String parseJwt(HttpServletRequest request) { String headerAuth = request.getHeader("Authorization"); if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) { return headerAuth.substring(7); } return null; } }
问题修复方案
1. 修正@Secured注解的用法
@Secured不支持SpEL表达式(hasAnyAuthority(...)是@PreAuthorize的专属语法),直接写角色名称即可:
// 错误写法 @Secured("hasAnyAuthority('ROLE_ADMIN')") // 正确写法1:用@Secured直接指定角色 @Secured("ROLE_ADMIN") // 正确写法2:用@PreAuthorize支持SpEL @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
2. 确保权限名称完全匹配
检查UserDetailsServiceImpl中返回的GrantedAuthority名称,和注解/配置里的名称完全一致:
- 如果你的权限是
ROLE_ADMIN,注解就写ROLE_ADMIN;如果是ADMIN就写ADMIN - 要是想去掉默认的
ROLE_前缀,可以在SecurityConfig里加个Bean:
@Bean public GrantedAuthorityDefaults grantedAuthorityDefaults() { return new GrantedAuthorityDefaults(""); // 移除默认的ROLE_前缀 }
3. 调整SecurityFilterChain的规则顺序
如果要给/authorizedAccess/environments加权限检查,必须把具体路径的规则放在通用路径规则前面,否则会优先匹配到/authorizedAccess/**的permitAll():
.authorizeHttpRequests(auth -> auth.antMatchers("/api/v1/**").permitAll() .antMatchers("/access/**").permitAll() .antMatchers("/authorizedAccess/environments").hasAnyAuthority("ROLE_ADMIN") .antMatchers("/authorizedAccess/**").permitAll() .anyRequest().authenticated() );
4. 确认权限构造正确
在UserDetailsServiceImpl里,要确保用SimpleGrantedAuthority构造权限:
// 示例:从数据库获取角色后构造权限 List<GrantedAuthority> authorities = userRoles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRoleName())) .collect(Collectors.toList()); return new User(user.getUsername(), user.getPassword(), authorities);
内容的提问来源于stack exchange,提问作者Jaswant Kondur
相关产品推荐
相关产品推荐

