refactor(security): 重构单令牌认证体系

- 重构 SingleTokenService 为 SingleTokenValidator,优化令牌验证流程
- 新增 TokenValidator 接口,用于定义通用令牌验证逻辑
- 更新 SingleTokenSecurityCoreAutoConfiguration,适配新的验证器
- 重构 SingleTokenAuthenticationProvider,使用新的验证器进行令牌验证
- 更新过滤器和认证令牌类,以适应新的认证流程
This commit is contained in:
gewuyou 2025-06-26 16:06:05 +08:00
parent aa9bd66259
commit 21542adfcc
9 changed files with 128 additions and 67 deletions

View File

@ -1,22 +0,0 @@
package com.gewuyou.forgeboot.security.authorize.api.core.service
import com.gewuyou.forgeboot.security.core.authorize.entities.SingleTokenPrincipal
/**
* 单一令牌验证服务接口
*
* 该接口用于定义对单一令牌的验证操作通过提供的令牌字符串返回对应的主体信息
*
* @since 2025-06-25 13:10:38
* @author gewuyou
*/
fun interface SingleTokenService {
/**
* 验证给定的令牌字符串并返回对应的主体信息
*
* @param token 要验证的令牌字符串
* @return 返回与令牌关联的主体信息对象 [SingleTokenPrincipal]
*/
fun validate(token: String): SingleTokenPrincipal
}

View File

@ -0,0 +1,9 @@
package com.gewuyou.forgeboot.security.authorize.api.core.validator
/**
*单令牌验证器
*
* @since 2025-06-26 15:55:34
* @author gewuyou
*/
interface SingleTokenValidator<SingleTokenPrincipal>: TokenValidator<SingleTokenPrincipal>

View File

@ -0,0 +1,21 @@
package com.gewuyou.forgeboot.security.authorize.api.core.validator
/**
* 令牌验证器接口用于定义通用的令牌验证逻辑
*
* 该接口设计为泛型接口支持不同类型的令牌验证结果
*
* @since 2025-06-26 15:54:14
* @author gewuyou
*/
fun interface TokenValidator<T> {
/**
* 验证指定的令牌字符串并返回解析后的结果对象
*
* @param token 待验证的令牌字符串通常由客户端在请求头中提供
* @return 返回解析后的泛型对象 T可能是用户信息权限列表或其他业务相关的数据结构
* @throws IllegalArgumentException 如果令牌格式不正确或为空
* @throws SecurityException 如果令牌无效或已过期
*/
fun validate(token: String): T
}

View File

@ -1,6 +1,6 @@
package com.gewuyou.forgeboot.security.authorize.autoconfigure.core package com.gewuyou.forgeboot.security.authorize.autoconfigure.core
import com.gewuyou.forgeboot.security.authorize.api.core.service.SingleTokenService import com.gewuyou.forgeboot.security.authorize.api.core.validator.SingleTokenValidator
import com.gewuyou.forgeboot.security.authorize.impl.core.provider.SingleTokenAuthenticationProvider import com.gewuyou.forgeboot.security.authorize.impl.core.provider.SingleTokenAuthenticationProvider
import com.gewuyou.forgeboot.security.core.authorize.entities.SingleTokenPrincipal import com.gewuyou.forgeboot.security.core.authorize.entities.SingleTokenPrincipal
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
@ -16,20 +16,19 @@ import org.springframework.security.authentication.AuthenticationProvider
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
class SingleTokenSecurityCoreAutoConfiguration { class SingleTokenSecurityCoreAutoConfiguration {
/** /**
* 提供一个默认的 SingleTokenService Bean用于验证令牌并返回用户主体信息 * 提供一个默认的 SingleTokenValidator Bean用于验证单点登录令牌
* *
* 如果上下文中尚未定义此类 Bean则使用此默认实现 * 如果上下文中尚未定义此类 Bean则使用此默认实现
* 默认实现会在调用 validate 方法时抛出 UnsupportedOperationException * 默认实现会在调用 validate 方法时抛出 UnsupportedOperationException
* 提示使用者应提供自定义的 SingleTokenService 实现 * 提示使用者应提供自定义的 SingleTokenValidator 实现
* *
* @return 返回一个 SingleTokenService 接口的默认实现 * @return 返回一个 SingleTokenValidator 接口的默认实现
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
fun singleTokenService(): SingleTokenService { fun singleTokenValidator(): SingleTokenValidator<SingleTokenPrincipal> {
return object : SingleTokenService { return object : SingleTokenValidator<SingleTokenPrincipal> {
/** /**
* 验证给定的 token 并返回对应的用户主体信息 * 验证给定的 token 并返回对应的用户主体信息
* *
@ -43,18 +42,21 @@ class SingleTokenSecurityCoreAutoConfiguration {
} }
} }
/** /**
* 注册 SingleTokenAuthenticationProvider Bean用于 Spring Security 的认证流程 * 注册 SingleTokenAuthenticationProvider Bean用于 Spring Security 的认证流程
* *
* 该认证提供者依赖于 SingleTokenService 来完成实际的令牌验证工作 * 该方法创建并返回一个 SingleTokenAuthenticationProvider 实例
* 如果上下文中尚未定义同名 Bean则注册该 Bean * 用于在 Spring Security 框架中处理基于单点令牌的认证逻辑
* 如果上下文中尚未定义同名 Bean则进行注册
* *
* @param singleTokenService 用于令牌验证的服务实例 * @param singleTokenValidator 提供的 SingleTokenValidator 实例
* @return 返回配置好的 SingleTokenAuthenticationProvider 实例 * 用于执行具体的令牌验证逻辑
* @return 返回配置好的 AuthenticationProvider 实现类实例
*/ */
@Bean("singleTokenAuthenticationProvider") @Bean("singleTokenAuthenticationProvider")
@ConditionalOnMissingBean @ConditionalOnMissingBean
fun singleTokenAuthenticationProvider(singleTokenService: SingleTokenService): AuthenticationProvider { fun singleTokenAuthenticationProvider(singleTokenValidator: SingleTokenValidator<SingleTokenPrincipal>): AuthenticationProvider {
return SingleTokenAuthenticationProvider(singleTokenService) return SingleTokenAuthenticationProvider(singleTokenValidator)
} }
} }

View File

@ -1,6 +1,7 @@
package com.gewuyou.forgeboot.security.authorize.impl.core.provider package com.gewuyou.forgeboot.security.authorize.impl.core.provider
import com.gewuyou.forgeboot.security.authorize.api.core.service.SingleTokenService import com.gewuyou.forgeboot.security.authorize.api.core.validator.SingleTokenValidator
import com.gewuyou.forgeboot.security.core.authorize.entities.SingleTokenPrincipal
import com.gewuyou.forgeboot.security.core.common.token.SingleTokenAuthenticationToken import com.gewuyou.forgeboot.security.core.common.token.SingleTokenAuthenticationToken
import org.springframework.security.authentication.AuthenticationProvider import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.core.Authentication import org.springframework.security.core.Authentication
@ -10,34 +11,37 @@ import org.springframework.security.core.Authentication
* *
* 用于处理基于 SingleToken 的身份验证流程 * 用于处理基于 SingleToken 的身份验证流程
* *
* @property singleTokenService 用于执行 Token 校验的服务组件 * @property singleTokenValidator 用于执行 Token 校验的服务组件
* @author gewuyou * @author gewuyou
* @since 2025-06-25 13:09:43 * @since 2025-06-25 13:09:43
*/ */
class SingleTokenAuthenticationProvider( class SingleTokenAuthenticationProvider(
private val singleTokenService: SingleTokenService private val singleTokenValidator: SingleTokenValidator<SingleTokenPrincipal>
) : AuthenticationProvider { ) : AuthenticationProvider {
/** /**
* 执行身份验证操作 * 执行身份验证操作
* *
* 将传入的身份验证对象转换为 SingleTokenAuthenticationToken * 将传入的身份验证对象转换为 SingleTokenAuthenticationToken 类型
* 然后通过 singleTokenService 验证 Token 的有效性并返回认证后的 Authentication 对象 * 然后通过 singleTokenService 验证 Token 的有效性并返回认证后的 Authentication 对象
* *
* @param authentication 需要被验证的 Authentication 实例 * 该方法的主要流程包括
* @return 返回已认证的 Authentication 对象 * 1. 强制类型转换输入的 Authentication 对象为 SingleTokenAuthenticationToken
* 2. 使用 Token principal 值调用 singleTokenService 进行 Token 校验
* 3. 构建并返回已认证的 Authentication 实例
*
* @param authentication 需要被验证的 Authentication 实例必须是 SingleTokenAuthenticationToken 类型
* @return 返回一个已认证的 Authentication 对象表示身份验证成功的结果
*/ */
override fun authenticate(authentication: Authentication): Authentication { override fun authenticate(authentication: Authentication): Authentication {
// 转换 Authentication 为 SingleTokenAuthenticationToken 类型
val token = authentication as SingleTokenAuthenticationToken val token = authentication as SingleTokenAuthenticationToken
val tokenInfo = singleTokenService.validate(token.singleToken)
return SingleTokenAuthenticationToken( // 使用 Token 的 principal 值进行校验,获取 Token 信息
token.singleToken, val tokenInfo = singleTokenValidator.validate(token.principal.toString())
tokenInfo.principal,
tokenInfo.authorities // 创建已认证的 Authentication 实例并返回
).apply { return SingleTokenAuthenticationToken.authenticated(tokenInfo, tokenInfo.authorities)
isAuthenticated = true
}
} }
/** /**
@ -49,6 +53,7 @@ class SingleTokenAuthenticationProvider(
* @return 如果支持该类型则返回 true否则返回 false * @return 如果支持该类型则返回 true否则返回 false
*/ */
override fun supports(authentication: Class<*>): Boolean { override fun supports(authentication: Class<*>): Boolean {
// 检查传入的身份验证类是否是 SingleTokenAuthenticationToken 或其子类
return SingleTokenAuthenticationToken::class.java.isAssignableFrom(authentication) return SingleTokenAuthenticationToken::class.java.isAssignableFrom(authentication)
} }
} }

View File

@ -40,7 +40,7 @@ class SingleTokenAuthenticationFilter() : OncePerRequestFilter() {
if (header?.startsWith(SecurityConstants.BEARER_PREFIX) == true) { if (header?.startsWith(SecurityConstants.BEARER_PREFIX) == true) {
val token = header.removePrefix(SecurityConstants.BEARER_PREFIX).trim() val token = header.removePrefix(SecurityConstants.BEARER_PREFIX).trim()
// 构造未认证的 token 放入上下文 // 构造未认证的 token 放入上下文
val authentication = SingleTokenAuthenticationToken(token, null) val authentication = SingleTokenAuthenticationToken.unauthenticated(token)
SecurityContextHolder.getContext().authentication = authentication SecurityContextHolder.getContext().authentication = authentication
} }
chain.doFilter(request, response) chain.doFilter(request, response)

View File

@ -56,8 +56,8 @@ class ReactiveSingleTokenAuthenticationFilter(
override fun convert(exchange: ServerWebExchange): Mono<Authentication>? { override fun convert(exchange: ServerWebExchange): Mono<Authentication>? {
val headerValue = exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION) val headerValue = exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION)
return if (headerValue?.startsWith(SecurityConstants.BEARER_PREFIX, ignoreCase = true) == true) { return if (headerValue?.startsWith(SecurityConstants.BEARER_PREFIX, ignoreCase = true) == true) {
val apiKey = headerValue.removePrefix(SecurityConstants.BEARER_PREFIX).trim() val singleToken = headerValue.removePrefix(SecurityConstants.BEARER_PREFIX).trim()
Mono.just(SingleTokenAuthenticationToken(apiKey, null)) Mono.just(SingleTokenAuthenticationToken(singleToken, null))
} else { } else {
Mono.empty() Mono.empty()
} }

View File

@ -14,6 +14,11 @@ object SecurityConstants {
*/ */
const val AUTHORIZATION_HEADER = "Authorization" const val AUTHORIZATION_HEADER = "Authorization"
/**
* HTTP请求头中用于携带刷新令牌的字段名称
*/
const val REFRESH_TOKEN_HEADER="X-Refresh-Token"
/** /**
* Bearer Token前缀用于在请求头中标识Token类型 * Bearer Token前缀用于在请求头中标识Token类型
*/ */

View File

@ -7,37 +7,78 @@ import org.springframework.security.core.GrantedAuthority
* 用于单令牌认证的认证令牌实现类该类扩展了 Spring Security AbstractAuthenticationToken * 用于单令牌认证的认证令牌实现类该类扩展了 Spring Security AbstractAuthenticationToken
* 用于表示基于单一令牌 API Key的认证请求 * 用于表示基于单一令牌 API Key的认证请求
* *
* @param singleToken 存储认证凭据如API密钥或令牌不可为 null * @param principal 表示经过认证的主体可以是用户对象或其他形式的身份标识不可为 null
* @param principal 表示经过认证的主体可以是用户对象或其他形式的身份标识
* @param authorities 用户所拥有的权限集合默认为空列表 * @param authorities 用户所拥有的权限集合默认为空列表
* *
* @author gewuyou * @author gewuyou
* @since 2025-06-25 13:06:54 * @since 2025-06-25 13:06:54
*/ */
class SingleTokenAuthenticationToken( class SingleTokenAuthenticationToken(
val singleToken: String, private val principal: Any,
private val principal: Any?, authorities: Collection<GrantedAuthority>? = null,
authorities: Collection<GrantedAuthority> = listOf()
) : AbstractAuthenticationToken(authorities) { ) : AbstractAuthenticationToken(authorities) {
/** /**
* 获取认证凭据 * 初始化方法根据是否提供权限信息设置认证状态
* * 如果权限信息为 null则认证状态设为未认证否则设为已认证
* @return 返回存储在 [singleToken] 中的认证凭据
*/ */
override fun getCredentials(): Any = singleToken init {
if (authorities == null) {
super.setAuthenticated(false)
} else {
super.setAuthenticated(true)
}
}
companion object {
/**
* 创建一个未认证的 SingleTokenAuthenticationToken 实例
*
* @param token 认证凭据 API 密钥或令牌不可为 null
* @return 返回未认证的 SingleTokenAuthenticationToken 实例
*/
@JvmStatic
fun unauthenticated(token: String): SingleTokenAuthenticationToken {
return SingleTokenAuthenticationToken(token)
}
/** /**
* 获取认证主体 * 创建一个已认证的 SingleTokenAuthenticationToken 实例
* *
* @return 返回认证主体对象可能为 null * @param principal 表示经过认证的主体不可为 null
* @param authorities 用户所拥有的权限集合不可为 null
* @return 返回已认证的 SingleTokenAuthenticationToken 实例
*/ */
override fun getPrincipal(): Any? = principal @JvmStatic
fun authenticated(
principal: Any,
authorities: Collection<GrantedAuthority>,
): SingleTokenAuthenticationToken {
return SingleTokenAuthenticationToken(principal.toString(), authorities)
}
}
/** /**
* 判断当前认证是否已完成 * 获取认证凭据对于单令牌认证来说凭证通常不适用因此返回 null
* *
* @return 如果认证成功则返回 true否则返回 false * @return 始终返回 null
*/ */
override fun isAuthenticated(): Boolean = super.isAuthenticated override fun getCredentials(): Any? = null
/**
* 获取认证的主体信息
*
* @return 返回认证的主体对象
*/
override fun getPrincipal(): Any = principal
/**
* 禁止直接设置认证状态应使用工厂方法创建已认证或未认证的实例
*
* @param authenticated 认证状态此参数将被忽略
* @throws IllegalArgumentException 总是抛出此异常以防止直接修改认证状态
*/
override fun setAuthenticated(authenticated: Boolean) {
throw IllegalArgumentException("请使用 factory 方法设置认证状态")
}
} }