DefaultOpaqueTokenIntrospector.kt
package io.github.lishangbu.avalon.oauth2.authorizationserver.introspection
import io.github.lishangbu.avalon.oauth2.common.userdetails.UserInfo
import org.slf4j.LoggerFactory
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.authority.AuthorityUtils
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.oauth2.core.AuthorizationGrantType
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector
import java.security.Principal
/**
* 默认不透明令牌内省器
*
* 根据授权记录和用户信息构建资源服务器使用的 Principal
*/
class DefaultOpaqueTokenIntrospector(
/** 授权服务 */
private val authorizationService: OAuth2AuthorizationService,
/** 用户详情服务 */
private val userDetailsService: UserDetailsService,
) : OpaqueTokenIntrospector {
/** 执行令牌内省 */
override fun introspect(token: String): OAuth2AuthenticatedPrincipal? {
val oldAuthorization: OAuth2Authorization? =
authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN)
if (oldAuthorization == null) {
throw InvalidBearerTokenException(token)
}
if (AuthorizationGrantType.CLIENT_CREDENTIALS == oldAuthorization.authorizationGrantType) {
val accessTokenClaims =
checkNotNull(oldAuthorization.accessToken?.claims) {
"Access token claims cannot be null for client credentials introspection"
}
return DefaultOAuth2AuthenticatedPrincipal(
oldAuthorization.principalName,
accessTokenClaims,
AuthorityUtils.NO_AUTHORITIES,
)
}
try {
val principal =
oldAuthorization.attributes[Principal::class.java.name]
as? UsernamePasswordAuthenticationToken
?: return null
val tokenPrincipal = principal.principal as? UserDetails ?: return null
val userDetails = userDetailsService.loadUserByUsername(tokenPrincipal.username)
if (userDetails is UserInfo) {
userDetails.attributes.putAll(oldAuthorization.accessToken?.claims.orEmpty())
return userDetails
}
} catch (notFoundException: UsernameNotFoundException) {
log.warn("用户不存在 {}", notFoundException.localizedMessage)
throw notFoundException
} catch (ex: Exception) {
log.error("资源服务器 introspect Token error {}", ex.localizedMessage)
}
return null
}
companion object {
/** 日志记录器 */
private val log = LoggerFactory.getLogger(DefaultOpaqueTokenIntrospector::class.java)
}
}