ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Spring Security 与 OAuth2 的关系:从过滤器链到 Token 校验的配置骨架

2026/9/28 18:11:03 拓冰建站 浏览量
Spring Security 与 OAuth2 的关系:从过滤器链到 Token 校验的配置骨架 1. 从一次 401 说起过滤器链到底卡在哪很多 Java 后端同学第一次把 Spring Security 和 OAuth2 拼在一起时都会遇到一个很迷惑的现象明明请求头里带了Authorization: Bearer xxx接口却还是返回 401日志里也看不出所以然。这个问题的根源往往不是 Token 本身有问题而是没搞清楚 Spring Security 的过滤器链和 OAuth2 资源服务器到底谁先谁后、谁负责什么。简单说Spring Security 是一套「认证 授权」的框架核心是一组按顺序执行的过滤器链FilterChain。它管的是「你是谁、你能干什么」。而 OAuth2 是一套授权协议它定义了怎么拿 Token、怎么用 Token 访问资源。在 Spring 生态里OAuth2 资源服务器的能力是「挂」在 Spring Security 过滤器链上的一个环节它负责从请求头里解析 Bearer Token然后交给 Spring Security 的上下文去判断权限。所以两者的关系可以这样理解Spring Security 是骨架和调度中心OAuth2 是其中一种具体的认证方式。你配置的SecurityFilterChain决定了哪些请求需要认证、用哪种方式认证而 OAuth2 资源服务器配置决定了 Token 怎么校验、校验通过后用户身份和权限怎么放进SecurityContext。这篇文章面向正在做 Spring Boot 后端、需要把 OAuth2 资源服务器接进 Spring Security 的开发者。我会给出一套可以直接复制的SecurityConfig和application.yml骨架然后用 curl 验证 Token 校验是否真的生效最后把常见的坑列出来。适合谁已经知道 JWT 大概长什么样、但被过滤器链顺序和配置类搞晕的人。2. 前置准备依赖、密钥与 TaoToken 接入信息在写配置之前先把依赖和基础信息准备好。这里我用 Spring Boot 3.x Spring Security 6.x 的组合OAuth2 资源服务器用spring-boot-starter-oauth2-resource-server这是目前官方推荐的方式不再依赖老的spring-security-oauth2那一套。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-oauth2-resource-server/artifactId /dependency如果你手上还没有可用的 Token 签发环境或者想先拿一个标准格式的 JWT 来验证资源服务器的校验逻辑可以用 TaoToken 的模型对话或 API Keys 页面生成一个测试用的 Key再配合它的接入文档确认请求头格式。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。注意 API 地址不带 UTM 参数直接用它做 base url 即可。资源服务器校验 Token 有两种主流方式一种是 JWT 本地校验用公钥验签不查授权中心另一种是 opaque token 远程校验把 Token 转发给授权中心 introspect 接口。本地校验性能好、无网络依赖适合大多数场景。下面我以 JWT 本地校验为主线因为它最能体现「过滤器链 OAuth2」的衔接点。你需要准备一个 JWK Set URI也就是授权中心暴露公钥的地址形如https://your-auth-server/oauth2/jwks。如果你用的是 TaoToken 这类服务可以在接入文档里找到对应的 JWKS 地址。拿到之后填进application.yml。3. 可复制配置SecurityFilterChain 与 application.yml 骨架先看application.yml这里定义了资源服务器的 issuer 和 jwk-set-uriSpring Security 会自动拉取公钥并缓存。spring: security: oauth2: resourceserver: jwt: issuer-uri: https://your-auth-server jwk-set-uri: https://your-auth-server/oauth2/jwks server: port: 8080注意issuer-uri和jwk-set-uri二选一即可如果两个都写Spring 会优先用jwk-set-uri。issuer-uri的好处是它会自动校验 Token 里的iss字段安全性更高。接下来是核心的SecurityConfig。这里我用 Spring Security 6 的 Lambda DSL 写法SecurityFilterChainBean 取代了老的WebSecurityConfigurerAdapter。import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; Configuration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf - csrf.disable()) .sessionManagement(session - session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth - auth .requestMatchers(/public/**).permitAll() .requestMatchers(/user/**).hasAuthority(SCOPE_user) .requestMatchers(/admin/**).hasAuthority(SCOPE_admin) .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 - oauth2 .jwt(jwt - jwt .jwtAuthenticationConverter(jwtAuthenticationConverter()) ) ); return http.build(); } private org.springframework.core.convert.converter.Converter org.springframework.security.oauth2.jwt.Jwt, org.springframework.security.authentication.AbstractAuthenticationToken jwtAuthenticationConverter() { org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter converter new org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter(); org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter authoritiesConverter new org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter(); authoritiesConverter.setAuthorityPrefix(SCOPE_); authoritiesConverter.setAuthoritiesClaimName(scope); converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter); return converter; } }这段配置里最关键的是.oauth2ResourceServer(oauth2 - oauth2.jwt(...))。它做的事情是在 Spring Security 过滤器链中插入BearerTokenAuthenticationFilter这个过滤器会从Authorization头里取出 Bearer Token然后交给JwtAuthenticationProvider去验签、解析 claims最后把结果放进SecurityContext。后面的authorizeHttpRequests规则才有东西可判断。jwtAuthenticationConverter的作用是把 JWT 里的scope字段映射成 Spring Security 的权限。默认情况下JwtGrantedAuthoritiesConverter会把 scope 值加上SCOPE_前缀所以配置里写hasAuthority(SCOPE_user)才能匹配上。如果你 Token 里的权限字段叫authorities或roles改setAuthoritiesClaimName即可。4. 验证请求用 curl 确认 Token 校验真的生效配置写完启动应用。接下来用 curl 做三组验证分别对应「无 Token」「有 Token 但权限不够」「有 Token 且权限正确」。第一组不带 Token 访问受保护接口curl -i http://localhost:8080/user/profile预期返回401 Unauthorized响应头里会有WWW-Authenticate: Bearer。这说明BearerTokenAuthenticationFilter没找到 Token直接判定未认证。第二组带一个格式正确但 scope 不含user的 Tokencurl -i -H Authorization: Bearer your-token http://localhost:8080/user/profile预期返回403 Forbidden。注意这里和 401 的区别401 是「你没认证」403 是「你认证了但没权限」。如果这里返回 401说明 Token 验签失败检查jwk-set-uri是否可达、Token 是否过期。第三组带一个 scope 包含user的 Tokencurl -i -H Authorization: Bearer your-token http://localhost:8080/user/profile预期返回200 OK和业务数据。到这一步说明过滤器链、OAuth2 资源服务器、权限映射三者已经串通了。如果你想更直观地看 Token 解析结果可以在 Controller 里注入Jwt对象import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; RestController public class ProfileController { GetMapping(/user/profile) public String profile(AuthenticationPrincipal Jwt jwt) { return subject jwt.getSubject() , scope jwt.getClaimAsString(scope); } }访问成功后返回的字符串里能看到sub和scope这就证明 Token 里的信息确实被解析并传到了业务层。5. 本篇常见错排查401、403 与过滤器顺序第一个高频错误配置了oauth2ResourceServer但依然返回 401且日志里没有验签失败信息。这通常是jwk-set-uri写错或网络不通Spring 拉不到公钥验签直接失败。排查方法是在启动日志里搜jwk看有没有Fetching JWK Set相关记录。如果用的是 TaoToken 的接入文档里的 JWKS 地址确认路径没有多余斜杠。第二个错误hasAuthority(user)匹配不上。原因是默认权限前缀是SCOPE_你写user自然匹配不到SCOPE_user。要么改配置里的setAuthorityPrefix()要么在规则里写全SCOPE_user。我建议保留前缀因为这样能区分 OAuth2 的 scope 和其他来源的权限。第三个错误自定义了一个Filter想手动解析 Token结果和BearerTokenAuthenticationFilter冲突。记住一旦启用oauth2ResourceServerToken 解析这件事就交给它了你的自定义过滤器应该放在它之后从SecurityContext里拿已经解析好的Authentication而不是重新解析一遍请求头。第四个错误SessionCreationPolicy.STATELESS没配导致 Spring Security 尝试创建 Session在分布式环境下出现状态不一致。资源服务器必须是无状态的这一行不能省。第五个错误issuer-uri配了但 Token 里的iss对不上返回 401。这种情况日志里会有Jwt issuer validation failed。检查授权中心实际签发的iss值和配置里的是否完全一致包括末尾斜杠。6. 把 Token 校验接进你的编码链路到这里Spring Security 和 OAuth2 的衔接点应该清楚了SecurityFilterChain负责定义规则和顺序oauth2ResourceServer负责插入 Token 解析过滤器JwtAuthenticationConverter负责把 claims 翻译成权限。三者缺一不可。如果你接下来要在本地长期跑这套资源服务器或者用它对接多个下游服务做联调建议把 Token 的获取和刷新也纳入日常流程。TaoToken 的 Coding Plan 适合这种需要反复验证 Token 校验、又不想每次手动换 Key 的场景入口在 https://taotoken.net/api 对应的控制台里可以找到。需要生成新的测试 Key 时直接去 API Keys 页面操作接入文档里有完整的请求头示例。最后留一个实用技巧在application.yml里把logging.level.org.springframework.securityDEBUG打开启动后你会看到过滤器链的完整顺序以及每个请求经过了哪些过滤器。这比任何文档都直观排障时先看这行日志能省掉大半猜测时间。