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

Spring OAuth2资源服务器特定端点自定义令牌校验实现方法

实现基于固定令牌的端点自定义校验

现有配置背景

当前Spring OAuth2资源服务器通过Issuer URI验证JWT,核心配置如下:

@EnableWebSecurity
class WebSecurityConfiguration {
    @Bean
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http.authorizeRequests()
            .antMatchers("/actuator/health").permitAll()
            .antMatchers("/**").hasAnyRole("User", "Client")
            .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt()
            .jwtAuthenticationConverter(jwtAuthenticationConverter())
        return http.build()
    }

    private fun jwtAuthenticationConverter(): Converter<Jwt?, out AbstractAuthenticationToken?> {
        val jwtConverter = JwtAuthenticationConverter()
        jwtConverter.setJwtGrantedAuthoritiesConverter(KeycloakRealmRoleConverter())
        return jwtConverter
    }
}

需求

为/api/custom/players端点实现基于环境变量PLAYERS_TOKEN的自定义令牌校验:请求需携带Bearer {PLAYERS_TOKEN}格式的Authorization头,校验通过后方可访问。

实现步骤

1. 读取环境变量

在配置类中注入环境变量PLAYERS_TOKEN:

@Value("\${PLAYERS_TOKEN}")
private lateinit var playersToken: String

2. 实现自定义令牌校验过滤器

创建PlayerTokenFilter,继承OncePerRequestFilter,针对目标端点做令牌校验:

class PlayerTokenFilter(
    private val playersToken: String
) : OncePerRequestFilter() {

    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        // 仅处理目标端点
        if (request.requestURI.startsWith("/api/custom/players")) {
            val authHeader = request.getHeader("Authorization")
            // 校验Authorization头格式及令牌匹配
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "缺失或无效的Authorization请求头")
                return
            }
            val token = authHeader.substring(7).trim()
            if (token != playersToken) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "无效令牌")
                return
            }
            // 校验通过,设置认证信息到SecurityContext
            val authentication = UsernamePasswordAuthenticationToken(
                "player-client",
                null,
                listOf(SimpleGrantedAuthority("ROLE_PLAYER"))
            )
            SecurityContextHolder.getContext().authentication = authentication
        }
        // 继续执行后续过滤器链
        filterChain.doFilter(request, response)
    }
}

3. 配置SecurityFilterChain,整合自定义过滤器

修改原filterChain方法,为目标端点单独配置,添加自定义过滤器并设置对应权限:

@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
    http
        .authorizeRequests()
            .antMatchers("/actuator/health").permitAll()
            // 目标端点需持有PLAYER角色(由自定义过滤器注入)
            .antMatchers("/api/custom/players/**").hasRole("PLAYER")
            .antMatchers("/**").hasAnyRole("User", "Client")
            .anyRequest().authenticated()
        .and()
        // 在用户名密码认证过滤器前执行自定义令牌校验
        .addFilterBefore(PlayerTokenFilter(playersToken), UsernamePasswordAuthenticationFilter::class.java)
        .oauth2ResourceServer()
            .jwt()
            .jwtAuthenticationConverter(jwtAuthenticationConverter())
    return http.build()
}

补充说明

  • 过滤器中注入的ROLE_PLAYER是为了配合hasRole("PLAYER")的权限校验,确保只有通过令牌校验的请求能访问目标端点;若无需权限细分,也可将目标端点配置为permitAll(),但建议保留角色校验以维持权限体系一致性。
  • 环境变量PLAYERS_TOKEN需在部署时正确配置,禁止硬编码到代码中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 11:15:36