使用带有spring security的keycloak JWT令牌时如何修复403
Posted
技术标签:
【中文标题】使用带有spring security的keycloak JWT令牌时如何修复403【英文标题】:How to fix 403 when using keycloak JWT token with spring security 【发布时间】:2021-05-01 05:21:13 【问题描述】: 我已经用Spring Security
和 Keycloak
配置了一个 Spring Boot
项目
我正在从 Keycloak 服务器获取令牌
当我在应用程序中调用端点时,我得到 403 和令牌
代码如下:
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
class SecurityConfig
@Bean
fun reactiveJwtDecoder() = NimbusReactiveJwtDecoder
val claimsSet = it.jwtClaimsSet
println(Gson().toJson(claimsSet))
Mono.just(claimsSet)
@Bean
fun configure(
http: ServerHttpSecurity,
authenticationConverter: KeycloakReactiveJwtAuthenticationConverter
): SecurityWebFilterChain = http.apply
headers().frameOptions().disable()
.and().csrf().disable()
.authorizeExchange()
.pathMatchers("/v/*").permitAll()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(authenticationConverter)
.build()
@Bean
fun clientRegistration(): ReactiveClientRegistrationRepository =
InMemoryReactiveClientRegistrationRepository(
ClientRegistrations.fromOidcIssuerLocation("http://localhost:8080/auth/realms/demo")
.clientId("demo")
.clientSecret("2de854c0-57a7-42f1-8a07-01773301646f")
.build()
)
@Component
class KeycloakReactiveJwtAuthenticationConverter : Converter<Jwt, Mono<AbstractAuthenticationToken>>
override fun convert(jwt: Jwt): Mono<AbstractAuthenticationToken>?
return Mono.just(AuthenticationToken(jwt))
fun convert(jwt: Jwt): Collection<GrantedAuthority>?
@Suppress("UNCHECKED_CAST")
val realmAccess = jwt.claims["realm_access"] as? Map<String, List<String>> ?: emptyMap()
val roles = realmAccess.getOrDefault("roles", listOf()).map "ROLE_$it"
return AuthorityUtils.createAuthorityList(*roles.toTypedArray())
data class AuthenticationToken(
val jwt: Jwt
) : AbstractAuthenticationToken(convert(jwt))
override fun getCredentials(): Any = ClientInfo(
clientId = jwt.getClaimAsString("client_id")
)
override fun getPrincipal(): Any = ClientInfo(
clientId = jwt.getClaimAsString("client_id")
)
data class ClientInfo(
val clientId: String
)
被传递的Json
"claims":"sub":"ba95843a-4ba8-4247-b2b9-4db993de7371","resource_access":"demo":"roles":["uma_protection"],"account":"roles":["manage-account","manage-account-links","view-profile"],"clientId":"demo","email_verified":false,"clientHost":"127.0.0.1","iss":"http://localhost:8080/auth/realms/demo","typ":"Bearer","preferred_username":"service-account-demo","clientAddress":"127.0.0.1","client_id":"demo","aud":["account"],"acr":"1","realm_access":"roles":["offline_access","uma_authorization","USER"],"azp":"demo","scope":"profile email","exp":"Jan 27, 2021, 5:15:29 PM","iat":"Jan 27, 2021, 5:10:29 PM","jti":"51831211-7831-4bdd-9e31-d0ba2aa7f733"
调试日志
2021-01-27 23:41:47.896 DEBUG 14484 --- [oundedElastic-1] o.s.w.s.s.DefaultWebSessionManager : Created new WebSession.
2021-01-27 23:41:47.899 DEBUG 14484 --- [oundedElastic-1] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcherpattern='/logout', method=POST
2021-01-27 23:41:47.900 DEBUG 14484 --- [oundedElastic-1] athPatternParserServerWebExchangeMatcher : Request 'GET /demo' doesn't match 'POST /logout'
2021-01-27 23:41:47.900 DEBUG 14484 --- [oundedElastic-1] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found
2021-01-27 23:41:47.901 DEBUG 14484 --- [oundedElastic-1] a.DelegatingReactiveAuthorizationManager : Checking authorization on '/demo' using org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager@6d47f864
2021-01-27 23:41:47.903 DEBUG 14484 --- [oundedElastic-1] o.s.s.w.s.a.AuthorizationWebFilter : Authorization failed: Access Denied
2021-01-27 23:41:47.911 DEBUG 14484 --- [oundedElastic-1] o.s.w.s.adapter.HttpWebHandlerAdapter : [53903720-1] Completed 403 FORBIDDEN
这里有什么问题?
【问题讨论】:
【参考方案1】:https://***.com/questions/61345957/spring-security-returns-403-with-valid-jwt
这里的第一个答案提供了解决方案
我的确切代码class CustomJwtAuthenticationConverter : Converter<Jwt, Mono<AbstractAuthenticationToken>>
private val jwtGrantedAuthoritiesConverter = JwtGrantedAuthoritiesConverter()
override fun convert(source: Jwt): Mono<AbstractAuthenticationToken>
val authorities = source.getClaimAsString("authorities")?.split(",")?.map SimpleGrantedAuthority(it)
return Mono.just(JwtAuthenticationToken(source, authorities.orEmpty()))
【讨论】:
我有相同的堆栈跟踪,但我什至没有使用 OAuth。可能是什么问题?以上是关于使用带有spring security的keycloak JWT令牌时如何修复403的主要内容,如果未能解决你的问题,请参考以下文章
如何使用带有多个过滤器和 Spring Security 的 Spring DelegatingFilterProxy?
我们如何使用带有 Spring 5.0 的最新 spring-security-oauth2 jar 来实现授权服务器?
带有 Spring 会话的 Spring Security SAML
使用带有 Java 配置的 Spring Security 注销