Skip to content

Instantly share code, notes, and snippets.

@foo4u
Last active March 15, 2023 17:39
Show Gist options
  • Save foo4u/45f6b9d16dd5739d342be5e889521117 to your computer and use it in GitHub Desktop.
Save foo4u/45f6b9d16dd5739d342be5e889521117 to your computer and use it in GitHub Desktop.
Spring Security Resource Server with multiple issuers
spring:
security:
oauth2:
resource-server:
jwt:
issuer-uri: https://token-exchange.example.com/
jwk-set-uri: https://token-exchange.example.com/.well-known/jwks.json
web:
resources:
add-mappings: false
example:
security:
oauth2:
resource-server:
jwt-set:
- issuer-uri: https://auth.example.com/
jwk-set-uri: https://auth.example.com/.well-known/jwks.json
package com.example.service.config
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.AuthenticationManagerResolver
import org.springframework.security.authentication.ProviderManager
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider
import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver
import org.springframework.security.web.SecurityFilterChain
import java.net.URL
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@EnableConfigurationProperties(AdditionalResourceServerProperties::class)
class SecurityConfig(
private val resourceServerProperties: OAuth2ResourceServerProperties,
private val additionalResourceServerProperties: AdditionalResourceServerProperties
) {
@Bean
fun httpSecurity(http: HttpSecurity): SecurityFilterChain =
http
.authorizeRequests { it.anyRequest().authenticated() }
.csrf { it.disable() }
.oauth2ResourceServer {
it.authenticationManagerResolver(
JwtIssuerAuthenticationManagerResolver(authResolver())
)
}
.build()
private fun primaryProviderManager(): ProviderManager =
buildProviderManager(jwkSetUri = resourceServerProperties.jwt.jwkSetUri)
private fun buildProviderManager(jwkSetUri: String): ProviderManager {
val decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
.jwsAlgorithm(SignatureAlgorithm.RS256)
.jwsAlgorithm(SignatureAlgorithm.ES256)
.jwsAlgorithm(SignatureAlgorithm.ES384)
.build()
return ProviderManager(JwtAuthenticationProvider(decoder))
}
private fun buildAuthResolverMap(): Map<String, ProviderManager> =
mutableMapOf(Pair(resourceServerProperties.jwt.issuerUri, primaryProviderManager())).apply {
additionalResourceServerProperties.jwtSet.forEach { issuer ->
put(issuer.issuerUri.toString(), buildProviderManager(issuer.jwkSetUri.toString()))
}
}
private fun authResolver(): MultiIssuerAuthenticationManagerResolver =
MultiIssuerAuthenticationManagerResolver(buildAuthResolverMap())
}
class MultiIssuerAuthenticationManagerResolver(
private val trustedIssuers: Map<String, AuthenticationManager>
) : AuthenticationManagerResolver<String> {
override fun resolve(context: String?): AuthenticationManager? =
trustedIssuers[context]
}
@ConfigurationProperties(prefix = "example.security.oauth2.resource-server")
@ConstructorBinding
data class AdditionalResourceServerProperties(
val jwtSet: List<JwtIssuerProperties>
) {
companion object {
@ConstructorBinding
data class JwtIssuerProperties(
val issuerUri: URL,
val jwkSetUri: URL
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment