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

如何在Spring OAuth2校验JWT时检查账号是否关联用户档案

可行的实现方案

方案1:自定义JwtAuthenticationProvider(推荐)

把用户档案检查逻辑整合到认证流程中,在JWT认证通过后、生成Authentication对象前执行检查,这是符合Spring Security认证流程的标准做法。

实现步骤:

  1. 实现自定义的JwtAuthenticationProvider,继承默认实现并重写认证方法:
@Component
public class CustomJwtAuthenticationProvider extends JwtAuthenticationProvider {
    private final UserProfileRepository userProfileRepository;

    public CustomJwtAuthenticationProvider(JwtDecoder jwtDecoder, UserProfileRepository userProfileRepository) {
        super(jwtDecoder);
        this.userProfileRepository = userProfileRepository;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 先执行默认JWT认证逻辑
        Authentication authenticated = super.authenticate(authentication);
        Jwt jwt = (Jwt) authenticated.getPrincipal();
        String userId = jwt.getSubject();

        // 跳过注册接口的检查
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (!"/public/register".equals(request.getRequestURI())) {
            if (!userProfileRepository.existsByUserId(userId)) {
                throw new InsufficientAuthenticationException("用户档案未创建,请先完成注册");
            }
        }

        return authenticated;
    }
}
  1. 在SecurityConfig中替换默认的认证提供者:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final CustomJwtAuthenticationProvider customJwtAuthenticationProvider;

    public SecurityConfig(CustomJwtAuthenticationProvider customJwtAuthenticationProvider) {
        this.customJwtAuthenticationProvider = customJwtAuthenticationProvider;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorizeRequests ->
                authorizeRequests
                    .requestMatchers("/public/**").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2ResourceServer ->
                oauth2ResourceServer
                    .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
            )
            .authenticationProvider(customJwtAuthenticationProvider); // 注入自定义认证提供者
        return http.build();
    }    

    private JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(new CustomAuthoritiesConverter());
        return converter;
    }
}

方案2:自定义Filter插入Security链

在OAuth2认证Filter之后添加自定义Filter,对已认证的请求检查用户档案,自动跳过公开接口和注册接口。

实现步骤:

  1. 编写自定义Filter:
@Component
public class UserProfileCheckFilter extends OncePerRequestFilter {
    private final UserProfileRepository userProfileRepository;

    public UserProfileCheckFilter(UserProfileRepository userProfileRepository) {
        this.userProfileRepository = userProfileRepository;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 跳过所有公开接口
        if (request.getRequestURI().startsWith("/public/")) {
            filterChain.doFilter(request, response);
            return;
        }

        // 获取已认证的用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated() && !(authentication instanceof AnonymousAuthenticationToken)) {
            Jwt jwt = (Jwt) authentication.getPrincipal();
            String userId = jwt.getSubject();
            if (!userProfileRepository.existsByUserId(userId)) {
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                response.getWriter().write("用户档案未创建,请先完成注册");
                return;
            }
        }

        filterChain.doFilter(request, response);
    }

    // 指定Filter执行顺序,确保在OAuth2认证完成后执行
    @Override
    protected void initFilterBean() throws ServletException {
        super.initFilterBean();
        setOrder(OAuth2ResourceServerAuthenticationFilter.DEFAULT_ORDER + 1);
    }
}
  1. 在SecurityConfig中注册该Filter:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final UserProfileCheckFilter userProfileCheckFilter;

    public SecurityConfig(UserProfileCheckFilter userProfileCheckFilter) {
        this.userProfileCheckFilter = userProfileCheckFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .addFilterAfter(userProfileCheckFilter, OAuth2ResourceServerAuthenticationFilter.class)
            .authorizeHttpRequests(authorizeRequests ->
                authorizeRequests
                    .requestMatchers("/public/**").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2ResourceServer ->
                oauth2ResourceServer
                    .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
            );
        return http.build();
    }    

    private JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(new CustomAuthoritiesConverter());
        return converter;
    }
}

方案3:全局方法安全+自定义检查逻辑

通过方法层面的注解控制,开启全局方法安全后,在需要保护的接口上添加检查逻辑,注册接口因属于/public/**范围自动放行。

实现步骤:

  1. 开启全局方法安全:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}
  1. 编写检查用户档案的服务类:
@Service
public class UserProfileCheckService {
    private final UserProfileRepository userProfileRepository;

    public UserProfileCheckService(UserProfileRepository userProfileRepository) {
        this.userProfileRepository = userProfileRepository;
    }

    public boolean isProfileExists() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !(authentication.getPrincipal() instanceof Jwt)) {
            return false;
        }
        Jwt jwt = (Jwt) authentication.getPrincipal();
        return userProfileRepository.existsByUserId(jwt.getSubject());
    }
}
  1. 在需要保护的控制器方法上添加注解:
@RestController
@RequestMapping("/api")
public class ApiController {
    @PreAuthorize("@userProfileCheckService.isProfileExists()")
    @GetMapping("/resource")
    public String getResource() {
        return "protected resource";
    }
}

方案对比

  • 方案1最贴合Spring Security认证流程,检查逻辑整合到认证环节,异常抛出更统一,适合全局统一检查场景。
  • 方案2适合需要灵活控制检查路径的场景,可快速调整需要检查的接口范围。
  • 方案3适合细粒度的方法级控制,无需修改Security链配置,仅针对特定接口生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 15:03:23