feat(security): 实现 Single Token 认证机制

- 重构了原有的 ApiKey 认证体系,改为支持 Single Token 认证
- 更新了相关的配置类、服务接口、过滤器和自定义器
- 优化了认证流程,支持在请求头中提取 Token 并进行验证
-调整了安全配置,确保 Token 认证与现有安全体系兼容
This commit is contained in:
gewuyou 2025-06-26 11:36:02 +08:00
parent ec326e5a1d
commit aa9bd66259
19 changed files with 397 additions and 364 deletions

View File

@ -6,10 +6,10 @@ import org.springframework.boot.context.properties.ConfigurationProperties
* Security 授权配置属性类用于定义安全相关的可配置项
*
* 该类通过@ConfigurationProperties绑定配置前缀"forgeboot.security.authorize"
* 提供了默认异常响应消息和API密钥启用状态的配置支持
* 提供了默认异常响应消息单Token控制及相关路径模式的配置支持
*
* @property defaultExceptionResponse 当访问被拒绝时返回的默认提示信息
* @property apiKey API密钥相关配置属性对象
* @property singleToken 单Token配置属性对象包含启用状态匹配路径及是否使用授权管理器
*
* @since 2025-06-15 19:26:20
* @author gewuyou
@ -22,21 +22,28 @@ class SecurityAuthorizeProperties {
var defaultExceptionResponse: String = "Sorry, you don't have access to the resource!"
/**
* API密钥相关配置属性对象包含是否启用API密钥验证的开关
* 单Token配置属性实例用于定义Token相关的行为和路径匹配规则
*/
var apiKey: ApiKeyProperties = ApiKeyProperties()
var singleToken: SingleTokenProperties = SingleTokenProperties()
/**
* API密钥功能的子配置类用于控制API密钥验证的启用状态
* 单Token配置内部类封装与Token验证行为相关的配置项
*
* @property enabled 是否启用API密钥验证功能默认为false
* 用于控制特定路径下的Token验证行为包括启用状态路径匹配模式以及是否使用授权管理器
*/
class ApiKeyProperties {
class SingleTokenProperties {
/**
* 控制是否启用API密钥验证功能默认值为false
*/
var enabled: Boolean = false
var pathPatterns: List<String> = listOf("/api/**")
/**
* 指定是否通过Spring Security的AuthorizationManager进行权限决策默认为true
*/
var useAuthorizationManager: Boolean = true
/**
* 定义需要应用Token验证的请求路径模式列表
*/
var pathPatterns: List<String> = listOf("/api/**")
}
}

View File

@ -1,13 +0,0 @@
package com.gewuyou.forgeboot.security.authorize.api.core.service
import com.gewuyou.forgeboot.security.core.authorize.entities.ApiKeyPrincipal
/**
*API 密钥服务
*
* @since 2025-06-25 13:10:38
* @author gewuyou
*/
fun interface ApiKeyService {
fun validate(apiKey: String): ApiKeyPrincipal // 负责验证与解析
}

View File

@ -0,0 +1,22 @@
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

@ -1,58 +0,0 @@
package com.gewuyou.forgeboot.security.authorize.autoconfigure.core
import com.gewuyou.forgeboot.security.authorize.api.core.service.ApiKeyService
import com.gewuyou.forgeboot.security.authorize.impl.core.provider.ApiKeyAuthenticationProvider
import com.gewuyou.forgeboot.security.core.authorize.entities.ApiKeyPrincipal
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationProvider
/**
* API 密钥安全核心自动配置类
*
* 该配置类用于注册与 API 密钥相关的 Bean确保在 Spring 容器中存在必要的服务和认证提供者
* 主要作用包括
* - 提供默认的 ApiKeyService Bean未实现时
* - 提供 ApiKeyAuthenticationProvider Bean 以支持基于 API 密钥的身份验证
*
* @since 2025-06-25 15:49:50
* @author gewuyou
*/
@Configuration(proxyBeanMethods = false)
class ApiKeySecurityCoreAutoConfiguration {
/**
* 提供一个默认的 ApiKeyService Bean 实现
*
* 如果容器中不存在 ApiKeyService 的实现则会使用此默认 Bean
* 默认实现的 validate 方法始终抛出 UnsupportedOperationException
* 提示用户需要自定义并注册自己的 ApiKeyService 实现
*
* @return 返回一个未实现的 ApiKeyService 对象
*/
@Bean
@ConditionalOnMissingBean
fun apiKeyService(): ApiKeyService {
return object : ApiKeyService {
override fun validate(apiKey: String): ApiKeyPrincipal {
throw UnsupportedOperationException("请提供 ApiKeyService 实现")
}
}
}
/**
* 提供一个用于认证的 ApiKeyAuthenticationProvider Bean
*
* 如果容器中尚不存在同名 Bean则创建并返回 ApiKeyAuthenticationProvider 实例
* Provider 使用传入的 apiKeyService 来处理具体的 API 密钥验证逻辑
*
* @param apiKeyService 用于处理 API 密钥逻辑的服务实现
* @return AuthenticationProvider 的具体实现对象
*/
@Bean("apiKeyAuthenticationProvider")
@ConditionalOnMissingBean
fun apiKeyAuthenticationProvider(apiKeyService: ApiKeyService): AuthenticationProvider {
return ApiKeyAuthenticationProvider(apiKeyService)
}
}

View File

@ -0,0 +1,60 @@
package com.gewuyou.forgeboot.security.authorize.autoconfigure.core
import com.gewuyou.forgeboot.security.authorize.api.core.service.SingleTokenService
import com.gewuyou.forgeboot.security.authorize.impl.core.provider.SingleTokenAuthenticationProvider
import com.gewuyou.forgeboot.security.core.authorize.entities.SingleTokenPrincipal
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationProvider
/**
* 配置类用于自动配置单点令牌Single Token认证相关的核心组件
*
* @since 2025-06-25 15:49:50
* @author gewuyou
*/
@Configuration(proxyBeanMethods = false)
class SingleTokenSecurityCoreAutoConfiguration {
/**
* 提供一个默认的 SingleTokenService Bean用于验证令牌并返回用户主体信息
*
* 如果上下文中尚未定义此类 Bean则使用此默认实现
* 默认实现会在调用 validate 方法时抛出 UnsupportedOperationException
* 提示使用者应提供自定义的 SingleTokenService 实现
*
* @return 返回一个 SingleTokenService 接口的默认实现
*/
@Bean
@ConditionalOnMissingBean
fun singleTokenService(): SingleTokenService {
return object : SingleTokenService {
/**
* 验证给定的 token 并返回对应的用户主体信息
*
* @param token 要验证的令牌字符串
* @return 返回包含用户信息的 SingleTokenPrincipal 对象
* @throws UnsupportedOperationException 始终抛出异常提示需要自定义实现
*/
override fun validate(token: String): SingleTokenPrincipal {
throw UnsupportedOperationException("请提供 SingleTokenService 实现")
}
}
}
/**
* 注册 SingleTokenAuthenticationProvider Bean用于 Spring Security 的认证流程
*
* 该认证提供者依赖于 SingleTokenService 来完成实际的令牌验证工作
* 如果上下文中尚未定义同名 Bean则注册该 Bean
*
* @param singleTokenService 用于令牌验证的服务实例
* @return 返回配置好的 SingleTokenAuthenticationProvider 实例
*/
@Bean("singleTokenAuthenticationProvider")
@ConditionalOnMissingBean
fun singleTokenAuthenticationProvider(singleTokenService: SingleTokenService): AuthenticationProvider {
return SingleTokenAuthenticationProvider(singleTokenService)
}
}

View File

@ -1,8 +1,8 @@
package com.gewuyou.forgeboot.security.authorize.autoconfigure.servlet
import com.gewuyou.forgeboot.security.authorize.api.core.config.SecurityAuthorizeProperties
import com.gewuyou.forgeboot.security.authorize.impl.servlet.customizer.ApiKeyHttpSecurityCustomizer
import com.gewuyou.forgeboot.security.authorize.impl.servlet.filter.ApiKeyAuthenticationFilter
import com.gewuyou.forgeboot.security.authorize.impl.servlet.customizer.SingleTokenHttpSecurityCustomizer
import com.gewuyou.forgeboot.security.authorize.impl.servlet.filter.SingleTokenAuthenticationFilter
import com.gewuyou.forgeboot.security.core.common.constants.SecurityConstants
import com.gewuyou.forgeboot.security.core.common.customizer.HttpSecurityCustomizer
import com.gewuyou.forgeboot.security.core.common.registrar.SecurityFilterChainRegistrar
@ -13,7 +13,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authorization.AuthorizationManager
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.SecurityFilterChain
@ -22,102 +21,110 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher
import org.springframework.security.web.util.matcher.OrRequestMatcher
/**
* API 密钥安全自动配置类用于在 Servlet Web 应用中自动装配与 API 密钥相关的安全组件
* Single Token安全自动配置类用于在 Servlet Web 应用中自动装配与 Single Token相关的安全组件
*
* 此配置仅在以下条件下生效
* 1. 应用类型为 Servlet
* 2. 配置项 "forgeboot.security.authorize.api-key.enabled" true
* 2. 配置项 "forgeboot.security.authorize.single-token.enabled" true
*
* @property securityAuthorizeProperties 安全授权配置属性用于获取 API 密钥相关路径等信息
* @property securityAuthorizeProperties 安全授权配置属性用于获取 Single Token相关路径等信息
* @since 2025-06-25 13:41:56
* @author gewuyou
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnProperty(
prefix = "forgeboot.security.authorize.api-key",
prefix = "forgeboot.security.authorize.single-token",
name = ["enabled"],
havingValue = "true",
matchIfMissing = false
)
class ServletApiKeySecurityAutoConfiguration(
class ServletSingleTokenSecurityAutoConfiguration(
private val securityAuthorizeProperties: SecurityAuthorizeProperties,
) {
/**
* 创建 API 密钥认证过滤器 Bean
* 创建 Single Token认证过滤器 Bean
*
* 该方法在 Spring 容器中尚不存在同名 Bean 的情况下
* 构建一个使用指定认证管理器的 ApiKeyAuthenticationFilter 实例
* 构建一个 SingleTokenAuthenticationFilter 实例作为 Spring Bean
* 该过滤器用于处理基于 Single Token 的身份验证逻辑
*
* @param authenticationManager 认证管理器用于执行认证逻辑
* @return 返回构建完成的 ApiKeyAuthenticationFilter 实例
* @return 返回构建完成的 Filter 接口实现对象 SingleTokenAuthenticationFilter
*/
@Bean
@ConditionalOnMissingBean(name = ["apiKeyAuthenticationFilter"])
fun apiKeyAuthenticationFilter(
authenticationManager: AuthenticationManager,
): Filter {
return ApiKeyAuthenticationFilter(authenticationManager)
@ConditionalOnMissingBean(name = ["singleTokenAuthenticationFilter"])
fun singleTokenAuthenticationFilter(): Filter {
return SingleTokenAuthenticationFilter()
}
/**
* 提供一个用于自定义 HTTP 安全配置的 API 密钥安全自定义器 Bean
* 提供一个用于自定义 HTTP 安全配置的 Single Token安全自定义器 Bean
*
* 如果容器中尚不存在同名 Bean则创建并返回 ApiKeyHttpSecurityCustomizer 实例
* 此自定义器将注入的认证提供者和过滤器用于构建定制化的安全配置
* 如果容器中尚不存在同名 Bean则创建并返回 SingleTokenHttpSecurityCustomizer 实例
* 此自定义器将注入的认证过滤器用于构建定制化的安全配置实现对 HttpSecurity 的扩展
*
* @param apiKeyAuthenticationProvider 注入已配置的认证提供者用于安全链构建
* @param apiKeyAuthenticationFilter 注入已配置的认证过滤器用于请求处理
* @param singleTokenAuthenticationFilter 注入已配置的认证过滤器用于请求处理
* @return 返回 HttpSecurityCustomizer 的具体实现对象
*/
@Bean
@ConditionalOnMissingBean
fun apiKeyHttpSecurityCustomizer(
@Qualifier("apiKeyAuthenticationFilter")
apiKeyAuthenticationFilter: Filter,
fun singleTokenHttpSecurityCustomizer(
@Qualifier("singleTokenAuthenticationFilter")
singleTokenAuthenticationFilter: Filter,
): HttpSecurityCustomizer {
return ApiKeyHttpSecurityCustomizer(apiKeyAuthenticationFilter)
return SingleTokenHttpSecurityCustomizer(singleTokenAuthenticationFilter)
}
/**
* 创建默认的安全过滤链适用于 Servlet 编程模型
*
* 此方法基于配置的路径模式构建一个复合请求匹配器并通过注册器创建对应的安全过滤链
* 过滤链根据配置决定是否使用授权管理器进行访问控制
* 此方法基于配置的路径模式构建一个复合请求匹配器并通过注册器创建对应的 SingleToken 安全过滤链
* 过滤链根据配置决定是否使用授权管理器进行访问控制提供两种模式
* - 使用授权管理器时所有请求通过指定的 authorizeManager 进行访问控制
* - 不使用授权管理器时要求所有请求必须经过身份验证
*
* @param registrar 安全过滤链注册器用于构建和管理过滤链
* @param http Spring Security HttpSecurity 配置对象
* @param authorizeManager 授权管理器用于在启用授权管理时定义访问策略
* @return 构建完成的安全过滤链实例
*/
@Bean(name = ["defaultApiKeySecurityFilterChain"])
fun defaultApiKeySecurityFilterChain(
@Bean(name = ["defaultSingleTokenSecurityFilterChain"])
fun defaultSingleTokenSecurityFilterChain(
registrar: SecurityFilterChainRegistrar,
http: HttpSecurity,
authorizeManager: AuthorizationManager<RequestAuthorizationContext>
): SecurityFilterChain {
// 从配置中获取 API 密钥适用的路径模式(如:["/api/**", "/open/**"]
val patterns = securityAuthorizeProperties.apiKey.pathPatterns
// 从配置中获取 singleToken 适用的路径模式(如:["/api/**", "/open/**"]
val patterns = securityAuthorizeProperties.singleToken.pathPatterns
// 将每个路径模式转换为 AntPathRequestMatcher 实例
/**
* 将每个路径模式转换为 AntPathRequestMatcher 实例
*/
val matchers = patterns.map { AntPathRequestMatcher(it) }
// 使用 OrRequestMatcher 组合所有路径匹配规则,实现多路径匹配支持
/**
* 使用 OrRequestMatcher 组合所有路径匹配规则实现多路径匹配支持
*/
val combinedMatcher = OrRequestMatcher(matchers)
// 调用注册器构建安全链,指定链 ID、HttpSecurity 对象和请求匹配器
/**
* 调用注册器构建安全链指定链 IDHttpSecurity 对象和请求匹配器
*/
return registrar.buildChain(
SecurityConstants.API_KEY_CHAIN_ID,
http,
combinedMatcher
) { config ->
if (securityAuthorizeProperties.apiKey.useAuthorizationManager) {
// 启用授权管理器时,配置请求通过指定的 authorizeManager 进行访问控制
if (securityAuthorizeProperties.singleToken.useAuthorizationManager) {
/**
* 启用授权管理器时配置请求通过指定的 authorizeManager 进行访问控制
*/
config.authorizeHttpRequests {
it.anyRequest().access(authorizeManager)
}
} else {
// 禁用授权管理器时,要求所有请求必须经过身份验证
/**
* 禁用授权管理器时要求所有请求必须经过身份验证
*/
config.authorizeHttpRequests {
it.anyRequest().authenticated()
}

View File

@ -1,8 +1,8 @@
package com.gewuyou.forgeboot.security.authorize.autoconfigure.webflux
import com.gewuyou.forgeboot.security.authorize.api.core.config.SecurityAuthorizeProperties
import com.gewuyou.forgeboot.security.authorize.impl.webflux.customizer.ApiKeyServerHttpSecurityCustomizer
import com.gewuyou.forgeboot.security.authorize.impl.webflux.filter.ApiKeyReactiveAuthenticationFilter
import com.gewuyou.forgeboot.security.authorize.impl.webflux.customizer.SingleTokenServerHttpSecurityCustomizer
import com.gewuyou.forgeboot.security.authorize.impl.webflux.filter.ReactiveSingleTokenAuthenticationFilter
import com.gewuyou.forgeboot.security.core.common.constants.SecurityConstants
import com.gewuyou.forgeboot.security.core.common.customizer.ServerHttpSecurityCustomizer
import com.gewuyou.forgeboot.security.core.common.registrar.SecurityWebFilterChainRegistrar
@ -24,92 +24,91 @@ import org.springframework.security.web.server.util.matcher.ServerWebExchangeMat
import org.springframework.web.server.WebFilter
/**
* 反应式 API 密钥安全自动配置
* 配置类用于在满足条件时自动配置基于 Single Token 的反应式安全机制
*
* 该配置类负责在 WebFlux 环境下启用 API Key 认证机制
* 仅在满足以下条件时生效
* - 应用类型为 REACTIVE反应式应用
* - 配置项 `forgeboot.security.authorize.api-key.enabled` 被设置为 true
* - 配置项 `forgeboot.security.authorize.single-token.enabled` 被设置为 true
*
* @property securityAuthorizeProperties 安全授权配置属性用于获取 API Key 的路径匹配规则等信息
* @property securityAuthorizeProperties 安全授权配置属性用于获取 Single Token 的路径匹配规则等信息
* @since 2025-06-25 21:04:37
* @author gewuyou
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnProperty(
prefix = "forgeboot.security.authorize.api-key",
prefix = "forgeboot.security.authorize.single-token",
name = ["enabled"],
havingValue = "true",
matchIfMissing = false
)
class ReactiveApiKeySecurityAutoConfiguration(
class ReactiveSingleTokenSecurityAutoConfiguration(
private val securityAuthorizeProperties: SecurityAuthorizeProperties
) {
/**
* 创建 API Key 反应式认证过滤器 Bean
* 创建并注册名为 reactiveSingleTokenAuthenticationFilter WebFilter Bean
*
* 该过滤器用于处理传入请求的 API Key 认证逻辑
* 如果容器中尚未定义名为 "apiKeyReactiveAuthenticationFilter" Bean则创建此 Bean
* 该过滤器负责处理基于 Single Token 的认证逻辑确保请求携带有效的 Token
* 仅当容器中尚未存在同名的 Bean 时才会创建此实例
*
* @param reactiveAuthenticationManager 反应式认证管理器用于执行认证操作
* @return 构建完成的 WebFilter 实例
*/
@Bean
@ConditionalOnMissingBean(name = ["apiKeyReactiveAuthenticationFilter"])
fun apiKeyAuthenticationFilter(
@ConditionalOnMissingBean(name = ["reactiveSingleTokenAuthenticationFilter"])
fun singleTokenAuthenticationFilter(
reactiveAuthenticationManager: ReactiveAuthenticationManager
): WebFilter {
return ApiKeyReactiveAuthenticationFilter(reactiveAuthenticationManager)
return ReactiveSingleTokenAuthenticationFilter(reactiveAuthenticationManager)
}
/**
* 创建并注册 ServerHttpSecurityCustomizer Bean用于定制 API Key 安全配置
* 创建并注册 ServerHttpSecurityCustomizer Bean用于定制 Single Token 安全配置
*
* 方法定义了一个用于构建 Spring Security 过滤器链时插入 API Key 认证逻辑的自定义器
* 仅当容器中尚未存在相同类型的 Bean 才会创建此 Bean
* 自定义器用于在构建 Spring Security 过滤器链时插入 Single Token 认证逻辑
* 仅当容器中尚未存在相同类型的 Bean 才会创建此实例
*
* @param apiKeyReactiveAuthenticationFilter 使用指定名称从 Spring 容器中注入的 API Key 反应式认证过滤器 Bean
* 该过滤器负责处理实际的 API Key 认证逻辑
* @param reactiveSingleTokenAuthenticationFilter 使用指定名称从 Spring 容器中注入的 Single Token 反应式认证过滤器 Bean
* 该过滤器负责处理实际的 Single Token 认证逻辑
*
* @return 构建完成的 ApiKeyServerHttpSecurityCustomizer 实例用于在安全配置中添加 API Key 相关逻辑
* @return 构建完成的 ServerHttpSecurityCustomizer 实例用于在安全配置中添加 Single Token 相关逻辑
*/
@Bean
@ConditionalOnMissingBean
fun apiKeyServerHttpSecurityCustomizer(
@Qualifier("apiKeyReactiveAuthenticationFilter")
apiKeyReactiveAuthenticationFilter: WebFilter,
fun singleTokenServerHttpSecurityCustomizer(
@Qualifier("reactiveSingleTokenAuthenticationFilter")
reactiveSingleTokenAuthenticationFilter: WebFilter,
): ServerHttpSecurityCustomizer {
return ApiKeyServerHttpSecurityCustomizer(apiKeyReactiveAuthenticationFilter)
return SingleTokenServerHttpSecurityCustomizer(reactiveSingleTokenAuthenticationFilter)
}
/**
* 创建并注册基于 API Key 认证的 WebFlux 安全过滤器链
* 创建并注册基于 Single Token 认证的 WebFlux 安全过滤器链
*
* 该方法利用 SecurityWebFilterChainRegistrar 注册一个具有路径匹配规则的安全过滤器链
* 仅对符合配置中指定路径模式的请求生效并要求通过 API Key 认证
* 仅对符合配置中指定路径模式的请求生效并要求通过 Single Token 认证
*
* @param registrar 用于注册和构建安全过滤器链的核心工具类负责链的组装过程
* @param http Spring Security 提供的 ServerHttpSecurity 实例用于构建 HTTP 安全配置
* @param reactiveAuthorizationManager 反应式授权管理器用于在使用授权管理逻辑时提供访问控制
* @return 构建完成的 SecurityWebFilterChain 实例表示定义好的安全过滤器链
*
* 重要逻辑说明
* 1. patterns: 从配置中获取 API Key 的路径匹配规则
* 2. matchers: 将每个路径转换为 ServerWebExchangeMatcher 实例
* 3. combinedMatcher: 综合所有 matcher生成复合的匹配规则
* 4. buildChain: 利用 registrar 构建过滤器链并根据 useAuthorizationManager 配置决定采用何种认证方式
* 关键逻辑总结
* 1. 获取配置中的路径模式列表 patterns
* 2. 将每个路径转换为 ServerWebExchangeMatcher 实例形成 matchers 列表
* 3. 合并所有 matcher 形成 combinedMatcher 复合匹配规则
* 4. 利用 registrar 构建过滤器链并根据 useAuthorizationManager 配置决定采用何种认证方式
* - 若启用 authorizationManager则通过 access 方法设置自定义的授权逻辑
* - 否则直接要求请求必须经过认证
*/
@Bean(name = ["defaultApiKeySecurityWebFilterChain"])
fun defaultApiKeySecurityWebFilterChain(
@Bean(name = ["defaultSingleTokenSecurityWebFilterChain"])
fun defaultSingleTokenSecurityWebFilterChain(
registrar: SecurityWebFilterChainRegistrar,
http: ServerHttpSecurity,
reactiveAuthorizationManager: ReactiveAuthorizationManager<AuthorizationContext>
): SecurityWebFilterChain {
val patterns = securityAuthorizeProperties.apiKey.pathPatterns
val patterns = securityAuthorizeProperties.singleToken.pathPatterns
val matchers = patterns.map { PathPatternParserServerWebExchangeMatcher(it) }
val combinedMatcher: ServerWebExchangeMatcher =
ServerWebExchangeMatchers.matchers(*matchers.toTypedArray())
@ -118,7 +117,7 @@ class ReactiveApiKeySecurityAutoConfiguration(
http,
combinedMatcher
) { config ->
if (securityAuthorizeProperties.apiKey.useAuthorizationManager) {
if (securityAuthorizeProperties.singleToken.useAuthorizationManager) {
config.authorizeExchange {
it.anyExchange().access(reactiveAuthorizationManager)
}

View File

@ -1,7 +1,7 @@
com.gewuyou.forgeboot.security.authorize.autoconfigure.ForgeSecurityAuthorizeAutoConfiguration
com.gewuyou.forgeboot.security.authorize.autoconfigure.core.ForgeSecurityAuthorizeCoreConfiguration
com.gewuyou.forgeboot.security.authorize.autoconfigure.core.ApiKeySecurityCoreAutoConfiguration
com.gewuyou.forgeboot.security.authorize.autoconfigure.core.SingleTokenSecurityCoreAutoConfiguration
com.gewuyou.forgeboot.security.authorize.autoconfigure.webflux.ReactiveAuthorizeSecurityConfiguration
com.gewuyou.forgeboot.security.authorize.autoconfigure.webflux.ReactiveApiKeySecurityAutoConfiguration
com.gewuyou.forgeboot.security.authorize.autoconfigure.webflux.ReactiveSingleTokenSecurityAutoConfiguration
com.gewuyou.forgeboot.security.authorize.autoconfigure.servlet.ServletAuthorizeSecurityConfiguration
com.gewuyou.forgeboot.security.authorize.autoconfigure.servlet.ServletApiKeySecurityAutoConfiguration
com.gewuyou.forgeboot.security.authorize.autoconfigure.servlet.ServletSingleTokenSecurityAutoConfiguration

View File

@ -1,57 +0,0 @@
package com.gewuyou.forgeboot.security.authorize.impl.core.provider
import com.gewuyou.forgeboot.security.authorize.api.core.service.ApiKeyService
import com.gewuyou.forgeboot.security.core.common.token.ApiKeyAuthenticationToken
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.core.Authentication
/**
* API 密钥身份验证提供程序
*
* 该类实现 Spring Security AuthenticationProvider 接口用于处理基于 API Key 的身份验证流程
*
* @property apiKeyService 用于校验 API Key 并获取用户信息和权限的服务组件
* @constructor 创建一个 ApiKeyAuthenticationProvider 实例
*
* @since 2025-06-25 13:09:43
* @author gewuyou
*/
class ApiKeyAuthenticationProvider(
private val apiKeyService: ApiKeyService
) : AuthenticationProvider {
/**
* 执行身份验证操作
*
* 将传入的 Authentication 对象转换为 ApiKeyAuthenticationToken
* 然后使用 apiKeyService 校验 API Key 并获取相关用户信息和权限
*
* @param authentication 包含 API Key 的身份验证请求对象
* @return 返回已认证的 Authentication 对象
*/
override fun authenticate(authentication: Authentication): Authentication {
val token = authentication as ApiKeyAuthenticationToken
val keyInfo = apiKeyService.validate(token.apiKey)
return ApiKeyAuthenticationToken(
token.apiKey,
keyInfo.principal,
keyInfo.authorities
).apply {
isAuthenticated = true
}
}
/**
* 判断此 Provider 是否支持给定的身份验证类型
*
* 用于确定当前 Provider 是否可以处理指定的 Authentication 类型
* 此方法被调用时会检查是否为 ApiKeyAuthenticationToken 或其子类
*
* @param authentication 要检查的身份验证类
* @return 如果支持则返回 true否则返回 false
*/
override fun supports(authentication: Class<*>): Boolean {
return ApiKeyAuthenticationToken::class.java.isAssignableFrom(authentication)
}
}

View File

@ -0,0 +1,54 @@
package com.gewuyou.forgeboot.security.authorize.impl.core.provider
import com.gewuyou.forgeboot.security.authorize.api.core.service.SingleTokenService
import com.gewuyou.forgeboot.security.core.common.token.SingleTokenAuthenticationToken
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.core.Authentication
/**
* 单例 Token 认证提供者实现 Spring Security AuthenticationProvider 接口
*
* 用于处理基于 SingleToken 的身份验证流程
*
* @property singleTokenService 用于执行 Token 校验的服务组件
* @author gewuyou
* @since 2025-06-25 13:09:43
*/
class SingleTokenAuthenticationProvider(
private val singleTokenService: SingleTokenService
) : AuthenticationProvider {
/**
* 执行身份验证操作
*
* 将传入的身份验证对象转换为 SingleTokenAuthenticationToken
* 然后通过 singleTokenService 验证 Token 的有效性并返回认证后的 Authentication 对象
*
* @param authentication 需要被验证的 Authentication 实例
* @return 返回已认证的 Authentication 对象
*/
override fun authenticate(authentication: Authentication): Authentication {
val token = authentication as SingleTokenAuthenticationToken
val tokenInfo = singleTokenService.validate(token.singleToken)
return SingleTokenAuthenticationToken(
token.singleToken,
tokenInfo.principal,
tokenInfo.authorities
).apply {
isAuthenticated = true
}
}
/**
* 判断当前 Provider 是否支持给定的身份验证类型
*
* 此方法检查传入的身份验证类是否是 SingleTokenAuthenticationToken 或其子类
*
* @param authentication 要检查的身份验证类
* @return 如果支持该类型则返回 true否则返回 false
*/
override fun supports(authentication: Class<*>): Boolean {
return SingleTokenAuthenticationToken::class.java.isAssignableFrom(authentication)
}
}

View File

@ -7,19 +7,17 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
/**
* API 密钥 HTTP 安全定制器
* 基于单 Token 认证的安全配置定制器
*
* 用于为基于 API 密钥的身份验证机制定制 Spring Security HTTP 安全配置
* 该类实现了 HttpSecurityCustomizer 接口能够根据指定的安全链 ID 决定是否启用当前的认证逻辑
* 并负责将 API 密钥身份验证过滤器和提供者注入到安全配置中
* 该类用于在 Spring Security 的过滤器链中注册并配置 API 密钥身份验证逻辑
* 仅当当前安全链标识符匹配 API_KEY_CHAIN_ID 时生效
*
* @property apiKeyAuthenticationProvider 提供 API 密钥身份验证逻辑的认证提供者
* @property apiKeyAuthenticationFilter 处理 API 密钥身份验证请求的过滤器实例
* @property singleTokenAuthenticationFilter 处理 API 密钥身份验证请求的过滤器实例
* @since 2025-06-25 16:09:38
* @author gewuyou
*/
class ApiKeyHttpSecurityCustomizer(
private val apiKeyAuthenticationFilter: Filter
class SingleTokenHttpSecurityCustomizer(
private val singleTokenAuthenticationFilter: Filter
) : HttpSecurityCustomizer {
/**
@ -38,11 +36,8 @@ class ApiKeyHttpSecurityCustomizer(
/**
* 执行安全配置的定制逻辑
*
* API 密钥身份验证相关的组件注册到 Spring Security 流程中
* 包括
* - 注册认证提供者apiKeyAuthenticationProvider
* - 在请求处理流程中插入 ApiKeyAuthenticationFilter 过滤器
* 该过滤器会在 UsernamePasswordAuthenticationFilter 前执行
* API 密钥身份验证过滤器添加到 Spring Security 的过滤器链中
* 置于 UsernamePasswordAuthenticationFilter 之前以确保优先处理 Token 请求
*
* @param http 用于构建 HTTP 安全策略的 HttpSecurity 实例
* 通过此参数可添加或修改安全规则如认证授权等
@ -50,6 +45,6 @@ class ApiKeyHttpSecurityCustomizer(
override fun customize(http: HttpSecurity) {
// 配置安全逻辑:注册认证提供者并将 API 密钥过滤器插入到过滤器链中的合适位置
http
.addFilterBefore(apiKeyAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java)
.addFilterBefore(singleTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java)
}
}

View File

@ -1,51 +0,0 @@
package com.gewuyou.forgeboot.security.authorize.impl.servlet.filter
import com.gewuyou.forgeboot.security.core.common.constants.SecurityConstants
import com.gewuyou.forgeboot.security.core.common.token.ApiKeyAuthenticationToken
import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.filter.OncePerRequestFilter
/**
* API 密钥身份验证筛选器
* 用于处理基于API密钥的身份验证流程继承自OncePerRequestFilter以确保每个请求只被过滤一次
*
* @param authenticationManager 身份验证管理器用于执行实际的身份验证操作
* @since 2025-06-25 13:34:47
* @author gewuyou
*/
class ApiKeyAuthenticationFilter(
private val authenticationManager: AuthenticationManager
) : OncePerRequestFilter() {
/**
* 执行内部过滤逻辑
* 从请求头中提取API密钥并进行身份验证
*
* @param request 当前HTTP请求
* @param response 当前HTTP响应
* @param chain 过滤器链
*/
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) {
// 从请求头中获取Authorization字段
val header = request.getHeader(SecurityConstants.AUTHORIZATION_HEADER)
// 检查是否为Bearer类型认证信息
if (header?.startsWith(SecurityConstants.BEARER_PREFIX) == true) {
// 提取并清理API密钥
val apiKey = header.removePrefix(SecurityConstants.BEARER_PREFIX).trim()
// 创建认证令牌
val token = ApiKeyAuthenticationToken(apiKey, null)
// 执行认证并存储认证结果到安全上下文中
val authResult = authenticationManager.authenticate(token)
SecurityContextHolder.getContext().authentication = authResult
}
// 继续执行过滤器链
chain.doFilter(request, response)
}
}

View File

@ -0,0 +1,48 @@
package com.gewuyou.forgeboot.security.authorize.impl.servlet.filter
import com.gewuyou.forgeboot.security.core.common.constants.SecurityConstants
import com.gewuyou.forgeboot.security.core.common.token.SingleTokenAuthenticationToken
import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.filter.OncePerRequestFilter
/**
* 单Token认证过滤器
*
* 用于在请求处理链中执行基于Token的身份验证逻辑
* 从请求头中提取指定格式的Token并构造未认证身份令牌放入安全上下文
*
* @since 2025-06-25 13:34:47
* @author gewuyou
*/
class SingleTokenAuthenticationFilter() : OncePerRequestFilter() {
/**
* 执行内部过滤逻辑
*
* 从请求头中提取API密钥解析出Token后构造未认证的SingleTokenAuthenticationToken
* 并将其设置到SecurityContextHolder上下文中若当前上下文已存在认证信息则直接跳过
*
* @param request 当前HTTP请求
* @param response 当前HTTP响应
* @param chain 过滤器链用于继续执行后续的过滤操作
*/
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) {
// 如果已有认证信息,跳过
if (SecurityContextHolder.getContext().authentication != null) {
chain.doFilter(request, response)
return
}
val header = request.getHeader(SecurityConstants.AUTHORIZATION_HEADER)
if (header?.startsWith(SecurityConstants.BEARER_PREFIX) == true) {
val token = header.removePrefix(SecurityConstants.BEARER_PREFIX).trim()
// 构造未认证的 token 放入上下文
val authentication = SingleTokenAuthenticationToken(token, null)
SecurityContextHolder.getContext().authentication = authentication
}
chain.doFilter(request, response)
}
}

View File

@ -1,46 +0,0 @@
package com.gewuyou.forgeboot.security.authorize.impl.webflux.customizer
import com.gewuyou.forgeboot.security.core.common.constants.SecurityConstants
import com.gewuyou.forgeboot.security.core.common.customizer.ServerHttpSecurityCustomizer
import org.springframework.security.config.web.server.SecurityWebFiltersOrder
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.web.server.WebFilter
/**
* API 密钥服务器 HTTP 安全定制器
*
* 该类用于在 Spring WebFlux 环境下基于 API 密钥进行认证的安全配置定制
* 它实现了 ServerHttpSecurityCustomizer 接口能将自定义的认证逻辑集成到安全链中
*
* @constructor
* @param apiKeyReactiveAuthenticationFilter 自定义的 WebFilter 实现用于执行 API 密钥认证逻辑
*/
class ApiKeyServerHttpSecurityCustomizer(
private val apiKeyReactiveAuthenticationFilter: WebFilter,
) : ServerHttpSecurityCustomizer {
/**
* 判断当前定制器是否支持处理指定的安全链配置
*
* 此方法用于标识该定制器是否适用于特定的安全链配置
* 实现类应根据 chainId 参数决定是否启用此定制器的逻辑
*
* @param chainId 安全链的唯一标识符用于区分不同的安全配置场景
* @return Boolean 返回 true 表示支持该 chainId否则不支持
*/
override fun supports(chainId: String): Boolean {
return SecurityConstants.API_KEY_CHAIN_ID == chainId
}
/**
* 自定义 ServerHttpSecurity 配置的方法
*
* 此方法由框架调用允许开发者插入自定义的安全配置逻辑
* 方法参数提供了 ServerHttpSecurity 实例可用于链式配置
*
* @param http ServerHttpSecurity 实例用于构建 WebFlux 安全配置
*/
override fun customize(http: ServerHttpSecurity) {
// 将过滤器添加到安全链中的认证位置
http.addFilterAt(apiKeyReactiveAuthenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION)
}
}

View File

@ -0,0 +1,46 @@
package com.gewuyou.forgeboot.security.authorize.impl.webflux.customizer
import com.gewuyou.forgeboot.security.core.common.constants.SecurityConstants
import com.gewuyou.forgeboot.security.core.common.customizer.ServerHttpSecurityCustomizer
import org.springframework.security.config.web.server.SecurityWebFiltersOrder
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.web.server.WebFilter
/**
* 用于在 Spring WebFlux 环境下进行基于 Token 的认证安全配置定制
*
* 此类实现 ServerHttpSecurityCustomizer 接口负责将自定义的 Token 认证逻辑集成到安全链中
* 它通过 supports 方法判断是否适用于当前的安全链配置并通过 customize 方法插入过滤器
*
* @constructor
* @param reactiveSingleTokenAuthenticationFilter 自定义的 WebFilter 实现用于执行 Token 认证逻辑
*/
class SingleTokenServerHttpSecurityCustomizer(
private val reactiveSingleTokenAuthenticationFilter: WebFilter,
) : ServerHttpSecurityCustomizer {
/**
* 判断当前定制器是否支持处理指定的安全链配置
*
* 该方法用于标识此定制器是否适用于给定 chainId 所代表的安全配置场景
* 在本实现中仅当 chainId 与预定义的 API_KEY_CHAIN_ID 匹配时返回 true
*
* @param chainId 安全链的唯一标识符用于区分不同的安全配置场景
* @return Boolean 返回 true 表示支持该 chainId否则不支持
*/
override fun supports(chainId: String): Boolean {
return SecurityConstants.API_KEY_CHAIN_ID == chainId
}
/**
* 自定义 ServerHttpSecurity 配置的方法
*
* 此方法由框架调用允许开发者插入特定于该安全链的配置逻辑
* 参数提供了 ServerHttpSecurity 实例可以通过其添加修改或删除安全相关的组件
*
* @param http ServerHttpSecurity 实例用于构建 WebFlux 安全配置
*/
override fun customize(http: ServerHttpSecurity) {
// 将 Token 认证过滤器添加到 WebFlux 安全链中的认证位置
http.addFilterAt(reactiveSingleTokenAuthenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION)
}
}

View File

@ -1,7 +1,7 @@
package com.gewuyou.forgeboot.security.authorize.impl.webflux.filter
import com.gewuyou.forgeboot.security.core.common.constants.SecurityConstants
import com.gewuyou.forgeboot.security.core.common.token.ApiKeyAuthenticationToken
import com.gewuyou.forgeboot.security.core.common.token.SingleTokenAuthenticationToken
import org.springframework.http.HttpHeaders
import org.springframework.security.authentication.ReactiveAuthenticationManager
import org.springframework.security.core.Authentication
@ -11,53 +11,53 @@ import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
/**
* API 密钥反应式身份验证过滤器
* 反应式单令牌身份验证过滤器
*
* 用于在 WebFlux 环境下通过 API 密钥进行身份验证的过滤器
* 它继承自 [AuthenticationWebFilter]并使用 [ApiKeyServerAuthenticationConverter]
* 将请求中的 API 密钥转换为认证对象 [ApiKeyAuthenticationToken]
* 用于在 WebFlux 环境下通过Token 进行身份验证的过滤器
* 它继承自 [AuthenticationWebFilter]并使用 [SingleTokenServerAuthenticationConverter]
* 将请求中的 Token 转换为认证对象 [SingleTokenAuthenticationToken]
*
* @property authenticationManager 身份验证管理器用于处理身份验证请求
* @since 2025-06-25 16:46:13
* @author gewuyou
*/
class ApiKeyReactiveAuthenticationFilter(
class ReactiveSingleTokenAuthenticationFilter(
authenticationManager: ReactiveAuthenticationManager
) : AuthenticationWebFilter(authenticationManager) {
/**
* 初始化代码块设置当前过滤器使用的身份验证转换器
*
* 此处将默认的转换逻辑替换为基于 API 密钥的身份验证转换器
* 该转换器负责从请求头中提取并解析 API 密钥
* 此处将默认的转换逻辑替换为基于 Token 的身份验证转换器
* 该转换器负责从请求头中提取并解析 Token
*/
init {
setServerAuthenticationConverter(ApiKeyServerAuthenticationConverter())
setServerAuthenticationConverter(SingleTokenServerAuthenticationConverter())
}
/**
* 基于 API 密钥的身份验证转换器
* 基于 Token 的身份验证转换器实现类
*
* 实现了 [ServerAuthenticationConverter] 接口负责从 [ServerWebExchange] 中提取 API 密钥
* 并将其转换为对应的认证对象 [ApiKeyAuthenticationToken]
* 实现了 [ServerAuthenticationConverter] 接口负责从 [ServerWebExchange] 中提取 Token
* 并将其转换为对应的认证对象 [SingleTokenAuthenticationToken]
*/
private class ApiKeyServerAuthenticationConverter : ServerAuthenticationConverter {
private class SingleTokenServerAuthenticationConverter : ServerAuthenticationConverter {
/**
* 将传入的请求上下文转换为认证对象
*
* 此方法尝试从请求头中获取 `Authorization` 字段并检查其是否以指定的前缀开头
* 如果匹配则移除前缀并提取出 API 密钥最终构造一个未认证的 [ApiKeyAuthenticationToken] 对象
* 如果匹配则移除前缀并提取出 Token最终构造一个未认证的 [SingleTokenAuthenticationToken] 对象
*
* @param exchange 当前的服务器 Web 交换对象包含请求信息
* @return 返回一个 [Mono<Authentication>?] 类型的对象如果成功提取 API 密钥则返回包含认证对象的 Mono
* @return 返回一个 [Mono<Authentication>?] 类型的对象如果成功提取 Token 则返回包含认证对象的 Mono
* 否则返回空的 Mono
*/
override fun convert(exchange: ServerWebExchange): Mono<Authentication>? {
val headerValue = exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION)
return if (headerValue?.startsWith(SecurityConstants.BEARER_PREFIX, ignoreCase = true) == true) {
val apiKey = headerValue.removePrefix(SecurityConstants.BEARER_PREFIX).trim()
Mono.just(ApiKeyAuthenticationToken(apiKey, null))
Mono.just(SingleTokenAuthenticationToken(apiKey, null))
} else {
Mono.empty()
}

View File

@ -11,7 +11,7 @@ import org.springframework.security.core.GrantedAuthority
* @since 2025-06-25 13:11:37
* @author gewuyou
*/
data class ApiKeyPrincipal (
data class SingleTokenPrincipal (
val principal: String,
val authorities: List<GrantedAuthority>
)

View File

@ -1,23 +0,0 @@
package com.gewuyou.forgeboot.security.core.common.token
import org.springframework.security.authentication.AbstractAuthenticationToken
import org.springframework.security.core.GrantedAuthority
/**
*API 密钥认证令牌
*
* @since 2025-06-25 13:06:54
* @author gewuyou
*/
class ApiKeyAuthenticationToken(
val apiKey: String,
private val principal: Any?,
private val authorities: Collection<GrantedAuthority> = listOf()
) : AbstractAuthenticationToken(authorities) {
override fun getCredentials(): Any = apiKey
override fun getPrincipal(): Any? = principal
override fun isAuthenticated(): Boolean = super.isAuthenticated
}

View File

@ -0,0 +1,43 @@
package com.gewuyou.forgeboot.security.core.common.token
import org.springframework.security.authentication.AbstractAuthenticationToken
import org.springframework.security.core.GrantedAuthority
/**
* 用于单令牌认证的认证令牌实现类该类扩展了 Spring Security AbstractAuthenticationToken
* 用于表示基于单一令牌 API Key的认证请求
*
* @param singleToken 存储认证凭据如API密钥或令牌不可为 null
* @param principal 表示经过认证的主体可以是用户对象或其他形式的身份标识
* @param authorities 用户所拥有的权限集合默认为空列表
*
* @author gewuyou
* @since 2025-06-25 13:06:54
*/
class SingleTokenAuthenticationToken(
val singleToken: String,
private val principal: Any?,
authorities: Collection<GrantedAuthority> = listOf()
) : AbstractAuthenticationToken(authorities) {
/**
* 获取认证凭据
*
* @return 返回存储在 [singleToken] 中的认证凭据
*/
override fun getCredentials(): Any = singleToken
/**
* 获取认证主体
*
* @return 返回认证主体对象可能为 null
*/
override fun getPrincipal(): Any? = principal
/**
* 判断当前认证是否已完成
*
* @return 如果认证成功则返回 true否则返回 false
*/
override fun isAuthenticated(): Boolean = super.isAuthenticated
}