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

Spring Boot 2.7.9中authorizeHttpRequests下自定义AuthenticationProvider未触发

问题1:改用authorizeHttpRequests()后自定义AuthenticationProvider不被调用的原因

Spring Security从authorizeRequests()切换到authorizeHttpRequests()时,授权流程的触发逻辑发生了变化。你的实现中,自定义Filter将未认证的UsernamePasswordAuthenticationToken放入SecurityContext,在旧版authorizeRequests()的逻辑下,授权检查阶段会发现该Authentication未完成认证,从而主动调用AuthenticationManager触发AuthenticationProvider的认证流程。但在新版authorizeHttpRequests()对应的Servlet 3.1+配置模型中,默认不会对SecurityContext中已存在的Authentication(哪怕未认证)触发二次认证,导致你的JwtAuthenticationProvider不会被调用。

另外,你的实现逻辑本身存在不规范的地方:Filter不应该直接将未认证的Authentication放入SecurityContext,正确的做法是通过AuthenticationManager完成认证后再设置已认证的对象。

修复方案:

  1. 修改JwtAuthenticationFilter,让AuthenticationManager处理认证
    去掉直接设置SecurityContext的代码,改为调用AuthenticationManager.authenticate()方法,将返回的已认证对象放入SecurityContext:

    public class JwtAuthenticationFilter extends OncePerRequestFilter {
        private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
        
        @Autowired
        private JwtProcessor jwtProcessor;
        
        @Autowired
        private AuthenticationManager authenticationManager; // 注入AuthenticationManager
        
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            logger.info("filter is working on..");
            String authHeader = request.getHeader("Authorization");
            
            if(authHeader != null) {
                logger.info("found an authorization header in my filter..");    
                String jwt = authHeader.substring("bearer: ".length());
                Map<String, Object> claims = jwtProcessor.validateJwt(jwt);
                
                UsernamePasswordAuthenticationToken unauthenticatedToken = createToken(jwt, claims);
                // 调用AuthenticationManager触发认证
                Authentication authenticatedToken = authenticationManager.authenticate(unauthenticatedToken);
                // 设置已认证的对象到SecurityContext
                SecurityContextHolder.getContext().setAuthentication(authenticatedToken);
            }
            
            filterChain.doFilter(request, response);
        }
    
        private UsernamePasswordAuthenticationToken createToken(String jwt, Map<String, Object> claims) {
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(jwt, claims);
            token.setAuthenticated(false);
            return token;
        }
    }
    
  2. 在SecurityConfig中暴露AuthenticationManager Bean
    Spring Security 5.7+使用组件式配置,需要显式暴露AuthenticationManager:

    @EnableWebSecurity
    @Configuration
    public class SecurityConfig {
        
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            http
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/user/signup", "/user/login").permitAll()
                        .anyRequest().authenticated())
                .authenticationProvider(jwtAuthenticationProvider())
                .csrf().disable()
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
            
            return http.build();
        }
    
        // 暴露AuthenticationManager
        @Bean
        public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
            return authConfig.getAuthenticationManager();
        }
        
        @Bean
        public OncePerRequestFilter jwtAuthenticationFilter() {
            return new JwtAuthenticationFilter();
        }
        
        @Bean
        public AuthenticationProvider jwtAuthenticationProvider() {
            return new JwtAuthenticationProvider();
        }
    }
    

这样修改后,无论使用authorizeRequests()还是authorizeHttpRequests(),AuthenticationManager都会主动调用你的JwtAuthenticationProvider完成认证流程。


问题2:官方JWT示例中如何处理JWT的scope变量

在你提供的Spring Security官方JWT配置中,使用了oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt),可以通过自定义JwtAuthenticationConverter来提取并处理JWT中的scope变量,实现额外逻辑。

实现方式:

  1. 自定义JwtAuthenticationConverter,处理scope

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        
        // 自定义权限转换逻辑,同时处理scope
        converter.setJwtGrantedAuthoritiesConverter(jwt -> {
            // 提取scope
            List<String> scopes = jwt.getClaimAsStringList("scope");
            if (scopes == null) {
                scopes = Collections.emptyList();
            }
            
            // 这里可以添加对scope的额外处理,比如校验特定scope是否存在、记录日志等
            scopes.forEach(scope -> {
                // 示例:如果包含admin scope,做额外处理
                if ("admin".equals(scope)) {
                    // 你的业务逻辑,比如记录权限日志、触发特定操作等
                }
            });
            
            // 将scope转换为GrantedAuthority(默认前缀是SCOPE_)
            return scopes.stream()
                    .map(scope -> new SimpleGrantedAuthority("SCOPE_" + scope))
                    .collect(Collectors.toList());
        });
        
        return converter;
    }
    
  2. 在SecurityConfig中配置该Converter
    修改oauth2ResourceServer的配置,指定自定义的JwtAuthenticationConverter:

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((authorize) -> 
                        authorize
                            .requestMatchers("/sign-up", "/login").permitAll()
                            .anyRequest().authenticated()
                )
                .cors().configurationSource(corsConfiguration()).and()
                .csrf().disable()
                .httpBasic(Customizer.withDefaults())
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())) // 配置自定义Converter
                )
                .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling((exceptions) -> exceptions
                        .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                        .accessDeniedHandler(new BearerTokenAccessDeniedHandler())
                )
                .logout()
                    .logoutUrl("/logout")
                    .clearAuthentication(true)
                    .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler());
    
        return http.build();
    }
    

通过这种方式,你可以在JWT转换为Authentication的过程中,直接提取scope变量进行任意业务处理,同时不影响默认的权限转换逻辑。


内容的提问来源于stack exchange,提问作者aaabbbccc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 21:35:09