From 3409d1b05f095c639a61a98e4d3ad0bfbd9b2a2a Mon Sep 17 00:00:00 2001 From: yaoxuwan Date: Wed, 4 Dec 2024 17:49:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:Oauth=20token=E5=88=B7=E6=96=B0=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E5=8E=9Ftoken=20#2822?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth/OauthAuthorizationServiceImpl.kt | 27 ++++++++------- .../http/HttpAuthSecurityConfiguration.kt | 2 +- .../security/http/oauth/OauthAuthHandler.kt | 34 ++++++++++++++----- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/oauth/OauthAuthorizationServiceImpl.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/oauth/OauthAuthorizationServiceImpl.kt index 4df366536d..f51ce3bb52 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/oauth/OauthAuthorizationServiceImpl.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/oauth/OauthAuthorizationServiceImpl.kt @@ -43,6 +43,7 @@ import com.tencent.bkrepo.auth.pojo.oauth.JsonWebKey import com.tencent.bkrepo.auth.pojo.oauth.JsonWebKeySet import com.tencent.bkrepo.auth.pojo.oauth.OauthToken import com.tencent.bkrepo.auth.dao.repository.OauthTokenRepository +import com.tencent.bkrepo.auth.pojo.enums.ResourceType import com.tencent.bkrepo.auth.pojo.oauth.OidcConfiguration import com.tencent.bkrepo.auth.pojo.oauth.UserInfo import com.tencent.bkrepo.auth.service.OauthAuthorizationService @@ -146,17 +147,16 @@ class OauthAuthorizationServiceImpl( with(generateTokenRequest) { Preconditions.checkNotNull(clientId, this::clientId.name) Preconditions.checkNotNull(refreshToken, this::refreshToken.name) - val token = oauthTokenRepository.findFirstByAccountIdAndRefreshToken(clientId!!, refreshToken!!) + var token = oauthTokenRepository.findFirstByAccountIdAndRefreshToken(clientId!!, refreshToken!!) ?: throw ErrorCodeException(CommonMessageCode.RESOURCE_NOT_FOUND, refreshToken!!) - val idToken = generateOpenIdToken( - clientId = clientId!!, + val client = accountDao.findById(clientId!!) + ?: throw ErrorCodeException(CommonMessageCode.RESOURCE_NOT_FOUND, clientId!!) + token = buildOauthToken( userId = token.userId, - nonce = OauthUtils.generateRandomString(10) + nonce = OauthUtils.generateRandomString(10), + client = client, + openId = token.idToken != null ) - token.accessToken = idToken.toJwtToken() - token.issuedAt = Instant.now(Clock.systemDefaultZone()) - token.idToken?.let { token.idToken = idToken } - oauthTokenRepository.save(token) responseToken(transfer(token)) } } @@ -201,7 +201,7 @@ class OauthAuthorizationServiceImpl( ): TOauthToken { val idToken = generateOpenIdToken(client.id!!, userId, nonce) val tOauthToken = TOauthToken( - accessToken = idToken.toJwtToken(), + accessToken = idToken.toJwtToken(client.scope), refreshToken = OauthUtils.generateRefreshToken(), expireSeconds = oauthProperties.expiredDuration.seconds, type = "Bearer", @@ -300,7 +300,7 @@ class OauthAuthorizationServiceImpl( accessToken = tOauthToken.accessToken, tokenType = tOauthToken.type, scope = if (tOauthToken.scope == null) "" else tOauthToken.scope!!.joinToString(StringPool.COMMA), - idToken = tOauthToken.idToken?.toJwtToken(), + idToken = tOauthToken.idToken?.toJwtToken(tOauthToken.scope), refreshToken = tOauthToken.refreshToken, expiresIn = if (tOauthToken.expireSeconds == null) { null @@ -310,12 +310,15 @@ class OauthAuthorizationServiceImpl( } ) - private fun IdToken.toJwtToken(): String { + private fun IdToken.toJwtToken(scope: Set?): String { + val claims = mutableMapOf() + claims["scope"] = scope.orEmpty() + claims.putAll(JsonUtils.objectMapper.convertValue(this, jacksonTypeRef())) return JwtUtils.generateToken( signingKey = RsaUtils.stringToPrivateKey(cryptoProperties.privateKeyStr2048PKCS8), expireDuration = oauthProperties.expiredDuration, subject = sub, - claims = JsonUtils.objectMapper.convertValue(this, jacksonTypeRef()), + claims = claims, header = mapOf(KEY_ID_NAME to KEY_ID_VALUE), algorithm = SignatureAlgorithm.RS256 ) diff --git a/src/backend/common/common-security/src/main/kotlin/com/tencent/bkrepo/common/security/http/HttpAuthSecurityConfiguration.kt b/src/backend/common/common-security/src/main/kotlin/com/tencent/bkrepo/common/security/http/HttpAuthSecurityConfiguration.kt index 18bafae662..dab3823234 100644 --- a/src/backend/common/common-security/src/main/kotlin/com/tencent/bkrepo/common/security/http/HttpAuthSecurityConfiguration.kt +++ b/src/backend/common/common-security/src/main/kotlin/com/tencent/bkrepo/common/security/http/HttpAuthSecurityConfiguration.kt @@ -110,7 +110,7 @@ class HttpAuthSecurityConfiguration( ) } if (httpAuthSecurity.oauthEnabled) { - httpAuthSecurity.addHttpAuthHandler(OauthAuthHandler(authenticationManager)) + httpAuthSecurity.addHttpAuthHandler(OauthAuthHandler(authenticationManager, cryptoProperties)) } if (httpAuthSecurity.temporaryTokenEnabled) { httpAuthSecurity.addHttpAuthHandler(TemporaryTokenAuthHandler(authenticationManager)) diff --git a/src/backend/common/common-security/src/main/kotlin/com/tencent/bkrepo/common/security/http/oauth/OauthAuthHandler.kt b/src/backend/common/common-security/src/main/kotlin/com/tencent/bkrepo/common/security/http/oauth/OauthAuthHandler.kt index b8de944aaa..43ea388c76 100644 --- a/src/backend/common/common-security/src/main/kotlin/com/tencent/bkrepo/common/security/http/oauth/OauthAuthHandler.kt +++ b/src/backend/common/common-security/src/main/kotlin/com/tencent/bkrepo/common/security/http/oauth/OauthAuthHandler.kt @@ -30,15 +30,21 @@ package com.tencent.bkrepo.common.security.http.oauth import com.tencent.bkrepo.common.api.constant.AUTHORITIES_KEY import com.tencent.bkrepo.common.api.constant.HttpHeaders import com.tencent.bkrepo.common.api.constant.OAUTH_AUTH_PREFIX +import com.tencent.bkrepo.common.security.crypto.CryptoProperties import com.tencent.bkrepo.common.security.exception.AuthenticationException import com.tencent.bkrepo.common.security.http.core.HttpAuthHandler import com.tencent.bkrepo.common.security.http.credentials.AnonymousCredentials import com.tencent.bkrepo.common.security.http.credentials.HttpAuthCredentials import com.tencent.bkrepo.common.security.manager.AuthenticationManager +import com.tencent.bkrepo.common.security.util.JwtUtils +import com.tencent.bkrepo.common.security.util.RsaUtils +import org.slf4j.LoggerFactory import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse -open class OauthAuthHandler(val authenticationManager: AuthenticationManager) : HttpAuthHandler { +open class OauthAuthHandler( + val authenticationManager: AuthenticationManager, + val cryptoProperties: CryptoProperties +) : HttpAuthHandler { override fun extractAuthCredentials(request: HttpServletRequest): HttpAuthCredentials { val authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION).orEmpty() return if (authorizationHeader.startsWith(OAUTH_AUTH_PREFIX)) { @@ -53,14 +59,24 @@ open class OauthAuthHandler(val authenticationManager: AuthenticationManager) : override fun onAuthenticate(request: HttpServletRequest, authCredentials: HttpAuthCredentials): String { require(authCredentials is OauthAuthCredentials) - return authenticationManager.checkOauthToken(authCredentials.token) + return try { + val claims = JwtUtils.validateToken( + signingKey = RsaUtils.stringToPrivateKey(cryptoProperties.privateKeyStr2048PKCS8), + token = authCredentials.token + ) + val scopeList = claims.body["scope"] as? List<*> + val scope = scopeList?.joinToString(",") + ?: authenticationManager.findOauthToken(authCredentials.token)?.scope + ?: throw AuthenticationException("Invalid access token: $authCredentials.token") + request.setAttribute(AUTHORITIES_KEY, scope) + claims.body.subject + } catch (e: Exception) { + logger.info("invalid oauth token[${authCredentials.token}]: ${e.message}") + throw AuthenticationException("Invalid token") + } } - override fun onAuthenticateSuccess(request: HttpServletRequest, response: HttpServletResponse, userId: String) { - val authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION).orEmpty() - val accessToken = authorizationHeader.removePrefix(OAUTH_AUTH_PREFIX).trim() - val oauthToken = authenticationManager.findOauthToken(accessToken) - ?: throw AuthenticationException("Invalid access token") - request.setAttribute(AUTHORITIES_KEY, oauthToken.scope) + companion object { + private val logger = LoggerFactory.getLogger(OauthAuthHandler::class.java) } }