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
相关产品推荐
相关产品推荐

