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

Keycloak认证后Spring Security Context未设置问题求助

问题根因

你遇到的SecurityContext为空的问题是由3个配置和逻辑错误共同导致的:

  • 授权规则配置错误:SecurityConfig中设置了anyRequest().permitAll(),Spring Security不会对全放开的请求触发Keycloak令牌校验流程,自然不会将解析后的认证信息写入SecurityContext
  • 手动获取令牌未同步到Spring上下文:你在signIn方法中直接调用AuthzClient获取JWT的操作只是客户端和Keycloak的独立交互,Spring Security感知不到该操作,也不会自动解析令牌写入上下文
  • 配置项冲突:同时配置了keycloak.public-client=true、keycloak.bearer-only=true和credentials.secret,三者逻辑互斥:public-client用于无密钥的前端客户端场景,bearer-only用于仅校验令牌的后端服务场景,不能同时开启。
修复步骤

1. 修正application.properties配置

删除冲突的keycloak.public-client=true配置,调整后配置如下:

keycloak.realm=Helpdesk
keycloak.auth-server-url=http://localhost:8180/auth
keycloak.resource=helpdesk
keycloak.credentials.secret=XXXXXXXXXXXXXXXXXXXXXXXXXX
keycloak.use-resource-role-mappings=true
keycloak.ssl-required=external
keycloak.bearer-only=true

2. 调整SecurityConfig授权规则

不要全接口放开权限,仅开放登录等不需要认证的接口,其余接口走认证流程:

override fun configure(http: HttpSecurity) {
    super.configure(http)
    http
        .csrf().disable()
        .authorizeRequests()
        .antMatchers("/auth/signin").permitAll() // 替换为你实际的登录接口路径
        .anyRequest().authenticated()
}

3. 补充signIn方法的上下文写入逻辑

手动拿到JWT后,需要主动解析构建KeycloakAuthenticationToken并写入SecurityContext,修改后逻辑如下:

@Service
class AuthenticationService(
    private val keycloakRequestConfig: KeycloakRequestConfig,
    private val keycloakDeployment: KeycloakDeployment,
    private val adapterTokenStore: AdapterTokenStore
) {

    // 注入你之前定义的AuthorityMapper复用权限映射规则
    @Autowired
    private lateinit var authorityMapper: SimpleAuthorityMapper

    fun signIn(signInRequest: SignInRequest): AccessTokenResponse {
        logger.info { ">>>>>>>>> User ${signInRequest.username} logs in" }
        val authClient: AuthzClient = AuthzClient.create(keycloakRequestConfig)
        val response: AccessTokenResponse = authClient.obtainAccessToken(signInRequest.username, signInRequest.password)
        
        // 新增逻辑:解析令牌并写入Spring Security上下文
        val tokenStr = response.token
        val securityContext = adapterTokenStore.loadKeycloakSecurityContext(tokenStr, null, null)
        val account = SimpleKeycloakAccount(keycloakDeployment, emptySet(), securityContext)
        val principal = KeycloakPrincipal(account)
        val roles = securityContext.token.realmAccess.roles.map { SimpleGrantedAuthority(it) }
        val authentication = KeycloakAuthenticationToken(
            principal, 
            false, 
            authorityMapper.mapAuthorities(roles)
        )
        SecurityContextHolder.getContext().authentication = authentication

        logger.info { "<<<<<<<<< User ${getCurrentLoggedUserId()} logged" }
        return response
    }
}
补充说明

后续其他接口请求只要在请求头中携带Authorization: Bearer {JWT令牌},Keycloak的过滤器会自动完成令牌解析、认证对象构建和上下文写入操作,不需要再手动处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 03:48:03