You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 06:00:38