From a95b1ac4fddecadcf1d11b8d559613e5b2e8019b Mon Sep 17 00:00:00 2001 From: Alexander Buntakov Date: Mon, 2 Aug 2021 16:06:42 +0300 Subject: [PATCH] split jwt issuer and verifier (#40) --- auth-jwt-core/build.gradle.kts | 1 + .../access/config/AccessTokenBeanConfig.kt | 38 +++++-------------- auth-jwt/build.gradle.kts | 3 +- .../jwt/configurations/JwtConfiguration.kt | 30 ++++++++++++++- .../JwtHttpSecurityConfigurator.kt | 4 +- .../properties/AccessTokenPublicProperties.kt | 15 ++++++++ .../auth/security/jwt/utils/JwtUtils.kt | 24 ++++++++++++ 7 files changed, 81 insertions(+), 34 deletions(-) create mode 100644 auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/properties/AccessTokenPublicProperties.kt create mode 100644 auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/utils/JwtUtils.kt diff --git a/auth-jwt-core/build.gradle.kts b/auth-jwt-core/build.gradle.kts index 229285c..5b4d9dd 100644 --- a/auth-jwt-core/build.gradle.kts +++ b/auth-jwt-core/build.gradle.kts @@ -6,6 +6,7 @@ plugins { dependencies { implementation(project(":auth-core")) + implementation(project(":auth-jwt")) implementation("com.auth0:java-jwt") implementation("org.springframework.security:spring-security-oauth2-jose") diff --git a/auth-jwt-core/src/main/kotlin/ru/touchin/auth/core/tokens/access/config/AccessTokenBeanConfig.kt b/auth-jwt-core/src/main/kotlin/ru/touchin/auth/core/tokens/access/config/AccessTokenBeanConfig.kt index e707958..0a8cea1 100644 --- a/auth-jwt-core/src/main/kotlin/ru/touchin/auth/core/tokens/access/config/AccessTokenBeanConfig.kt +++ b/auth-jwt-core/src/main/kotlin/ru/touchin/auth/core/tokens/access/config/AccessTokenBeanConfig.kt @@ -1,35 +1,33 @@ package ru.touchin.auth.core.tokens.access.config import com.auth0.jwt.algorithms.Algorithm +import org.springframework.beans.factory.annotation.Qualifier import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Import import ru.touchin.auth.core.tokens.access.properties.AccessTokenProperties -import ru.touchin.common.string.StringUtils.emptyString +import ru.touchin.auth.security.jwt.configurations.JwtConfiguration +import ru.touchin.auth.security.jwt.utils.JwtUtils.getKeySpec import java.security.KeyFactory import java.security.interfaces.RSAPrivateKey import java.security.interfaces.RSAPublicKey import java.security.spec.PKCS8EncodedKeySpec -import java.security.spec.X509EncodedKeySpec -import java.util.* @Configuration +@Import(JwtConfiguration::class) class AccessTokenBeanConfig(private val accessTokenProperties: AccessTokenProperties) { @Bean - fun accessTokenSigningAlgorithm(): Algorithm { + fun accessTokenSigningAlgorithm( + @Qualifier("accessTokenPublicKey") + accessTokenPublicKey: RSAPublicKey + ): Algorithm { return Algorithm.RSA256( - accessTokenPublicKey(), + accessTokenPublicKey, accessTokenPrivateKey() ) } - @Bean("accessTokenPublicKey") - fun accessTokenPublicKey(): RSAPublicKey { - val keySpecX509 = getKeySpec(accessTokenProperties.keyPair.public, ::X509EncodedKeySpec) - - return keyFactory.generatePublic(keySpecX509) as RSAPublicKey - } - @Bean("accessTokenPrivateKey") fun accessTokenPrivateKey(): RSAPrivateKey { val keySpecPKCS8 = getKeySpec(accessTokenProperties.keyPair.private, ::PKCS8EncodedKeySpec) @@ -37,22 +35,6 @@ class AccessTokenBeanConfig(private val accessTokenProperties: AccessTokenProper return keyFactory.generatePrivate(keySpecPKCS8) as RSAPrivateKey } - private fun getKeySpec(key: String, keySpecFn: (ByteArray) -> T): T { - val rawKey = getRawKey(key) - - return Base64.getDecoder() - .decode(rawKey) - .let(keySpecFn) - } - - private fun getRawKey(key: String): String { - return key - .replace("-----BEGIN .+KEY-----".toRegex(), emptyString()) - .replace("-----END .+KEY-----".toRegex(), emptyString()) - .replace("\n", emptyString()) - .trim() - } - companion object { val keyFactory: KeyFactory = KeyFactory.getInstance("RSA") } diff --git a/auth-jwt/build.gradle.kts b/auth-jwt/build.gradle.kts index 187cbba..55cb1ec 100644 --- a/auth-jwt/build.gradle.kts +++ b/auth-jwt/build.gradle.kts @@ -4,8 +4,7 @@ plugins { } dependencies { - implementation(project(":auth-jwt-core")) - + implementation(project(":common")) implementation(project(":common-spring-security")) implementation("org.springframework.security:spring-security-oauth2-jose") diff --git a/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/configurations/JwtConfiguration.kt b/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/configurations/JwtConfiguration.kt index 17aae35..0eefa9f 100644 --- a/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/configurations/JwtConfiguration.kt +++ b/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/configurations/JwtConfiguration.kt @@ -1,6 +1,32 @@ package ru.touchin.auth.security.jwt.configurations +import org.springframework.boot.context.properties.ConfigurationPropertiesScan +import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan +import ru.touchin.auth.security.jwt.properties.AccessTokenPublicProperties +import ru.touchin.auth.security.jwt.utils.JwtUtils.getKeySpec +import java.security.KeyFactory +import java.security.interfaces.RSAPublicKey +import java.security.spec.X509EncodedKeySpec -@ComponentScan("ru.touchin.auth.security.jwt.http.configurators") -class JwtConfiguration +@ComponentScan( + "ru.touchin.auth.security.jwt.http.configurators", + "ru.touchin.auth.security.jwt.properties", +) +@ConfigurationPropertiesScan("ru.touchin.auth.security.jwt.properties") +class JwtConfiguration { + + @Bean("accessTokenPublicKey") + fun accessTokenPublicKey( + accessTokenPublicProperties: AccessTokenPublicProperties, + ): RSAPublicKey { + val keySpecX509 = getKeySpec(accessTokenPublicProperties.keyPair.public, ::X509EncodedKeySpec) + + return keyFactory.generatePublic(keySpecX509) as RSAPublicKey + } + + companion object { + val keyFactory: KeyFactory = KeyFactory.getInstance("RSA") + } + +} diff --git a/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/http/configurators/JwtHttpSecurityConfigurator.kt b/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/http/configurators/JwtHttpSecurityConfigurator.kt index aebe4e0..c259d54 100644 --- a/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/http/configurators/JwtHttpSecurityConfigurator.kt +++ b/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/http/configurators/JwtHttpSecurityConfigurator.kt @@ -6,7 +6,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.oauth2.jwt.JwtDecoder import org.springframework.security.oauth2.jwt.NimbusJwtDecoder import org.springframework.stereotype.Component -import ru.touchin.auth.core.tokens.access.properties.AccessTokenProperties +import ru.touchin.auth.security.jwt.properties.AccessTokenPublicProperties import ru.touchin.common.spring.Ordered import ru.touchin.common.spring.security.http.configurators.HttpSecurityConfigurator import java.security.interfaces.RSAPublicKey @@ -16,7 +16,7 @@ import java.security.interfaces.RSAPublicKey class JwtHttpSecurityConfigurator( @Qualifier("accessTokenPublicKey") private val accessTokenPublicKey: RSAPublicKey, - private val accessTokenProperties: AccessTokenProperties + private val accessTokenProperties: AccessTokenPublicProperties ) : HttpSecurityConfigurator { override fun configure(http: HttpSecurity) { diff --git a/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/properties/AccessTokenPublicProperties.kt b/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/properties/AccessTokenPublicProperties.kt new file mode 100644 index 0000000..0a4fb9d --- /dev/null +++ b/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/properties/AccessTokenPublicProperties.kt @@ -0,0 +1,15 @@ +package ru.touchin.auth.security.jwt.properties + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.ConstructorBinding +import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm + +data class AccessTokenKeyPair(val public: String) + +@ConstructorBinding +@ConfigurationProperties(prefix = "token.access") +data class AccessTokenPublicProperties( + val keyPair: AccessTokenKeyPair, + val issuer: String, + val signatureAlgorithm: SignatureAlgorithm +) diff --git a/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/utils/JwtUtils.kt b/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/utils/JwtUtils.kt new file mode 100644 index 0000000..051d4e1 --- /dev/null +++ b/auth-jwt/src/main/kotlin/ru/touchin/auth/security/jwt/utils/JwtUtils.kt @@ -0,0 +1,24 @@ +package ru.touchin.auth.security.jwt.utils + +import ru.touchin.common.string.StringUtils.emptyString +import java.util.* + +object JwtUtils { + + fun getKeySpec(key: String, keySpecFn: (ByteArray) -> T): T { + val rawKey = getRawKey(key) + + return Base64.getDecoder() + .decode(rawKey) + .let(keySpecFn) + } + + private fun getRawKey(key: String): String { + return key + .replace("-----BEGIN .+KEY-----".toRegex(), emptyString()) + .replace("-----END .+KEY-----".toRegex(), emptyString()) + .replace("\n", emptyString()) + .trim() + } + +}