mirror of https://github.com/usbharu/Hideout.git
feat: Spring Securityの導入
This commit is contained in:
parent
ef6f9ae6ab
commit
6aaf01a026
|
@ -135,10 +135,15 @@ dependencies {
|
||||||
ksp("io.insert-koin:koin-ksp-compiler:1.2.0")
|
ksp("io.insert-koin:koin-ksp-compiler:1.2.0")
|
||||||
|
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-oauth2-authorization-server")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
|
||||||
implementation("jakarta.validation:jakarta.validation-api")
|
implementation("jakarta.validation:jakarta.validation-api")
|
||||||
implementation("jakarta.annotation:jakarta.annotation-api:2.1.0")
|
implementation("jakarta.annotation:jakarta.annotation-api:2.1.0")
|
||||||
compileOnly("io.swagger.core.v3:swagger-annotations:2.2.6")
|
compileOnly("io.swagger.core.v3:swagger-annotations:2.2.6")
|
||||||
implementation("io.swagger.core.v3:swagger-models:2.2.6")
|
implementation("io.swagger.core.v3:swagger-models:2.2.6")
|
||||||
|
implementation("org.jetbrains.exposed:exposed-java-time:$exposed_version")
|
||||||
|
implementation("org.jetbrains.exposed:spring-transaction:$exposed_version")
|
||||||
|
|
||||||
implementation("io.ktor:ktor-client-logging-jvm:$ktor_version")
|
implementation("io.ktor:ktor-client-logging-jvm:$ktor_version")
|
||||||
implementation("io.ktor:ktor-server-host-common-jvm:$ktor_version")
|
implementation("io.ktor:ktor-server-host-common-jvm:$ktor_version")
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
package dev.usbharu.hideout.config
|
||||||
|
|
||||||
|
import com.nimbusds.jose.jwk.JWKSet
|
||||||
|
import com.nimbusds.jose.jwk.RSAKey
|
||||||
|
import com.nimbusds.jose.jwk.source.ImmutableJWKSet
|
||||||
|
import com.nimbusds.jose.jwk.source.JWKSource
|
||||||
|
import com.nimbusds.jose.proc.SecurityContext
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.annotation.Order
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.security.config.Customizer
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtDecoder
|
||||||
|
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration
|
||||||
|
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings
|
||||||
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||||
|
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher
|
||||||
|
import java.security.KeyPairGenerator
|
||||||
|
import java.security.interfaces.RSAPrivateKey
|
||||||
|
import java.security.interfaces.RSAPublicKey
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(1)
|
||||||
|
fun oauth2SecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
|
||||||
|
http
|
||||||
|
.exceptionHandling {
|
||||||
|
it.defaultAuthenticationEntryPointFor(
|
||||||
|
LoginUrlAuthenticationEntryPoint("/login"), MediaTypeRequestMatcher(MediaType.TEXT_HTML)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.oauth2ResourceServer {
|
||||||
|
it.jwt(Customizer.withDefaults())
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(2)
|
||||||
|
fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests {
|
||||||
|
it.anyRequest().authenticated()
|
||||||
|
}
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun passwordEncoder(): PasswordEncoder {
|
||||||
|
return BCryptPasswordEncoder()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun genJwkSource(): JWKSource<SecurityContext> {
|
||||||
|
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||||
|
keyPairGenerator.initialize(2048)
|
||||||
|
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
||||||
|
val rsaPublicKey = generateKeyPair.public as RSAPublicKey
|
||||||
|
val rsaPrivateKey = generateKeyPair.private as RSAPrivateKey
|
||||||
|
val rsaKey = RSAKey
|
||||||
|
.Builder(rsaPublicKey)
|
||||||
|
.privateKey(rsaPrivateKey)
|
||||||
|
.keyID(UUID.randomUUID().toString())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val jwkSet = JWKSet(rsaKey)
|
||||||
|
return ImmutableJWKSet(jwkSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(name = ["hideout.security.jwt.generate"], havingValue = "")
|
||||||
|
fun loadJwkSource(jwkConfig: JwkConfig): JWKSource<SecurityContext> {
|
||||||
|
val rsaKey = RSAKey.Builder(jwkConfig.publicKey)
|
||||||
|
.privateKey(jwkConfig.privateKey)
|
||||||
|
.keyID(jwkConfig.keyId)
|
||||||
|
.build()
|
||||||
|
return ImmutableJWKSet(JWKSet(rsaKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun jwtDecoder(jwkSource: JWKSource<SecurityContext>): JwtDecoder {
|
||||||
|
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun authorizationServerSettings(): AuthorizationServerSettings {
|
||||||
|
return AuthorizationServerSettings.builder().build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ConfigurationProperties("hideout.security.jwt")
|
||||||
|
@ConditionalOnProperty(name = ["hideout.security.jwt.generate"], havingValue = "")
|
||||||
|
data class JwkConfig(
|
||||||
|
val keyId: String,
|
||||||
|
val publicKey: RSAPublicKey,
|
||||||
|
val privateKey: RSAPrivateKey
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dev.usbharu.hideout.controller
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.dto.JwtToken
|
||||||
|
import dev.usbharu.hideout.service.api.UserAuthApiService
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
|
import org.springframework.stereotype.Controller
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class DefaultApiImpl(private val userAuthApiService: UserAuthApiService) : DefaultApi {
|
||||||
|
override fun refreshTokenPost(): ResponseEntity<JwtToken> {
|
||||||
|
return ResponseEntity(HttpStatus.OK)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
package dev.usbharu.hideout.repository
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.repository.RegisteredClient.clientId
|
||||||
|
import dev.usbharu.hideout.repository.RegisteredClient.clientSettings
|
||||||
|
import dev.usbharu.hideout.repository.RegisteredClient.tokenSettings
|
||||||
|
import dev.usbharu.hideout.util.JsonUtil
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.javatime.CurrentTimestamp
|
||||||
|
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import org.springframework.security.oauth2.core.AuthorizationGrantType
|
||||||
|
import org.springframework.security.oauth2.core.ClientAuthenticationMethod
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository
|
||||||
|
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings
|
||||||
|
import org.springframework.security.oauth2.server.authorization.settings.ConfigurationSettingNames
|
||||||
|
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat
|
||||||
|
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
import java.time.Instant
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient as SpringRegisteredClient
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class RegisteredClientRepositoryImpl(private val database: Database) : RegisteredClientRepository {
|
||||||
|
|
||||||
|
init {
|
||||||
|
transaction(database) {
|
||||||
|
SchemaUtils.create(RegisteredClient)
|
||||||
|
SchemaUtils.createMissingTablesAndColumns(RegisteredClient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun save(registeredClient: SpringRegisteredClient?) {
|
||||||
|
requireNotNull(registeredClient)
|
||||||
|
val singleOrNull = RegisteredClient.select { RegisteredClient.id eq registeredClient.id }.singleOrNull()
|
||||||
|
if (singleOrNull == null) {
|
||||||
|
RegisteredClient.insert {
|
||||||
|
it[id] = registeredClient.id
|
||||||
|
it[clientId] = registeredClient.clientId
|
||||||
|
it[clientIdIssuedAt] = registeredClient.clientIdIssuedAt ?: Instant.now()
|
||||||
|
it[clientSecret] = registeredClient.clientSecret
|
||||||
|
it[clientSecretExpiresAt] = registeredClient.clientSecretExpiresAt
|
||||||
|
it[clientName] = registeredClient.clientName
|
||||||
|
it[clientAuthenticationMethods] = registeredClient.clientAuthenticationMethods.joinToString(",")
|
||||||
|
it[authorizationGrantTypes] = registeredClient.authorizationGrantTypes.joinToString(",")
|
||||||
|
it[redirectUris] = registeredClient.redirectUris.joinToString(",")
|
||||||
|
it[postLogoutRedirectUris] = registeredClient.postLogoutRedirectUris.joinToString(",")
|
||||||
|
it[scopes] = registeredClient.scopes.joinToString(",")
|
||||||
|
it[clientSettings] = JsonUtil.mapToJson(registeredClient.clientSettings.settings)
|
||||||
|
it[tokenSettings] = JsonUtil.mapToJson(registeredClient.tokenSettings.settings)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RegisteredClient.update({ RegisteredClient.id eq registeredClient.id }) {
|
||||||
|
it[clientId] = registeredClient.clientId
|
||||||
|
it[clientIdIssuedAt] = registeredClient.clientIdIssuedAt ?: Instant.now()
|
||||||
|
it[clientSecret] = registeredClient.clientSecret
|
||||||
|
it[clientSecretExpiresAt] = registeredClient.clientSecretExpiresAt
|
||||||
|
it[clientName] = registeredClient.clientName
|
||||||
|
it[clientAuthenticationMethods] = registeredClient.clientAuthenticationMethods.joinToString(",")
|
||||||
|
it[authorizationGrantTypes] = registeredClient.authorizationGrantTypes.joinToString(",")
|
||||||
|
it[redirectUris] = registeredClient.redirectUris.joinToString(",")
|
||||||
|
it[postLogoutRedirectUris] = registeredClient.postLogoutRedirectUris.joinToString(",")
|
||||||
|
it[scopes] = registeredClient.scopes.joinToString(",")
|
||||||
|
it[clientSettings] = JsonUtil.mapToJson(registeredClient.clientSettings.settings)
|
||||||
|
it[tokenSettings] = JsonUtil.mapToJson(registeredClient.tokenSettings.settings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findById(id: String?): SpringRegisteredClient? {
|
||||||
|
if (id == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return RegisteredClient.select {
|
||||||
|
RegisteredClient.id eq id
|
||||||
|
}.singleOrNull()?.toRegisteredClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findByClientId(clientId: String?): SpringRegisteredClient? {
|
||||||
|
if (clientId == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return RegisteredClient.select {
|
||||||
|
RegisteredClient.clientId eq clientId
|
||||||
|
}.singleOrNull()?.toRegisteredClient()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql
|
||||||
|
object RegisteredClient : Table("registered_client") {
|
||||||
|
val id = varchar("id", 100)
|
||||||
|
val clientId = varchar("client_id", 100)
|
||||||
|
val clientIdIssuedAt = timestamp("client_id_issued_at").defaultExpression(CurrentTimestamp())
|
||||||
|
val clientSecret = varchar("client_secret", 200).nullable().default(null)
|
||||||
|
val clientSecretExpiresAt = timestamp("client_secret_expires_at").nullable().default(null)
|
||||||
|
val clientName = varchar("client_name", 200)
|
||||||
|
val clientAuthenticationMethods = varchar("client_authentication_methods", 1000)
|
||||||
|
val authorizationGrantTypes = varchar("authorization_grant_types", 1000)
|
||||||
|
val redirectUris = varchar("redirect_uris", 1000).nullable().default(null)
|
||||||
|
val postLogoutRedirectUris = varchar("post_logout_redirect_uris", 1000).nullable().default(null)
|
||||||
|
val scopes = varchar("scopes", 1000)
|
||||||
|
val clientSettings = varchar("client_settings", 2000)
|
||||||
|
val tokenSettings = varchar("token_settings", 2000)
|
||||||
|
|
||||||
|
override val primaryKey = PrimaryKey(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ResultRow.toRegisteredClient(): SpringRegisteredClient {
|
||||||
|
|
||||||
|
fun resolveClientAuthenticationMethods(string: String): ClientAuthenticationMethod {
|
||||||
|
return when (string) {
|
||||||
|
ClientAuthenticationMethod.CLIENT_SECRET_BASIC.value -> ClientAuthenticationMethod.CLIENT_SECRET_BASIC
|
||||||
|
ClientAuthenticationMethod.CLIENT_SECRET_JWT.value -> ClientAuthenticationMethod.CLIENT_SECRET_JWT
|
||||||
|
ClientAuthenticationMethod.CLIENT_SECRET_POST.value -> ClientAuthenticationMethod.CLIENT_SECRET_POST
|
||||||
|
ClientAuthenticationMethod.NONE.value -> ClientAuthenticationMethod.NONE
|
||||||
|
else -> {
|
||||||
|
ClientAuthenticationMethod(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolveAuthorizationGrantType(string: String): AuthorizationGrantType {
|
||||||
|
return when (string) {
|
||||||
|
AuthorizationGrantType.AUTHORIZATION_CODE.value -> AuthorizationGrantType.AUTHORIZATION_CODE
|
||||||
|
AuthorizationGrantType.CLIENT_CREDENTIALS.value -> AuthorizationGrantType.CLIENT_CREDENTIALS
|
||||||
|
AuthorizationGrantType.REFRESH_TOKEN.value -> AuthorizationGrantType.REFRESH_TOKEN
|
||||||
|
else -> {
|
||||||
|
AuthorizationGrantType(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val clientAuthenticationMethods = this[RegisteredClient.clientAuthenticationMethods].split(",").toSet()
|
||||||
|
val authorizationGrantTypes = this[RegisteredClient.authorizationGrantTypes].split(",").toSet()
|
||||||
|
val redirectUris = this[RegisteredClient.redirectUris]?.split(",").orEmpty().toSet()
|
||||||
|
val postLogoutRedirectUris = this[RegisteredClient.postLogoutRedirectUris]?.split(",").orEmpty().toSet()
|
||||||
|
val clientScopes = this[RegisteredClient.scopes].split(",").toSet()
|
||||||
|
|
||||||
|
val builder = SpringRegisteredClient
|
||||||
|
.withId(this[RegisteredClient.id])
|
||||||
|
.clientId(this[clientId])
|
||||||
|
.clientIdIssuedAt(this[RegisteredClient.clientIdIssuedAt])
|
||||||
|
.clientSecret(this[RegisteredClient.clientSecret])
|
||||||
|
.clientSecretExpiresAt(this[RegisteredClient.clientSecretExpiresAt])
|
||||||
|
.clientName(this[RegisteredClient.clientName])
|
||||||
|
.clientAuthenticationMethods {
|
||||||
|
clientAuthenticationMethods.forEach { s ->
|
||||||
|
it.add(resolveClientAuthenticationMethods(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.authorizationGrantTypes {
|
||||||
|
authorizationGrantTypes.forEach { s ->
|
||||||
|
it.add(resolveAuthorizationGrantType(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.redirectUris { it.addAll(redirectUris) }
|
||||||
|
.postLogoutRedirectUris { it.addAll(postLogoutRedirectUris) }
|
||||||
|
.scopes { it.addAll(clientScopes) }
|
||||||
|
.clientSettings(ClientSettings.withSettings(JsonUtil.jsonToMap(this[clientSettings])).build())
|
||||||
|
|
||||||
|
|
||||||
|
val tokenSettingsMap = JsonUtil.jsonToMap<String, Any>(this[tokenSettings])
|
||||||
|
val withSettings = TokenSettings.withSettings(tokenSettingsMap)
|
||||||
|
if (tokenSettingsMap.containsKey(ConfigurationSettingNames.Token.ACCESS_TOKEN_FORMAT)) {
|
||||||
|
withSettings.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
|
||||||
|
}
|
||||||
|
builder.tokenSettings(withSettings.build())
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package dev.usbharu.hideout.service.auth
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||||
|
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent as AuthorizationConsent
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ExposedOAuth2AuthorizationConsentService(private val registeredClientRepository: RegisteredClientRepository) :
|
||||||
|
OAuth2AuthorizationConsentService {
|
||||||
|
override fun save(authorizationConsent: AuthorizationConsent?) {
|
||||||
|
requireNotNull(authorizationConsent)
|
||||||
|
val singleOrNull =
|
||||||
|
OAuth2AuthorizationConsent.select { OAuth2AuthorizationConsent.registeredClientId eq authorizationConsent.registeredClientId and (OAuth2AuthorizationConsent.principalName eq authorizationConsent.principalName) }
|
||||||
|
.singleOrNull()
|
||||||
|
if (singleOrNull == null) {
|
||||||
|
OAuth2AuthorizationConsent.insert {
|
||||||
|
it[registeredClientId] = authorizationConsent.registeredClientId
|
||||||
|
it[principalName] = authorizationConsent.principalName
|
||||||
|
it[authorities] = authorizationConsent.authorities.joinToString(",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(authorizationConsent: AuthorizationConsent?) {
|
||||||
|
if (authorizationConsent == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
OAuth2AuthorizationConsent.deleteWhere {
|
||||||
|
registeredClientId eq authorizationConsent.registeredClientId and (principalName eq principalName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findById(registeredClientId: String?, principalName: String?): AuthorizationConsent? {
|
||||||
|
requireNotNull(registeredClientId)
|
||||||
|
requireNotNull(principalName)
|
||||||
|
|
||||||
|
return OAuth2AuthorizationConsent.select { OAuth2AuthorizationConsent.registeredClientId eq registeredClientId and (OAuth2AuthorizationConsent.principalName eq principalName) }
|
||||||
|
.singleOrNull()?.toAuthorizationConsent()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ResultRow.toAuthorizationConsent(): AuthorizationConsent {
|
||||||
|
val registeredClientId = this[OAuth2AuthorizationConsent.registeredClientId]
|
||||||
|
val registeredClient = registeredClientRepository.findById(registeredClientId)
|
||||||
|
|
||||||
|
val principalName = this[OAuth2AuthorizationConsent.principalName]
|
||||||
|
val builder = AuthorizationConsent.withId(registeredClientId, principalName)
|
||||||
|
|
||||||
|
this[OAuth2AuthorizationConsent.authorities].split(",").forEach {
|
||||||
|
builder.authority(SimpleGrantedAuthority(it))
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object OAuth2AuthorizationConsent : Table("oauth2_authorization_consent") {
|
||||||
|
val registeredClientId = varchar("registered_client_id", 100)
|
||||||
|
val principalName = varchar("principal_name", 200)
|
||||||
|
val authorities = varchar("authorities", 1000)
|
||||||
|
override val primaryKey = PrimaryKey(registeredClientId, principalName)
|
||||||
|
}
|
|
@ -0,0 +1,329 @@
|
||||||
|
package dev.usbharu.hideout.service.auth
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.util.JsonUtil
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||||
|
import org.springframework.security.oauth2.core.*
|
||||||
|
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames
|
||||||
|
import org.springframework.security.oauth2.core.oidc.OidcIdToken
|
||||||
|
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames
|
||||||
|
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization
|
||||||
|
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode
|
||||||
|
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService
|
||||||
|
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ExposedOAuth2AuthorizationService(private val registeredClientRepository: RegisteredClientRepository) :
|
||||||
|
OAuth2AuthorizationService {
|
||||||
|
override fun save(authorization: OAuth2Authorization?) {
|
||||||
|
requireNotNull(authorization)
|
||||||
|
val singleOrNull = Authorization.select { Authorization.id eq authorization.id }.singleOrNull()
|
||||||
|
if (singleOrNull == null) {
|
||||||
|
val authorizationCodeToken = authorization.getToken(OAuth2AuthorizationCode::class.java)
|
||||||
|
val accessToken = authorization.getToken(OAuth2AccessToken::class.java)
|
||||||
|
val refreshToken = authorization.getToken(OAuth2RefreshToken::class.java)
|
||||||
|
val oidcIdToken = authorization.getToken(OidcIdToken::class.java)
|
||||||
|
val userCode = authorization.getToken(OAuth2UserCode::class.java)
|
||||||
|
val deviceCode = authorization.getToken(OAuth2DeviceCode::class.java)
|
||||||
|
Authorization.insert {
|
||||||
|
it[id] = authorization.id
|
||||||
|
it[registeredClientId] = authorization.registeredClientId
|
||||||
|
it[principalName] = authorization.principalName
|
||||||
|
it[authorizationGrantType] = authorization.authorizationGrantType.value
|
||||||
|
it[authorizedScopes] = authorization.authorizedScopes.joinToString(",").takeIf { it.isEmpty() }
|
||||||
|
it[attributes] = JsonUtil.mapToJson(authorization.attributes)
|
||||||
|
it[state] = authorization.getAttribute(OAuth2ParameterNames.STATE)
|
||||||
|
it[authorizationCodeValue] = authorizationCodeToken?.token?.tokenValue
|
||||||
|
it[authorizationCodeIssuedAt] = authorizationCodeToken?.token?.issuedAt
|
||||||
|
it[authorizationCodeExpiresAt] = authorizationCodeToken?.token?.expiresAt
|
||||||
|
it[authorizationCodeMetadata] = authorizationCodeToken?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
it[accessTokenValue] = accessToken?.token?.tokenValue
|
||||||
|
it[accessTokenIssuedAt] = accessToken?.token?.issuedAt
|
||||||
|
it[accessTokenExpiresAt] = accessToken?.token?.expiresAt
|
||||||
|
it[accessTokenMetadata] = accessToken?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
it[accessTokenType] = accessToken?.token?.tokenType?.value
|
||||||
|
it[accessTokenScopes] = accessToken?.token?.scopes?.joinToString(",")?.takeIf { it.isEmpty() }
|
||||||
|
it[refreshTokenValue] = refreshToken?.token?.tokenValue
|
||||||
|
it[refreshTokenIssuedAt] = refreshToken?.token?.issuedAt
|
||||||
|
it[refreshTokenExpiresAt] = refreshToken?.token?.expiresAt
|
||||||
|
it[refreshTokenMetadata] = refreshToken?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
it[oidcIdTokenValue] = oidcIdToken?.token?.tokenValue
|
||||||
|
it[oidcIdTokenIssuedAt] = oidcIdToken?.token?.issuedAt
|
||||||
|
it[oidcIdTokenExpiresAt] = oidcIdToken?.token?.expiresAt
|
||||||
|
it[oidcIdTokenMetadata] = oidcIdToken?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
it[userCodeValue] = userCode?.token?.tokenValue
|
||||||
|
it[userCodeIssuedAt] = userCode?.token?.issuedAt
|
||||||
|
it[userCodeExpiresAt] = userCode?.token?.expiresAt
|
||||||
|
it[userCodeMetadata] = userCode?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
it[deviceCodeValue] = deviceCode?.token?.tokenValue
|
||||||
|
it[deviceCodeIssuedAt] = deviceCode?.token?.issuedAt
|
||||||
|
it[deviceCodeExpiresAt] = deviceCode?.token?.expiresAt
|
||||||
|
it[deviceCodeMetadata] = deviceCode?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val authorizationCodeToken = authorization.getToken(OAuth2AuthorizationCode::class.java)
|
||||||
|
val accessToken = authorization.getToken(OAuth2AccessToken::class.java)
|
||||||
|
val refreshToken = authorization.getToken(OAuth2RefreshToken::class.java)
|
||||||
|
val oidcIdToken = authorization.getToken(OidcIdToken::class.java)
|
||||||
|
val userCode = authorization.getToken(OAuth2UserCode::class.java)
|
||||||
|
val deviceCode = authorization.getToken(OAuth2DeviceCode::class.java)
|
||||||
|
Authorization.update({ Authorization.id eq authorization.id }) {
|
||||||
|
it[registeredClientId] = authorization.registeredClientId
|
||||||
|
it[principalName] = authorization.principalName
|
||||||
|
it[authorizationGrantType] = authorization.authorizationGrantType.value
|
||||||
|
it[authorizedScopes] = authorization.authorizedScopes.joinToString(",").takeIf { it.isEmpty() }
|
||||||
|
it[attributes] = JsonUtil.mapToJson(authorization.attributes)
|
||||||
|
it[state] = authorization.getAttribute(OAuth2ParameterNames.STATE)
|
||||||
|
it[authorizationCodeValue] = authorizationCodeToken?.token?.tokenValue
|
||||||
|
it[authorizationCodeIssuedAt] = authorizationCodeToken?.token?.issuedAt
|
||||||
|
it[authorizationCodeExpiresAt] = authorizationCodeToken?.token?.expiresAt
|
||||||
|
it[authorizationCodeMetadata] = authorizationCodeToken?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
it[accessTokenValue] = accessToken?.token?.tokenValue
|
||||||
|
it[accessTokenIssuedAt] = accessToken?.token?.issuedAt
|
||||||
|
it[accessTokenExpiresAt] = accessToken?.token?.expiresAt
|
||||||
|
it[accessTokenMetadata] = accessToken?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
it[accessTokenType] = accessToken?.token?.tokenType?.value
|
||||||
|
it[accessTokenScopes] = accessToken?.token?.scopes?.joinToString(",")?.takeIf { it.isEmpty() }
|
||||||
|
it[refreshTokenValue] = refreshToken?.token?.tokenValue
|
||||||
|
it[refreshTokenIssuedAt] = refreshToken?.token?.issuedAt
|
||||||
|
it[refreshTokenExpiresAt] = refreshToken?.token?.expiresAt
|
||||||
|
it[refreshTokenMetadata] = refreshToken?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
it[oidcIdTokenValue] = oidcIdToken?.token?.tokenValue
|
||||||
|
it[oidcIdTokenIssuedAt] = oidcIdToken?.token?.issuedAt
|
||||||
|
it[oidcIdTokenExpiresAt] = oidcIdToken?.token?.expiresAt
|
||||||
|
it[oidcIdTokenMetadata] = oidcIdToken?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
it[userCodeValue] = userCode?.token?.tokenValue
|
||||||
|
it[userCodeIssuedAt] = userCode?.token?.issuedAt
|
||||||
|
it[userCodeExpiresAt] = userCode?.token?.expiresAt
|
||||||
|
it[userCodeMetadata] = userCode?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
it[deviceCodeValue] = deviceCode?.token?.tokenValue
|
||||||
|
it[deviceCodeIssuedAt] = deviceCode?.token?.issuedAt
|
||||||
|
it[deviceCodeExpiresAt] = deviceCode?.token?.expiresAt
|
||||||
|
it[deviceCodeMetadata] = deviceCode?.metadata?.let { it1 -> JsonUtil.mapToJson(it1) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(authorization: OAuth2Authorization?) {
|
||||||
|
if (authorization == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Authorization.deleteWhere { Authorization.id eq authorization.id }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findById(id: String?): OAuth2Authorization? {
|
||||||
|
if (id == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return Authorization.select { Authorization.id eq id }.singleOrNull()?.toAuthorization()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findByToken(token: String?, tokenType: OAuth2TokenType?): OAuth2Authorization? {
|
||||||
|
requireNotNull(token)
|
||||||
|
return when (tokenType?.value) {
|
||||||
|
null -> {
|
||||||
|
Authorization.select {
|
||||||
|
Authorization.authorizationCodeValue eq token
|
||||||
|
}.orWhere {
|
||||||
|
Authorization.accessTokenValue eq token
|
||||||
|
}.orWhere {
|
||||||
|
Authorization.oidcIdTokenValue eq token
|
||||||
|
}.orWhere {
|
||||||
|
Authorization.refreshTokenValue eq token
|
||||||
|
}.orWhere {
|
||||||
|
Authorization.userCodeValue eq token
|
||||||
|
}.orWhere {
|
||||||
|
Authorization.deviceCodeValue eq token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2ParameterNames.STATE -> {
|
||||||
|
Authorization.select { Authorization.state eq token }
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2ParameterNames.CODE -> {
|
||||||
|
Authorization.select { Authorization.authorizationCodeValue eq token }
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2ParameterNames.ACCESS_TOKEN -> {
|
||||||
|
Authorization.select { Authorization.accessTokenValue eq token }
|
||||||
|
}
|
||||||
|
|
||||||
|
OidcParameterNames.ID_TOKEN -> {
|
||||||
|
Authorization.select { Authorization.oidcIdTokenValue eq token }
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2ParameterNames.REFRESH_TOKEN -> {
|
||||||
|
Authorization.select { Authorization.refreshTokenValue eq token }
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2ParameterNames.USER_CODE -> {
|
||||||
|
Authorization.select { Authorization.userCodeValue eq token }
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2ParameterNames.DEVICE_CODE -> {
|
||||||
|
Authorization.select { Authorization.deviceCodeValue eq token }
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}?.singleOrNull()?.toAuthorization()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ResultRow.toAuthorization(): OAuth2Authorization {
|
||||||
|
|
||||||
|
val registeredClientId = this[Authorization.registeredClientId]
|
||||||
|
|
||||||
|
val registeredClient = registeredClientRepository.findById(registeredClientId)
|
||||||
|
|
||||||
|
val builder = OAuth2Authorization.withRegisteredClient(registeredClient)
|
||||||
|
val id = this[Authorization.id]
|
||||||
|
val principalName = this[Authorization.principalName]
|
||||||
|
val authorizationGrantType = this[Authorization.authorizationGrantType]
|
||||||
|
val authorizedScopes = this[Authorization.authorizedScopes]?.split(",").orEmpty().toSet()
|
||||||
|
val attributes = this[Authorization.attributes]?.let { JsonUtil.jsonToMap<String, Any>(it) } ?: emptyMap()
|
||||||
|
|
||||||
|
builder.id(id).principalName(principalName)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType(authorizationGrantType)).authorizedScopes(authorizedScopes)
|
||||||
|
.attributes { it.putAll(attributes) }
|
||||||
|
|
||||||
|
val state = this[Authorization.state].orEmpty()
|
||||||
|
if (state.isNotBlank()) {
|
||||||
|
builder.attribute(OAuth2ParameterNames.STATE, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
val authorizationCodeValue = this[Authorization.authorizationCodeValue]
|
||||||
|
if (authorizationCodeValue.isNullOrBlank()) {
|
||||||
|
val authorizationCodeIssuedAt = this[Authorization.authorizationCodeIssuedAt]
|
||||||
|
val authorizationCodeExpiresAt = this[Authorization.authorizationCodeExpiresAt]
|
||||||
|
val authorizationCodeMetadata = this[Authorization.authorizationCodeMetadata]?.let {
|
||||||
|
JsonUtil.jsonToMap<String, Any>(
|
||||||
|
it
|
||||||
|
)
|
||||||
|
} ?: emptyMap()
|
||||||
|
val oAuth2AuthorizationCode =
|
||||||
|
OAuth2AuthorizationCode(authorizationCodeValue, authorizationCodeIssuedAt, authorizationCodeExpiresAt)
|
||||||
|
builder.token(oAuth2AuthorizationCode) {
|
||||||
|
it.putAll(authorizationCodeMetadata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val accessTokenValue = this[Authorization.accessTokenValue].orEmpty()
|
||||||
|
if (accessTokenValue.isNotBlank()) {
|
||||||
|
val accessTokenIssuedAt = this[Authorization.accessTokenIssuedAt]
|
||||||
|
val accessTokenExpiresAt = this[Authorization.accessTokenExpiresAt]
|
||||||
|
val accessTokenMetadata =
|
||||||
|
this[Authorization.accessTokenMetadata]?.let { JsonUtil.jsonToMap<String, Any>(it) } ?: emptyMap()
|
||||||
|
val accessTokenType =
|
||||||
|
if (this[Authorization.accessTokenType].equals(OAuth2AccessToken.TokenType.BEARER.value, true)) {
|
||||||
|
OAuth2AccessToken.TokenType.BEARER
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val accessTokenScope = this[Authorization.accessTokenScopes]?.split(",").orEmpty().toSet()
|
||||||
|
|
||||||
|
val oAuth2AccessToken = OAuth2AccessToken(
|
||||||
|
accessTokenType, accessTokenValue, accessTokenIssuedAt, accessTokenExpiresAt, accessTokenScope
|
||||||
|
)
|
||||||
|
|
||||||
|
builder.token(oAuth2AccessToken) { it.putAll(accessTokenMetadata) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val oidcIdTokenValue = this[Authorization.oidcIdTokenValue].orEmpty()
|
||||||
|
if (oidcIdTokenValue.isNotBlank()) {
|
||||||
|
val oidcTokenIssuedAt = this[Authorization.oidcIdTokenIssuedAt]
|
||||||
|
val oidcTokenExpiresAt = this[Authorization.oidcIdTokenExpiresAt]
|
||||||
|
val oidcTokenMetadata =
|
||||||
|
this[Authorization.oidcIdTokenMetadata]?.let { JsonUtil.jsonToMap<String, Any>(it) } ?: emptyMap()
|
||||||
|
|
||||||
|
val oidcIdToken = OidcIdToken(
|
||||||
|
oidcIdTokenValue,
|
||||||
|
oidcTokenIssuedAt,
|
||||||
|
oidcTokenExpiresAt,
|
||||||
|
oidcTokenMetadata.getValue(OAuth2Authorization.Token.CLAIMS_METADATA_NAME) as MutableMap<String, Any>?
|
||||||
|
)
|
||||||
|
|
||||||
|
builder.token(oidcIdToken) { it.putAll(oidcTokenMetadata) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val refreshTokenValue = this[Authorization.refreshTokenValue].orEmpty()
|
||||||
|
if (refreshTokenValue.isNotBlank()) {
|
||||||
|
val refreshTokenIssuedAt = this[Authorization.refreshTokenIssuedAt]
|
||||||
|
val refreshTokenExpiresAt = this[Authorization.refreshTokenExpiresAt]
|
||||||
|
val refreshTokenMetadata =
|
||||||
|
this[Authorization.refreshTokenMetadata]?.let { JsonUtil.jsonToMap<String, Any>(it) } ?: emptyMap()
|
||||||
|
|
||||||
|
val oAuth2RefreshToken = OAuth2RefreshToken(refreshTokenValue, refreshTokenIssuedAt, refreshTokenExpiresAt)
|
||||||
|
|
||||||
|
builder.token(oAuth2RefreshToken) { it.putAll(refreshTokenMetadata) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val userCodeValue = this[Authorization.userCodeValue].orEmpty()
|
||||||
|
if (userCodeValue.isNotBlank()) {
|
||||||
|
val userCodeIssuedAt = this[Authorization.userCodeIssuedAt]
|
||||||
|
val userCodeExpiresAt = this[Authorization.userCodeExpiresAt]
|
||||||
|
val userCodeMetadata =
|
||||||
|
this[Authorization.userCodeMetadata]?.let { JsonUtil.jsonToMap<String, Any>(it) } ?: emptyMap()
|
||||||
|
val oAuth2UserCode = OAuth2UserCode(userCodeValue, userCodeIssuedAt, userCodeExpiresAt)
|
||||||
|
builder.token(oAuth2UserCode) { it.putAll(userCodeMetadata) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val deviceCodeValue = this[Authorization.deviceCodeValue].orEmpty()
|
||||||
|
if (deviceCodeValue.isNotBlank()) {
|
||||||
|
val deviceCodeIssuedAt = this[Authorization.deviceCodeIssuedAt]
|
||||||
|
val deviceCodeExpiresAt = this[Authorization.deviceCodeExpiresAt]
|
||||||
|
val deviceCodeMetadata =
|
||||||
|
this[Authorization.deviceCodeMetadata]?.let { JsonUtil.jsonToMap<String, Any>(it) } ?: emptyMap()
|
||||||
|
|
||||||
|
val oAuth2DeviceCode = OAuth2DeviceCode(deviceCodeValue, deviceCodeIssuedAt, deviceCodeExpiresAt)
|
||||||
|
builder.token(oAuth2DeviceCode) { it.putAll(deviceCodeMetadata) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Authorization : Table("authorization") {
|
||||||
|
val id = varchar("id", 255)
|
||||||
|
val registeredClientId = varchar("registered_client_id", 255)
|
||||||
|
val principalName = varchar("principal_name", 255)
|
||||||
|
val authorizationGrantType = varchar("authorization_grant_type", 255)
|
||||||
|
val authorizedScopes = varchar("authorized_scopes", 1000).nullable().default(null)
|
||||||
|
val attributes = varchar("attributes", 4000).nullable().default(null)
|
||||||
|
val state = varchar("state", 500).nullable().default(null)
|
||||||
|
val authorizationCodeValue = varchar("authorization_code_value", 4000).nullable().default(null)
|
||||||
|
val authorizationCodeIssuedAt = timestamp("authorization_code_issued_at").nullable().default(null)
|
||||||
|
val authorizationCodeExpiresAt = timestamp("authorization_code_expires_at").nullable().default(null)
|
||||||
|
val authorizationCodeMetadata = varchar("authorization_code_metadata", 2000).nullable().default(null)
|
||||||
|
val accessTokenValue = varchar("access_token_value", 4000).nullable().default(null)
|
||||||
|
val accessTokenIssuedAt = timestamp("access_token_issued_at").nullable().default(null)
|
||||||
|
val accessTokenExpiresAt = timestamp("access_token_expires_at").nullable().default(null)
|
||||||
|
val accessTokenMetadata = varchar("access_token_metadata", 2000).nullable().default(null)
|
||||||
|
val accessTokenType = varchar("access_token_type", 255).nullable().default(null)
|
||||||
|
val accessTokenScopes = varchar("access_token_scopes", 1000).nullable().default(null)
|
||||||
|
val refreshTokenValue = varchar("refresh_token_value", 4000).nullable().default(null)
|
||||||
|
val refreshTokenIssuedAt = timestamp("refresh_token_issued_at").nullable().default(null)
|
||||||
|
val refreshTokenExpiresAt = timestamp("refresh_token_expires_at").nullable().default(null)
|
||||||
|
val refreshTokenMetadata = varchar("refresh_token_metadata", 2000).nullable().default(null)
|
||||||
|
val oidcIdTokenValue = varchar("oidc_id_token_value", 4000).nullable().default(null)
|
||||||
|
val oidcIdTokenIssuedAt = timestamp("oidc_id_token_issued_at").nullable().default(null)
|
||||||
|
val oidcIdTokenExpiresAt = timestamp("oidc_id_token_expires_at").nullable().default(null)
|
||||||
|
val oidcIdTokenMetadata = varchar("oidc_id_token_metadata", 2000).nullable().default(null)
|
||||||
|
val oidcIdTokenClaims = varchar("oidc_id_token_claims", 2000).nullable().default(null)
|
||||||
|
val userCodeValue = varchar("user_code_value", 4000).nullable().default(null)
|
||||||
|
val userCodeIssuedAt = timestamp("user_code_issued_at").nullable().default(null)
|
||||||
|
val userCodeExpiresAt = timestamp("user_code_expires_at").nullable().default(null)
|
||||||
|
val userCodeMetadata = varchar("user_code_metadata", 2000).nullable().default(null)
|
||||||
|
val deviceCodeValue = varchar("device_code_value", 4000).nullable().default(null)
|
||||||
|
val deviceCodeIssuedAt = timestamp("device_code_issued_at").nullable().default(null)
|
||||||
|
val deviceCodeExpiresAt = timestamp("device_code_expires_at").nullable().default(null)
|
||||||
|
val deviceCodeMetadata = varchar("device_code_metadata", 2000).nullable().default(null)
|
||||||
|
|
||||||
|
override val primaryKey = PrimaryKey(id)
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package dev.usbharu.hideout.service.auth
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.query.UserQueryService
|
||||||
|
import dev.usbharu.hideout.service.core.Transaction
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.springframework.security.core.userdetails.User
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class UserDetailsServiceImpl(private val userQueryService: UserQueryService, private val transaction: Transaction) :
|
||||||
|
UserDetailsService {
|
||||||
|
override fun loadUserByUsername(username: String?): UserDetails = runBlocking {
|
||||||
|
if (username == null) {
|
||||||
|
throw UsernameNotFoundException("$username not found")
|
||||||
|
}
|
||||||
|
transaction.transaction {
|
||||||
|
val findById = userQueryService.findByNameAndDomain(username, "")
|
||||||
|
User(
|
||||||
|
findById.name, findById.password, listOf()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package dev.usbharu.hideout.service.auth
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
|
||||||
|
|
||||||
|
|
||||||
|
class UsernamePasswordAuthFilter(jwtService: JwtService, authenticationManager: AuthenticationManager?) :
|
||||||
|
UsernamePasswordAuthenticationFilter(authenticationManager) {
|
||||||
|
init {
|
||||||
|
setRequiresAuthenticationRequestMatcher(AntPathRequestMatcher("/api/internal/v1/login", "POST"))
|
||||||
|
|
||||||
|
this.setAuthenticationSuccessHandler { request, response, authentication ->
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package dev.usbharu.hideout.util
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
|
||||||
|
object JsonUtil {
|
||||||
|
val objectMapper = jacksonObjectMapper()
|
||||||
|
|
||||||
|
fun mapToJson(map: Map<*, *>, objectMapper: ObjectMapper = this.objectMapper): String =
|
||||||
|
objectMapper.writeValueAsString(map)
|
||||||
|
|
||||||
|
fun <K, V> jsonToMap(json: String, objectMapper: ObjectMapper = this.objectMapper): Map<K, V> =
|
||||||
|
objectMapper.readValue(json)
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue