Skip to content

Commit

Permalink
fix: client-credential模式每次认证都会覆盖之前的token #2715
Browse files Browse the repository at this point in the history
* fix: client-credential模式每次认证都会覆盖之前的token #2715

* fix: client-credential模式每次认证都会覆盖之前的token #2715
  • Loading branch information
yaoxuwan authored Oct 31, 2024
1 parent cdac896 commit 8bcd65c
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import org.springframework.stereotype.Repository

@Repository
interface OauthTokenRepository : MongoRepository<TOauthToken, String> {
fun findFirstByAccountIdAndUserId(accountId: String, userId: String): TOauthToken?
fun findFirstByAccessToken(accessToken: String): TOauthToken?
fun findByUserId(userId: String): List<TOauthToken>
fun findFirstByAccountIdAndRefreshToken(accountId: String, refreshToken: String): TOauthToken?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,28 @@

package com.tencent.bkrepo.auth.model

import com.tencent.bkrepo.auth.model.TOauthToken.Companion.ACCESS_TOKEN_IDX
import com.tencent.bkrepo.auth.model.TOauthToken.Companion.ACCESS_TOKEN_IDX_DEF
import com.tencent.bkrepo.auth.model.TOauthToken.Companion.ACCOUNT_ID_ACCESS_TOKEN_IDX
import com.tencent.bkrepo.auth.model.TOauthToken.Companion.ACCOUNT_ID_ACCESS_TOKEN_IDX_DEF
import com.tencent.bkrepo.auth.model.TOauthToken.Companion.ACCOUNT_ID_USER_ID_IDX
import com.tencent.bkrepo.auth.model.TOauthToken.Companion.ACCOUNT_ID_USER_ID_IDX_DEF
import com.tencent.bkrepo.auth.model.TOauthToken.Companion.USER_IDX
import com.tencent.bkrepo.auth.model.TOauthToken.Companion.USER_IDX_DEF
import com.tencent.bkrepo.auth.pojo.enums.ResourceType
import com.tencent.bkrepo.auth.pojo.oauth.IdToken
import org.springframework.data.mongodb.core.index.CompoundIndex
import org.springframework.data.mongodb.core.index.CompoundIndexes
import org.springframework.data.mongodb.core.mapping.Document
import java.time.Instant

@Document("oauth_token")
data class TOauthToken(
@CompoundIndexes(
CompoundIndex(name = ACCESS_TOKEN_IDX, def = ACCESS_TOKEN_IDX_DEF, background = true),
CompoundIndex(name = USER_IDX, def = USER_IDX_DEF, background = true),
CompoundIndex(name = ACCOUNT_ID_ACCESS_TOKEN_IDX, def = ACCOUNT_ID_ACCESS_TOKEN_IDX_DEF, background = true),
CompoundIndex(name = ACCOUNT_ID_USER_ID_IDX, def = ACCOUNT_ID_USER_ID_IDX_DEF, background = true),
)data class TOauthToken(
val id: String? = null,
var accessToken: String,
var refreshToken: String?,
Expand All @@ -44,4 +59,15 @@ data class TOauthToken(
var scope: Set<ResourceType>?,
var issuedAt: Instant,
var idToken: IdToken?
)
) {
companion object {
const val ACCESS_TOKEN_IDX = "access_token"
const val ACCESS_TOKEN_IDX_DEF = "{'accessToken': 1}"
const val USER_IDX = "user_id"
const val USER_IDX_DEF = "{'userId': 1}"
const val ACCOUNT_ID_ACCESS_TOKEN_IDX = "account_id_access_token"
const val ACCOUNT_ID_ACCESS_TOKEN_IDX_DEF = "{'accountId': 1, 'access_token': 1}"
const val ACCOUNT_ID_USER_ID_IDX = "account_id_user_id"
const val ACCOUNT_ID_USER_ID_IDX_DEF = "{'accountId': 1, 'userId': 1}"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -199,29 +199,19 @@ class OauthAuthorizationServiceImpl(
client: TAccount,
openId: Boolean
): TOauthToken {
var tOauthToken = oauthTokenRepository.findFirstByAccountIdAndUserId(client.id!!, userId)
val idToken = generateOpenIdToken(client.id, userId, nonce)
if (tOauthToken == null) {
tOauthToken = TOauthToken(
accessToken = idToken.toJwtToken(),
refreshToken = OauthUtils.generateRefreshToken(),
expireSeconds = oauthProperties.expiredDuration.seconds,
type = "Bearer",
accountId = client.id,
userId = userId,
scope = client.scope,
issuedAt = Instant.now(Clock.systemDefaultZone()),
idToken = if (openId) idToken else null
)
}
if (client.scope != tOauthToken.scope) {
tOauthToken.scope = client.scope!!
}
tOauthToken.userId = userId
tOauthToken.accessToken = idToken.toJwtToken()
tOauthToken.idToken = if (openId) idToken else null
tOauthToken.issuedAt = Instant.now(Clock.systemDefaultZone())
oauthTokenRepository.save(tOauthToken)
val idToken = generateOpenIdToken(client.id!!, userId, nonce)
val tOauthToken = TOauthToken(
accessToken = idToken.toJwtToken(),
refreshToken = OauthUtils.generateRefreshToken(),
expireSeconds = oauthProperties.expiredDuration.seconds,
type = "Bearer",
accountId = client.id,
userId = userId,
scope = client.scope,
issuedAt = Instant.now(Clock.systemDefaultZone()),
idToken = if (openId) idToken else null,
)
oauthTokenRepository.insert(tOauthToken)
return tOauthToken
}

Expand Down Expand Up @@ -261,17 +251,11 @@ class OauthAuthorizationServiceImpl(
}

override fun validateToken(accessToken: String): String? {
val token = oauthTokenRepository.findFirstByAccessToken(accessToken)
?: throw ErrorCodeException(CommonMessageCode.RESOURCE_NOT_FOUND, "access_token[$accessToken]")
if (token.expireSeconds == null) {
return token.userId
}

val expiredInstant = Instant.ofEpochSecond(token.issuedAt.epochSecond + token.expireSeconds)
if (expiredInstant.isBefore(Instant.now())) {
throw ErrorCodeException(CommonMessageCode.RESOURCE_EXPIRED, "access_token[$accessToken]")
}
return token.userId
val claims = JwtUtils.validateToken(
signingKey = RsaUtils.stringToPrivateKey(cryptoProperties.privateKeyStr2048PKCS8),
token = accessToken
)
return claims.body.subject
}

override fun deleteToken(clientId: String, clientSecret: String, accessToken: String) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.tencent.bkrepo.job.batch.task.clean

import com.tencent.bkrepo.common.metadata.constant.ID
import com.tencent.bkrepo.job.batch.base.DefaultContextMongoDbJob
import com.tencent.bkrepo.job.batch.base.JobContext
import com.tencent.bkrepo.job.batch.utils.TimeUtils
import com.tencent.bkrepo.job.config.properties.OauthTokenCleanupJobProperties
import org.slf4j.LoggerFactory
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query
import org.springframework.data.mongodb.core.query.isEqualTo
import org.springframework.data.mongodb.core.query.where
import org.springframework.stereotype.Component
import java.time.Duration
import java.time.Instant
import java.time.ZoneId
import kotlin.reflect.KClass


@Component
@EnableConfigurationProperties(OauthTokenCleanupJobProperties::class)
class OauthTokenCleanupJob(
private val properties: OauthTokenCleanupJobProperties
) : DefaultContextMongoDbJob<OauthTokenCleanupJob.OauthToken>(properties) {
override fun collectionNames(): List<String> {
return listOf(COLLECTION_NAME)
}

override fun buildQuery(): Query {
return Query(where(OauthToken::expireSeconds).gt(0))
}

override fun mapToEntity(row: Map<String, Any?>): OauthToken {
return OauthToken(row)
}

override fun entityClass(): KClass<OauthToken> {
return OauthToken::class
}

override fun run(row: OauthToken, collectionName: String, context: JobContext) {
val accessTokenExpireTime = row.issuedAt.plusSeconds(row.expireSeconds!!)
val reservedTime = accessTokenExpireTime.plusSeconds(properties.reservedDuration.seconds)
if (Instant.now().isAfter(reservedTime)) {
val query = Query(Criteria.where(ID).isEqualTo(row.id))
mongoTemplate.remove(query, COLLECTION_NAME)
context.success.incrementAndGet()
logger.info("clean up oauth token: ${row.id}, ${row.issuedAt.epochSecond}, ${row.expireSeconds}")
}
context.total.incrementAndGet()
}

override fun getLockAtMostFor(): Duration {
return Duration.ofDays(7)
}

data class OauthToken(
var id: String,
var issuedAt: Instant,
val expireSeconds: Long?,
) {
constructor(map: Map<String, Any?>) : this(
map[OauthToken::id.name].toString(),
TimeUtils.parseMongoDateTimeStr(map[OauthToken::issuedAt.name].toString())
?.atZone(ZoneId.systemDefault())
?.toInstant() ?: Instant.now(),
map[OauthToken::expireSeconds.name]?.toString()?.toLong(),
)
}

companion object {
private const val COLLECTION_NAME = "oauth_token"
private val logger = LoggerFactory.getLogger(OauthTokenCleanupJob::class.java)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.tencent.bkrepo.job.config.properties

import org.springframework.boot.context.properties.ConfigurationProperties
import java.time.Duration

@ConfigurationProperties(value = "job.oauth-token-clean-up")
class OauthTokenCleanupJobProperties(
override var enabled: Boolean = true,
override var cron: String = "0 0 10 1/3 * ?",
var reservedDuration: Duration = Duration.ofDays(7),
) : MongodbJobProperties()

0 comments on commit 8bcd65c

Please sign in to comment.