mirror of
https://github.moeyy.xyz/https://github.com/GeWuYou/forgeboot
synced 2025-10-27 16:24:22 +08:00
feat(security): 添加安全认证模块
- 新增安全认证相关的多个子模块,包括核心模块、认证模块和授权模块 - 实现了用户认证、权限控制、登录请求解析等功能 - 添加了安全认证的自动配置和相关Bean定义 - 定义了多种认证策略和处理机制,支持灵活的认证流程定制
This commit is contained in:
parent
1ef75d7c17
commit
9c11b7e2e5
@ -11,6 +11,7 @@ plugins {
|
|||||||
// Kotlin kapt 支持
|
// Kotlin kapt 支持
|
||||||
alias(libs.plugins.kotlin.kapt)
|
alias(libs.plugins.kotlin.kapt)
|
||||||
id(libs.plugins.kotlin.jvm.get().pluginId)
|
id(libs.plugins.kotlin.jvm.get().pluginId)
|
||||||
|
alias(libs.plugins.gradleMavenPublishPlugin)
|
||||||
}
|
}
|
||||||
java {
|
java {
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
@ -58,7 +59,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
project.group = "com.gewuyou.forgeboot"
|
project.group = "io.github.gewuyou"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 子项目配置
|
// 子项目配置
|
||||||
@ -104,9 +105,15 @@ subprojects {
|
|||||||
plugin(libs.plugins.kotlin.jvm.get().pluginId)
|
plugin(libs.plugins.kotlin.jvm.get().pluginId)
|
||||||
plugin(libs.plugins.axionRelease.get().pluginId)
|
plugin(libs.plugins.axionRelease.get().pluginId)
|
||||||
plugin(libs.plugins.kotlin.kapt.get().pluginId)
|
plugin(libs.plugins.kotlin.kapt.get().pluginId)
|
||||||
|
plugin(libs.plugins.gradleMavenPublishPlugin.get().pluginId)
|
||||||
// 导入仓库配置
|
// 导入仓库配置
|
||||||
from(file("$configDir/repositories.gradle.kts"))
|
from(file("$configDir/repositories.gradle.kts"))
|
||||||
}
|
}
|
||||||
|
// 中央仓库
|
||||||
|
mavenPublishing {
|
||||||
|
publishToMavenCentral("DEFAULT", true)
|
||||||
|
signAllPublications()
|
||||||
|
}
|
||||||
// 发布配置
|
// 发布配置
|
||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
@ -128,9 +135,8 @@ subprojects {
|
|||||||
val host = System.getenv("GITEA_HOST")
|
val host = System.getenv("GITEA_HOST")
|
||||||
host?.let {
|
host?.let {
|
||||||
maven {
|
maven {
|
||||||
isAllowInsecureProtocol = true
|
|
||||||
name = "Gitea"
|
name = "Gitea"
|
||||||
url = uri("http://${it}/api/packages/gewuyou/maven")
|
url = uri("${it}/api/packages/gewuyou/maven")
|
||||||
credentials(HttpHeaderCredentials::class.java) {
|
credentials(HttpHeaderCredentials::class.java) {
|
||||||
name = "Authorization"
|
name = "Authorization"
|
||||||
value = "token ${System.getenv("GITEA_TOKEN")}"
|
value = "token ${System.getenv("GITEA_TOKEN")}"
|
||||||
@ -204,9 +210,88 @@ subprojects {
|
|||||||
tasks.named<Test>("test") {
|
tasks.named<Test>("test") {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 注册一个 Gradle 任务用于清理项目中的无用文件。
|
||||||
|
*
|
||||||
|
* 该任务会清理以下内容:
|
||||||
|
* - gradlew 和 gradlew.bat(Gradle 包装器脚本)
|
||||||
|
* - gradle-wrapper.jar 和 gradle-wrapper.properties(Gradle 包装器库和配置)
|
||||||
|
* - .gradle 和 gradle 目录(Gradle 缓存和配置目录)
|
||||||
|
* - src/main/resources/application.properties(开发环境配置文件)
|
||||||
|
* - src/test 目录(测试资源)
|
||||||
|
*/
|
||||||
|
tasks.register<CleanUselessFilesTask>("cleanUselessFiles") {
|
||||||
|
group = "project"
|
||||||
|
description = "删除根目录及子模块的无用文件"
|
||||||
|
|
||||||
|
includedProjectPaths.set(rootProject.subprojects.map { it.projectDir.absolutePath })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取项目布尔属性的辅助函数
|
/**
|
||||||
|
* 获取项目的布尔属性值。
|
||||||
|
*
|
||||||
|
* 用于从项目属性中获取指定键对应的布尔值,如果未设置或无法解析为布尔值,则返回默认值 false。
|
||||||
|
*
|
||||||
|
* @param key 属性键名
|
||||||
|
* @return 属性对应的布尔值,若不存在或无效则返回 false
|
||||||
|
*/
|
||||||
fun Project.getPropertyByBoolean(key: String): Boolean {
|
fun Project.getPropertyByBoolean(key: String): Boolean {
|
||||||
return properties[key]?.toString()?.toBoolean() ?: false
|
return properties[key]?.toString()?.toBoolean() ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CleanUselessFilesTask 是一个 Gradle 自定义任务类,用于清理项目中的无用文件。
|
||||||
|
*
|
||||||
|
* 该任务会遍历所有子项目,并删除预定义的无用文件和目录。
|
||||||
|
*/
|
||||||
|
abstract class CleanUselessFilesTask : DefaultTask() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取需要清理的项目路径列表。
|
||||||
|
*
|
||||||
|
* 每个路径代表一个项目的根目录,任务将基于这些目录查找并删除无用文件。
|
||||||
|
*/
|
||||||
|
@get:Input
|
||||||
|
abstract val includedProjectPaths: ListProperty<String>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行清理操作的主要方法。
|
||||||
|
*
|
||||||
|
* 此方法会构建待删除文件列表,并逐个检查是否存在并删除,最后输出删除结果。
|
||||||
|
*/
|
||||||
|
@TaskAction
|
||||||
|
fun clean() {
|
||||||
|
val targets = mutableListOf<File>()
|
||||||
|
|
||||||
|
// 构建需要清理的文件和目录列表
|
||||||
|
includedProjectPaths.get().forEach { path ->
|
||||||
|
val projectDir = File(path)
|
||||||
|
targets.add(projectDir.resolve("gradlew"))
|
||||||
|
targets.add(projectDir.resolve("gradlew.bat"))
|
||||||
|
targets.add(projectDir.resolve("gradle/wrapper/gradle-wrapper.jar"))
|
||||||
|
targets.add(projectDir.resolve("gradle/wrapper/gradle-wrapper.properties"))
|
||||||
|
targets.add(projectDir.resolve(".gradle"))
|
||||||
|
targets.add(projectDir.resolve("gradle"))
|
||||||
|
targets.add(projectDir.resolve("src/main/resources/application.properties"))
|
||||||
|
targets.add(projectDir.resolve("src/test"))
|
||||||
|
targets.add(projectDir.resolve("HELP.md"))
|
||||||
|
targets.add(projectDir.resolve("settings.gradle.kts"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行删除操作并统计结果
|
||||||
|
var count = 0
|
||||||
|
targets.filter { it.exists() }.forEach {
|
||||||
|
logger.lifecycle("删除:${it.absolutePath}")
|
||||||
|
count++
|
||||||
|
it.deleteRecursively()
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
logger.lifecycle("已删除 $count 个无用文件。")
|
||||||
|
} else {
|
||||||
|
logger.lifecycle("无无用文件。")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -7,39 +7,114 @@
|
|||||||
*/
|
*/
|
||||||
object Modules {
|
object Modules {
|
||||||
|
|
||||||
object Context{
|
/**
|
||||||
|
* Context模块:Spring Boot Starter上下文支持模块
|
||||||
|
* 提供基础的上下文功能,包含API、实现及自动配置模块
|
||||||
|
*/
|
||||||
|
object Context {
|
||||||
const val STARTER = ":forgeboot-context-spring-boot-starter"
|
const val STARTER = ":forgeboot-context-spring-boot-starter"
|
||||||
const val API = ":forgeboot-context-spring-boot-starter:forgeboot-context-api"
|
const val API = ":forgeboot-context-spring-boot-starter:forgeboot-context-api"
|
||||||
const val IMPL = ":forgeboot-context-spring-boot-starter:forgeboot-context-impl"
|
const val IMPL = ":forgeboot-context-spring-boot-starter:forgeboot-context-impl"
|
||||||
const val AUTOCONFIGURE = ":forgeboot-context-spring-boot-starter:forgeboot-context-autoconfigure"
|
const val AUTOCONFIGURE = ":forgeboot-context-spring-boot-starter:forgeboot-context-autoconfigure"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Webmvc模块:Spring Boot Starter对WebMVC的支持模块
|
||||||
|
* 提供Web开发相关的功能,包括数据传输对象(DTO)、验证、版本控制及日志功能
|
||||||
|
*/
|
||||||
object Webmvc {
|
object Webmvc {
|
||||||
const val STARTER = ":forgeboot-webmvc-spring-boot-starter"
|
const val STARTER = ":forgeboot-webmvc-spring-boot-starter"
|
||||||
const val DTO = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-dto"
|
const val DTO = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-dto"
|
||||||
|
const val EXCEPTION = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-exception-spring-boot-starter"
|
||||||
const val VALIDATION = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-validation"
|
const val VALIDATION = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-validation"
|
||||||
const val VERSION = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-version-spring-boot-starter"
|
const val VERSION = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-version-spring-boot-starter"
|
||||||
const val LOGGER = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-logger-spring-boot-starter"
|
const val LOGGER = ":forgeboot-webmvc-spring-boot-starter:forgeboot-webmvc-logger-spring-boot-starter"
|
||||||
}
|
}
|
||||||
object Core{
|
|
||||||
|
/**
|
||||||
|
* Core模块:项目的基础核心模块
|
||||||
|
* 包含项目的通用核心功能以及扩展支持
|
||||||
|
*/
|
||||||
|
object Core {
|
||||||
const val ROOT = ":forgeboot-core"
|
const val ROOT = ":forgeboot-core"
|
||||||
const val EXTENSION = ":forgeboot-core:forgeboot-core-extension"
|
const val EXTENSION = ":forgeboot-core:forgeboot-core-extension"
|
||||||
|
const val SERIALIZATION = ":forgeboot-core:forgeboot-core-serialization"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache模块:缓存支持模块
|
||||||
|
* 提供缓存功能,包含API、实现及自动配置模块
|
||||||
|
*/
|
||||||
|
object Cache {
|
||||||
|
const val STARTER = ":forgeboot-cache-spring-boot-starter"
|
||||||
|
const val API = ":forgeboot-cache-spring-boot-starter:forgeboot-cache-api"
|
||||||
|
const val IMPL = ":forgeboot-cache-spring-boot-starter:forgeboot-cache-impl"
|
||||||
|
const val AUTOCONFIGURE = ":forgeboot-cache-spring-boot-starter:forgeboot-cache-autoconfigure"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* I18n模块:国际化支持模块
|
||||||
|
* 提供多语言支持功能,包含API、实现及自动配置模块
|
||||||
|
*/
|
||||||
object I18n {
|
object I18n {
|
||||||
const val STARTER = ":forgeboot-i18n-spring-boot-starter"
|
const val STARTER = ":forgeboot-i18n-spring-boot-starter"
|
||||||
const val API = ":forgeboot-i18n-spring-boot-starter:forgeboot-i18n-api"
|
const val API = ":forgeboot-i18n-spring-boot-starter:forgeboot-i18n-api"
|
||||||
const val IMPL = ":forgeboot-i18n-spring-boot-starter:forgeboot-i18n-impl"
|
const val IMPL = ":forgeboot-i18n-spring-boot-starter:forgeboot-i18n-impl"
|
||||||
const val AUTOCONFIGURE = ":forgeboot-i18n-spring-boot-starter:forgeboot-i18n-autoconfigure"
|
const val AUTOCONFIGURE = ":forgeboot-i18n-spring-boot-starter:forgeboot-i18n-autoconfigure"
|
||||||
}
|
}
|
||||||
object TRACE{
|
|
||||||
|
/**
|
||||||
|
* TRACE模块:分布式链路追踪支持模块
|
||||||
|
* 提供请求链路追踪能力,包含API、实现及自动配置模块
|
||||||
|
*/
|
||||||
|
object TRACE {
|
||||||
const val STARTER = ":forgeboot-trace-spring-boot-starter"
|
const val STARTER = ":forgeboot-trace-spring-boot-starter"
|
||||||
const val API = ":forgeboot-trace-spring-boot-starter:forgeboot-trace-api"
|
const val API = ":forgeboot-trace-spring-boot-starter:forgeboot-trace-api"
|
||||||
const val IMPL = ":forgeboot-trace-spring-boot-starter:forgeboot-trace-impl"
|
const val IMPL = ":forgeboot-trace-spring-boot-starter:forgeboot-trace-impl"
|
||||||
const val AUTOCONFIGURE = ":forgeboot-trace-spring-boot-starter:forgeboot-trace-autoconfigure"
|
const val AUTOCONFIGURE = ":forgeboot-trace-spring-boot-starter:forgeboot-trace-autoconfigure"
|
||||||
}
|
}
|
||||||
object Banner {
|
|
||||||
|
/**
|
||||||
|
* Banner模块:启动横幅定制模块
|
||||||
|
* 负责自定义应用启动时显示的横幅信息
|
||||||
|
*/
|
||||||
|
object Banner {
|
||||||
const val STARTER = ":forgeboot-banner"
|
const val STARTER = ":forgeboot-banner"
|
||||||
const val API = ":forgeboot-banner:forgeboot-banner-api"
|
const val API = ":forgeboot-banner:forgeboot-banner-api"
|
||||||
const val IMPL = ":forgeboot-banner:forgeboot-banner-impl"
|
const val IMPL = ":forgeboot-banner:forgeboot-banner-impl"
|
||||||
const val AUTOCONFIGURE = ":forgeboot-banner:forgeboot-banner-autoconfigure"
|
const val AUTOCONFIGURE = ":forgeboot-banner:forgeboot-banner-autoconfigure"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security模块:安全认证与授权模块集合
|
||||||
|
* 包含认证(Authenticate)和授权(Authorize)两个子模块
|
||||||
|
*/
|
||||||
|
object Security {
|
||||||
|
private const val SECURITY = ":forgeboot-security-spring-boot-starter"
|
||||||
|
private const val AUTHENTICATE = "${SECURITY}:forgeboot-security-authenticate-spring-boot-starter"
|
||||||
|
private const val AUTHORIZE = "${SECURITY}:forgeboot-security-authorize-spring-boot-starter"
|
||||||
|
const val CORE = "${SECURITY}:forgeboot-security-core"
|
||||||
|
/**
|
||||||
|
* Authenticate模块:身份认证支持模块
|
||||||
|
* 提供用户身份认证相关功能,包含API、实现及自动配置模块
|
||||||
|
*/
|
||||||
|
object Authenticate {
|
||||||
|
const val STARTER = AUTHENTICATE
|
||||||
|
const val API = "${AUTHENTICATE}:forgeboot-security-authenticate-api"
|
||||||
|
const val IMPL = "${AUTHENTICATE}:forgeboot-security-authenticate-impl"
|
||||||
|
const val AUTOCONFIGURE =
|
||||||
|
"${AUTHENTICATE}:forgeboot-security-authenticate"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorize模块:权限控制支持模块
|
||||||
|
* 提供基于角色或策略的权限控制功能,包含API、实现及自动配置模块
|
||||||
|
*/
|
||||||
|
object Authorize {
|
||||||
|
const val STARTER = AUTHORIZE
|
||||||
|
const val API = "${AUTHORIZE}:forgeboot-security-authorize-api"
|
||||||
|
const val IMPL = "${AUTHORIZE}:forgeboot-security-authorize-impl"
|
||||||
|
const val AUTOCONFIGURE =
|
||||||
|
"${AUTHORIZE}:forgeboot-security-authorize-autoconfigure"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
5
forgeboot-security/build.gradle.kts
Normal file
5
forgeboot-security/build.gradle.kts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
extra{
|
||||||
|
setProperty(ProjectFlags.IS_ROOT_MODULE,true)
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(project(Modules.Webmvc.DTO))
|
||||||
|
compileOnly(project(Modules.Security.CORE))
|
||||||
|
compileOnly(libs.springBootStarter.web)
|
||||||
|
compileOnly(libs.springBootStarter.security)
|
||||||
|
kapt(libs.springBoot.configuration.processor)
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.config
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
import org.springframework.http.HttpMethod
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全身份验证属性配置类
|
||||||
|
*
|
||||||
|
* 该类用于定义安全认证相关的配置属性,包含路径权限设置、异常响应信息和基础URL配置。
|
||||||
|
* 属性值通过@ConfigurationProperties绑定前缀"forgeboot.security.authenticate"的配置项。
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 15:20:27
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(prefix = "forgeboot.security.authenticate")
|
||||||
|
class SecurityAuthenticateProperties {
|
||||||
|
/**
|
||||||
|
* 登录请求的HTTP方法,默认为POST
|
||||||
|
*/
|
||||||
|
var loginHttpMethod: String = HttpMethod.POST.name()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认认证异常响应内容,当请求缺少认证时返回此字符串
|
||||||
|
*/
|
||||||
|
var defaultExceptionResponse = "Full authentication is required to access this resource"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认认证失败响应内容,当认证失败时返回此字符串
|
||||||
|
*/
|
||||||
|
var defaultAuthenticationFailureResponse = "If the authentication fails, please report the request ID for more information!"
|
||||||
|
/**
|
||||||
|
* 认证模块的基础URL前缀
|
||||||
|
*/
|
||||||
|
var baseUrl = "/api/auth"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录接口的URL路径
|
||||||
|
*/
|
||||||
|
var loginUrl = "/login"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登出接口的URL路径
|
||||||
|
*/
|
||||||
|
var logoutUrl = "/logout"
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.customizer
|
||||||
|
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录过滤器定制器接口
|
||||||
|
*
|
||||||
|
* 该函数式接口用于自定义Spring Security的登录过滤器配置。
|
||||||
|
* 实现此接口的类或lambda表达式可用于调整安全配置中的HttpSecurity设置,
|
||||||
|
* 特别是在登录认证流程中对请求的安全策略进行定制化处理。
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 18:53:25
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface LoginFilterCustomizer {
|
||||||
|
/**
|
||||||
|
* 自定义方法,用于配置或修改HttpSecurity对象。
|
||||||
|
*
|
||||||
|
* 此方法将被调用以应用特定的安全性规则到登录过滤器上。
|
||||||
|
*
|
||||||
|
* @param http HttpSecurity对象,提供了配置Web安全性的API
|
||||||
|
*/
|
||||||
|
fun customize(http: HttpSecurity)
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.enums
|
||||||
|
|
||||||
|
/**
|
||||||
|
*登录类型
|
||||||
|
*
|
||||||
|
* @since 2025-06-12 21:11:02
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
object LoginTypes {
|
||||||
|
/**
|
||||||
|
* 默认登录类型标识
|
||||||
|
*/
|
||||||
|
const val DEFAULT = "default"
|
||||||
|
/**
|
||||||
|
* 用户名密码登录类型标识
|
||||||
|
*/
|
||||||
|
const val USERNAME_PASSWORD = "username_password"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth2 认证登录类型标识
|
||||||
|
*/
|
||||||
|
const val OAUTH2 = "oauth2"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信验证码登录类型标识
|
||||||
|
*/
|
||||||
|
const val SMS = "sms"
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.exception
|
||||||
|
|
||||||
|
import org.springframework.security.core.AuthenticationException
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*Forge Boot身份验证异常
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 15:49:33
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
open class ForgeBootAuthenticationException(
|
||||||
|
msg: String,
|
||||||
|
cause: Throwable? = null
|
||||||
|
): AuthenticationException(
|
||||||
|
msg,
|
||||||
|
cause
|
||||||
|
)
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.handler
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 委派处理程序接口,用于定义委派处理器的行为规范。
|
||||||
|
*
|
||||||
|
* 该接口设计为泛型函数式接口,要求实现类提供一个方法,
|
||||||
|
* 将给定的 HTTP 请求解析为指定类型的委派对象。
|
||||||
|
*
|
||||||
|
* @param <T> 委派对象的类型,必须是非空类型
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 14:51:48
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface DelegatingHandler<T: Any> {
|
||||||
|
/**
|
||||||
|
* 根据提供的 HTTP 请求解析出对应的委派对象。
|
||||||
|
*
|
||||||
|
* 此方法通常用于从请求中提取认证信息或上下文数据,
|
||||||
|
* 并将其转换为具体的业务对象以供后续处理使用。
|
||||||
|
*
|
||||||
|
* @param request 要解析的 HTTP 请求对象,不能为 null
|
||||||
|
* @return 返回解析得到的委派对象,具体类型由接口泛型 T 决定
|
||||||
|
*/
|
||||||
|
fun resolveDelegate(request: HttpServletRequest): T
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.registry
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.core.authenticate.entities.request.LoginRequest
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录请求类型注册表
|
||||||
|
*
|
||||||
|
* 用于管理不同登录类型的请求类映射关系,支持动态注册和获取登录请求类型。
|
||||||
|
*
|
||||||
|
* ```kt
|
||||||
|
* @Bean
|
||||||
|
* fun loginRequestTypeRegistry(): LoginRequestTypeRegistry {
|
||||||
|
* return LoginRequestTypeRegistry().apply {
|
||||||
|
* register("default", DefaultLoginRequest::class.java)
|
||||||
|
* register("sms", SmsLoginRequest::class.java)
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* @since 2025-06-10 16:33:43
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class LoginRequestTypeRegistry {
|
||||||
|
/**
|
||||||
|
* 内部使用可变Map保存登录类型与对应LoginRequest子类的映射关系
|
||||||
|
*/
|
||||||
|
private val mapping = mutableMapOf<String, Class<out LoginRequest>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册指定登录类型对应的请求类
|
||||||
|
*
|
||||||
|
* @param loginType 登录类型的标识字符串
|
||||||
|
* @param clazz 继承自LoginRequest的具体请求类
|
||||||
|
*/
|
||||||
|
fun register(loginType: String, clazz: Class<out LoginRequest>) {
|
||||||
|
mapping[loginType] = clazz
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据登录类型获取对应的请求类
|
||||||
|
*
|
||||||
|
* @param loginType 登录类型的标识字符串
|
||||||
|
* @return 返回对应的LoginRequest子类,若未找到则返回null
|
||||||
|
*/
|
||||||
|
fun getTypeForLoginType(loginType: String): Class<out LoginRequest>? {
|
||||||
|
return mapping[loginType]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.resolver
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.core.authenticate.entities.request.LoginRequest
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录请求解析器接口
|
||||||
|
* 用于定义处理登录请求的通用规范,包括内容类型支持判断和请求参数解析功能
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 16:32:32
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface LoginRequestResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前解析器是否支持指定的内容类型
|
||||||
|
*
|
||||||
|
* @param contentType 请求内容类型
|
||||||
|
* @return Boolean 返回true表示支持该内容类型,否则不支持
|
||||||
|
*/
|
||||||
|
fun supports(contentType: String): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析HTTP请求为具体的登录请求对象
|
||||||
|
* 该方法应从HttpServletRequest中提取并封装登录所需参数
|
||||||
|
*
|
||||||
|
* @param request HTTP请求对象
|
||||||
|
* @return LoginRequest 解析后的登录请求实体对象
|
||||||
|
*/
|
||||||
|
fun resolve(request: HttpServletRequest): LoginRequest
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.service
|
||||||
|
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*用户缓存服务
|
||||||
|
*
|
||||||
|
* @since 2025-02-15 17:15:17
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface UserCacheService {
|
||||||
|
/**
|
||||||
|
* 从缓存中获取用户信息
|
||||||
|
* @param principal 用户标识
|
||||||
|
* @return 用户信息 返回null表示缓存中没有该用户信息或信息已过期
|
||||||
|
*/
|
||||||
|
fun getUserFromCache(principal: String): UserDetails?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将用户信息缓存到缓存中 注意,请将过期时间设置得尽可能短,以防止缓存与数据库出现数据不一致
|
||||||
|
* @param userDetails 用户信息
|
||||||
|
*/
|
||||||
|
fun putUserToCache(userDetails: UserDetails)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从缓存中移除用户信息
|
||||||
|
* @param principal 用户标识
|
||||||
|
*/
|
||||||
|
fun removeUserFromCache(principal: String)
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.service
|
||||||
|
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户详情服务
|
||||||
|
*
|
||||||
|
* @author gewuyou
|
||||||
|
* @since 2024-11-05 19:34:50
|
||||||
|
*/
|
||||||
|
fun interface UserDetailsService {
|
||||||
|
/**
|
||||||
|
* 根据用户身份标识获取用户
|
||||||
|
* @param principal 用户身份标识 通常为用户名 手机号 邮箱等
|
||||||
|
* @return 用户详情 不能为空
|
||||||
|
*/
|
||||||
|
fun loadUserByPrincipal(principal: Any): UserDetails
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.strategy
|
||||||
|
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 身份验证失败策略接口,用于定义不同登录类型的身份验证失败处理机制。
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 23:43:43
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface AuthenticationFailureStrategy : AuthenticationHandlerSupportStrategy, AuthenticationFailureHandler
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.strategy
|
||||||
|
|
||||||
|
/**
|
||||||
|
*身份验证处理程序支持策略
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 00:03:28
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface AuthenticationHandlerSupportStrategy{
|
||||||
|
/**
|
||||||
|
* 获取当前策略支持的登录类型标识符。
|
||||||
|
*
|
||||||
|
* @return 返回当前策略支持的登录类型字符串
|
||||||
|
*/
|
||||||
|
fun supportedLoginType(): String
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.strategy
|
||||||
|
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 身份验证成功策略接口,用于定义不同登录类型的成功处理策略。
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 23:42:54
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface AuthenticationSuccessStrategy : AuthenticationHandlerSupportStrategy, AuthenticationSuccessHandler
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.strategy
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.core.authenticate.entities.request.LoginRequest
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
|
||||||
|
/**
|
||||||
|
*登录请求转换器策略
|
||||||
|
* 请注意,该接口仅用于定义转换器的接口只负责转换登录请求为认证对象,不负责提供认证逻辑。
|
||||||
|
* 具体的认证逻辑由具体的认证提供类实现。因此当需要自定义认证逻辑时,请实现具体的认证提供类。
|
||||||
|
* @since 2025-02-15 03:23:34
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface LoginRequestConverterStrategy {
|
||||||
|
/**
|
||||||
|
* 转换登录请求为认证对象
|
||||||
|
* @param loginRequest 登录请求
|
||||||
|
* @return 认证对象
|
||||||
|
*/
|
||||||
|
fun convert(loginRequest: LoginRequest): Authentication
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取支持的登录类型
|
||||||
|
* @return 请求的登录类型
|
||||||
|
*/
|
||||||
|
fun getSupportedLoginType(): String
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.api.strategy
|
||||||
|
|
||||||
|
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销成功策略接口,用于定义在用户注销成功后的处理逻辑。
|
||||||
|
*
|
||||||
|
* 实现此类的策略应当提供具体地登出后操作,例如清理会话、记录日志等。
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 23:49:35
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface LogoutSuccessStrategy : AuthenticationHandlerSupportStrategy, LogoutSuccessHandler
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlin.plugin.spring)
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
compileOnly(libs.springBootStarter.security)
|
||||||
|
compileOnly(libs.springBootStarter.web)
|
||||||
|
compileOnly(project(Modules.Security.Authenticate.IMPL))
|
||||||
|
implementation(project(Modules.Security.CORE))
|
||||||
|
}
|
||||||
@ -0,0 +1,309 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.autoconfigure
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.config.SecurityAuthenticateProperties
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.customizer.LoginFilterCustomizer
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.resolver.LoginRequestResolver
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.AuthenticationFailureStrategy
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.AuthenticationSuccessStrategy
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.LogoutSuccessStrategy
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.extension.cleanUnNeedConfig
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.filter.UnifiedAuthenticationFilter
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.handler.AuthenticationExceptionHandler
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.handler.CompositeAuthenticationFailureHandler
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.handler.CompositeAuthenticationSuccessHandler
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.handler.CompositeLogoutSuccessHandler
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.resolver.CompositeLoginRequestResolver
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.context.AuthenticationFailureHandlerContext
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.context.AuthenticationSuccessHandlerContext
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.context.LoginRequestConverterContext
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.context.LogoutSuccessHandlerContext
|
||||||
|
import org.springframework.beans.factory.ObjectProvider
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
|
import org.springframework.context.ApplicationContext
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.ComponentScan
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.context.annotation.Primary
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager
|
||||||
|
import org.springframework.security.authentication.AuthenticationProvider
|
||||||
|
import org.springframework.security.authentication.ProviderManager
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint
|
||||||
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全身份验证自动配置
|
||||||
|
*
|
||||||
|
* 主要用于配置与安全认证相关的 Bean 和 Spring Security 的过滤器链。
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 15:04:58
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(SecurityAuthenticateProperties::class)
|
||||||
|
class SecurityAuthenticateAutoConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Security 认证流程的自动配置类。
|
||||||
|
*
|
||||||
|
* 该内部类负责构建主要的安全过滤器链,处理登录、登出以及异常入口点等核心功能。
|
||||||
|
*
|
||||||
|
* @property logoutSuccessHandler 处理登出成功的处理器
|
||||||
|
* @property authenticationExceptionHandler 处理认证异常的入口点
|
||||||
|
* @property unifiedAuthenticationFilter 统一的身份验证过滤器
|
||||||
|
* @property customizers 可选的登录过滤器定制器集合
|
||||||
|
*/
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@ConditionalOnClass(SecurityFilterChain::class)
|
||||||
|
class SpringSecurityAuthenticateAutoConfiguration(
|
||||||
|
private val logoutSuccessHandler: CompositeLogoutSuccessHandler,
|
||||||
|
private val authenticationExceptionHandler: AuthenticationEntryPoint,
|
||||||
|
private val unifiedAuthenticationFilter: UnifiedAuthenticationFilter,
|
||||||
|
private val customizers: ObjectProvider<List<LoginFilterCustomizer>>,
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建并返回一个自定义的安全过滤器链。
|
||||||
|
*
|
||||||
|
* 该方法配置了:
|
||||||
|
* - 清除不必要地默认安全配置
|
||||||
|
* - 登出功能及其成功处理器
|
||||||
|
* - 异常处理(认证失败入口点)
|
||||||
|
* - 请求匹配规则和访问控制策略
|
||||||
|
* - 添加统一的身份验证过滤器到过滤器链中
|
||||||
|
* - 对可排序的登录过滤器定制器进行排序并应用
|
||||||
|
*
|
||||||
|
* @param http HttpSecurity 实例,用于构建安全过滤器链
|
||||||
|
* @param properties 安全认证属性配置对象
|
||||||
|
* @return 构建完成的 SecurityFilterChain 实例
|
||||||
|
*/
|
||||||
|
@Bean(name = ["forgebootSecurityAuthenticationChain"])
|
||||||
|
fun loginFilterChain(
|
||||||
|
http: HttpSecurity,
|
||||||
|
properties: SecurityAuthenticateProperties,
|
||||||
|
): SecurityFilterChain {
|
||||||
|
return http
|
||||||
|
.cleanUnNeedConfig()
|
||||||
|
.logout {
|
||||||
|
it.logoutUrl(properties.logoutUrl)
|
||||||
|
.logoutSuccessHandler(logoutSuccessHandler)
|
||||||
|
}
|
||||||
|
.exceptionHandling {
|
||||||
|
it.authenticationEntryPoint(authenticationExceptionHandler)
|
||||||
|
}
|
||||||
|
.securityMatcher(AntPathRequestMatcher(properties.baseUrl))
|
||||||
|
.addFilterBefore(unifiedAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java)
|
||||||
|
.also {
|
||||||
|
customizers.ifAvailable?.sortedBy { c -> (c as? OrderedLoginFilterCustomizer)?.order ?: 0 }
|
||||||
|
?.forEach { it.customize(http) }
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ForgeBoot安全组件的自动配置类。
|
||||||
|
*
|
||||||
|
* 负责注册多个用于处理身份验证流程的组合式组件,如解析器、上下文、处理器等。
|
||||||
|
*/
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
class ForgeBootSecurityAuthenticateAutoConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个组合式的登录请求解析器。
|
||||||
|
*
|
||||||
|
* 将多个 LoginRequestResolver 实现组合成一个统一的接口调用入口。
|
||||||
|
*
|
||||||
|
* @param resolvers 所有可用的 LoginRequestResolver 实例列表
|
||||||
|
* @return 组合后的 CompositeLoginRequestResolver 实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
fun compositeLoginRequestResolver(
|
||||||
|
resolvers: List<LoginRequestResolver>,
|
||||||
|
) = CompositeLoginRequestResolver(resolvers)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个登出成功处理的上下文实例。
|
||||||
|
*
|
||||||
|
* 用于根据注册的策略动态选择合适的 LogoutSuccessStrategy。
|
||||||
|
*
|
||||||
|
* @param strategies 所有可用的 LogoutSuccessStrategy 实例列表
|
||||||
|
* @return 初始化好的 LogoutSuccessHandlerContext 实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
fun logoutSuccessHandlerContext(
|
||||||
|
strategies: List<LogoutSuccessStrategy>,
|
||||||
|
) = LogoutSuccessHandlerContext(strategies)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个组合式的登出成功处理器。
|
||||||
|
*
|
||||||
|
* 使用解析器和上下文来决定最终使用的登出成功策略。
|
||||||
|
*
|
||||||
|
* @param resolver 登录请求解析器
|
||||||
|
* @param context 登出成功处理上下文
|
||||||
|
* @return 初始化好的 CompositeLogoutSuccessHandler 实例
|
||||||
|
*/
|
||||||
|
@Bean(name = ["logoutSuccessHandler"])
|
||||||
|
fun compositeLogoutSuccessHandler(
|
||||||
|
resolver: CompositeLoginRequestResolver,
|
||||||
|
context: LogoutSuccessHandlerContext,
|
||||||
|
) = CompositeLogoutSuccessHandler(resolver, context)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个认证异常处理器。
|
||||||
|
*
|
||||||
|
* 当用户未认证或认证失败时,通过此处理器响应客户端。
|
||||||
|
*
|
||||||
|
* @param objectMapper JSON 序列化工具
|
||||||
|
* @param properties 安全认证属性配置
|
||||||
|
* @return 初始化好的 AuthenticationExceptionHandler 实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
fun authenticationExceptionHandler(
|
||||||
|
objectMapper: ObjectMapper,
|
||||||
|
properties: SecurityAuthenticateProperties,
|
||||||
|
): AuthenticationEntryPoint = AuthenticationExceptionHandler(objectMapper, properties)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个认证管理器。
|
||||||
|
*
|
||||||
|
* 使用一组 AuthenticationProvider 来支持多种认证方式。
|
||||||
|
*
|
||||||
|
* @param authenticationProviders 所有可用的认证提供者
|
||||||
|
* @return 初始化好的 ProviderManager 实例
|
||||||
|
*/
|
||||||
|
@Bean(name = ["authenticationManager"])
|
||||||
|
@Primary
|
||||||
|
fun authenticationManager(
|
||||||
|
authenticationProviders: List<AuthenticationProvider>,
|
||||||
|
): AuthenticationManager = ProviderManager(authenticationProviders)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个认证成功处理的上下文。
|
||||||
|
*
|
||||||
|
* 根据当前请求上下文动态选择合适的认证成功策略。
|
||||||
|
*
|
||||||
|
* @param strategies 所有可用的 AuthenticationSuccessStrategy 实例列表
|
||||||
|
* @return 初始化好的 AuthenticationSuccessHandlerContext 实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
fun authenticationSuccessHandlerContext(
|
||||||
|
strategies: List<AuthenticationSuccessStrategy>,
|
||||||
|
) = AuthenticationSuccessHandlerContext(strategies)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个组合式的认证成功处理器。
|
||||||
|
*
|
||||||
|
* 委托给具体的策略实现来进行响应。
|
||||||
|
*
|
||||||
|
* @param resolver 登录请求解析器
|
||||||
|
* @param context 认证成功处理上下文
|
||||||
|
* @return 初始化好的 CompositeAuthenticationSuccessHandler 实例
|
||||||
|
*/
|
||||||
|
@Bean(name = ["authenticationSuccessHandler"])
|
||||||
|
fun authenticationSuccessHandler(
|
||||||
|
resolver: CompositeLoginRequestResolver,
|
||||||
|
context: AuthenticationSuccessHandlerContext,
|
||||||
|
): AuthenticationSuccessHandler = CompositeAuthenticationSuccessHandler(resolver, context)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个认证失败处理的上下文。
|
||||||
|
*
|
||||||
|
* 根据当前请求上下文动态选择合适的认证失败策略。
|
||||||
|
*
|
||||||
|
* @param strategies 所有可用的 AuthenticationFailureStrategy 实例列表
|
||||||
|
* @return 初始化好的 AuthenticationFailureHandlerContext 实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
fun authenticationFailureHandlerContext(
|
||||||
|
strategies: List<AuthenticationFailureStrategy>,
|
||||||
|
) = AuthenticationFailureHandlerContext(strategies)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个组合式的认证失败处理器。
|
||||||
|
*
|
||||||
|
* 委托给具体的策略实现来进行响应。
|
||||||
|
*
|
||||||
|
* @param resolver 登录请求解析器
|
||||||
|
* @param context 认证失败处理上下文
|
||||||
|
* @return 初始化好的 CompositeAuthenticationFailureHandler 实例
|
||||||
|
*/
|
||||||
|
@Bean(name = ["authenticationFailureHandler"])
|
||||||
|
fun authenticationFailureHandler(
|
||||||
|
resolver: CompositeLoginRequestResolver,
|
||||||
|
context: AuthenticationFailureHandlerContext,
|
||||||
|
): AuthenticationFailureHandler = CompositeAuthenticationFailureHandler(resolver, context)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个登录请求转换上下文。
|
||||||
|
*
|
||||||
|
* 用于根据当前请求上下文动态选择合适的登录请求转换逻辑。
|
||||||
|
*
|
||||||
|
* @param applicationContext Spring 应用上下文
|
||||||
|
* @return 初始化好的 LoginRequestConverterContext 实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
fun loginRequestConverterContext(
|
||||||
|
applicationContext: ApplicationContext,
|
||||||
|
) = LoginRequestConverterContext(applicationContext)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个统一的身份验证过滤器。
|
||||||
|
*
|
||||||
|
* 该过滤器是整个认证流程的核心,处理登录请求并触发相应的成功/失败处理器。
|
||||||
|
*
|
||||||
|
* @param properties 安全认证属性配置
|
||||||
|
* @param authenticationManager 认证管理器
|
||||||
|
* @param authenticationSuccessHandler 成功认证处理器
|
||||||
|
* @param authenticationFailureHandler 失败认证处理器
|
||||||
|
* @param compositeLoginRequestResolver 组合登录请求解析器
|
||||||
|
* @param loginRequestConverterContext 登录请求转换上下文
|
||||||
|
* @return 初始化好的 UnifiedAuthenticationFilter 实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
fun unifiedAuthenticationFilter(
|
||||||
|
properties: SecurityAuthenticateProperties,
|
||||||
|
authenticationManager: AuthenticationManager,
|
||||||
|
authenticationSuccessHandler: AuthenticationSuccessHandler,
|
||||||
|
authenticationFailureHandler: AuthenticationFailureHandler,
|
||||||
|
compositeLoginRequestResolver: CompositeLoginRequestResolver,
|
||||||
|
loginRequestConverterContext: LoginRequestConverterContext,
|
||||||
|
): UnifiedAuthenticationFilter =
|
||||||
|
UnifiedAuthenticationFilter(
|
||||||
|
AntPathRequestMatcher(
|
||||||
|
properties.baseUrl + properties.loginUrl,
|
||||||
|
properties.loginHttpMethod
|
||||||
|
),
|
||||||
|
authenticationManager,
|
||||||
|
authenticationSuccessHandler,
|
||||||
|
authenticationFailureHandler,
|
||||||
|
compositeLoginRequestResolver,
|
||||||
|
loginRequestConverterContext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@ComponentScan(
|
||||||
|
basePackages = [
|
||||||
|
"com.gewuyou.forgeboot.security.authenticate.impl.strategy.impl",
|
||||||
|
"com.gewuyou.forgeboot.security.authenticate.impl.provider.impl",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
class ForgeBootDefaultSecurityAuthenticateAutoConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于支持登录过滤器扩展器排序
|
||||||
|
*
|
||||||
|
* 通过 order 属性为 LoginFilterCustomizer 提供排序能力,确保其按预期顺序执行。
|
||||||
|
*/
|
||||||
|
interface OrderedLoginFilterCustomizer : LoginFilterCustomizer {
|
||||||
|
val order: Int
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
com.gewuyou.forgeboot.security.authenticate.autoconfigure.SecurityAuthenticateAutoConfiguration
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
extra{
|
||||||
|
setProperty(ProjectFlags.IS_ROOT_MODULE,true)
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
plugins{
|
||||||
|
alias(libs.plugins.kotlin.plugin.spring)
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
api(project(Modules.Security.Authenticate.API))
|
||||||
|
implementation(project(Modules.Security.CORE))
|
||||||
|
implementation(project(Modules.Core.EXTENSION))
|
||||||
|
implementation(project(Modules.Webmvc.DTO))
|
||||||
|
compileOnly(libs.springBootStarter.web)
|
||||||
|
compileOnly(libs.springBootStarter.security)
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.constants
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forge Boot安全身份验证响应信息
|
||||||
|
*
|
||||||
|
* 该对象包含用于身份验证过程中的各种预定义错误响应消息。
|
||||||
|
* 每个属性代表一种特定的身份验证失败或系统错误情况,供在认证流程中抛出或返回给调用者。
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 17:05:09
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
object ForgeBootSecurityAuthenticationResponseInformation {
|
||||||
|
const val INCORRECT_USERNAME_OR_PASSWORD: String = "the username or password is incorrect"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当用户未提供密码时使用的响应消息。
|
||||||
|
*/
|
||||||
|
const val PASSWORD_NOT_PROVIDED: String = "the password cannot be empty"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当用户的凭证已过期时使用的响应消息。
|
||||||
|
*/
|
||||||
|
const val ACCOUNT_CREDENTIALS_HAVE_EXPIRED: String = "the account credentials have expired"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当用户账户被禁用时使用的响应消息。
|
||||||
|
*/
|
||||||
|
const val ACCOUNT_IS_DISABLED: String = "the account is disabled"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当用户账户被锁定时使用的响应消息。
|
||||||
|
*/
|
||||||
|
const val ACCOUNT_IS_LOCKED: String = "the account is locked"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当发生内部服务器错误时使用的通用错误响应消息。
|
||||||
|
*/
|
||||||
|
const val INTERNAL_SERVER_ERROR: String = "internal server error occurred"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当认证主体(principal)未提供时使用的常量字符串。
|
||||||
|
* 这通常意味着认证请求缺少必要的身份标识信息。
|
||||||
|
*/
|
||||||
|
const val PRINCIPAL_NOT_PROVIDED = "the authentication principal cannot be empty"
|
||||||
|
|
||||||
|
const val LOGIN_SUCCESS: String = "login success"
|
||||||
|
|
||||||
|
const val LOGOUT_SUCCESS: String = "logout success"
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.filter
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.resolver.CompositeLoginRequestResolver
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.context.LoginRequestConverterContext
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一的身份验证过滤器
|
||||||
|
*
|
||||||
|
* 该类实现了身份验证流程的核心逻辑,通过组合不同的解析策略和转换上下文,
|
||||||
|
* 来支持多种登录请求的处理方式。
|
||||||
|
*
|
||||||
|
* @param pathRequestMatcher 请求路径匹配器,用于判断当前请求是否需要处理
|
||||||
|
* @param authenticationManager 身份验证管理器,用于执行实际的身份验证操作
|
||||||
|
* @param authenticationSuccessHandler 认证成功处理器,用于处理认证成功后的响应
|
||||||
|
* @param authenticationFailureHandler 认证失败处理器,用于处理认证失败后的响应
|
||||||
|
* @param compositeLoginRequestResolver 组合登录请求解析器,用于解析不同类型的登录请求
|
||||||
|
* @param loginRequestConverterContext 登录请求转换上下文,用于选择合适地转换策略
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 20:28:08
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class UnifiedAuthenticationFilter(
|
||||||
|
pathRequestMatcher: AntPathRequestMatcher,
|
||||||
|
authenticationManager: AuthenticationManager,
|
||||||
|
authenticationSuccessHandler: AuthenticationSuccessHandler,
|
||||||
|
authenticationFailureHandler: AuthenticationFailureHandler,
|
||||||
|
private val compositeLoginRequestResolver: CompositeLoginRequestResolver,
|
||||||
|
private val loginRequestConverterContext: LoginRequestConverterContext,
|
||||||
|
) : AbstractAuthenticationProcessingFilter(pathRequestMatcher) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
// 初始化身份验证管理器、成功处理器和失败处理器
|
||||||
|
setAuthenticationManager(authenticationManager)
|
||||||
|
setAuthenticationSuccessHandler(authenticationSuccessHandler)
|
||||||
|
setAuthenticationFailureHandler(authenticationFailureHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试进行身份验证
|
||||||
|
*
|
||||||
|
* 此方法负责从请求中提取登录信息,并使用组合解析器和转换上下文来构建认证请求。
|
||||||
|
* 最终调用身份验证管理器执行认证操作。
|
||||||
|
*
|
||||||
|
* @param request HTTP请求对象
|
||||||
|
* @param response HTTP响应对象
|
||||||
|
* @return 返回经过认证的Authentication对象
|
||||||
|
*/
|
||||||
|
override fun attemptAuthentication(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
): Authentication {
|
||||||
|
// 尝试认证:通过组合解析器解析请求内容,再根据转换上下文选择合适的策略进行认证
|
||||||
|
return authenticationManager.authenticate(
|
||||||
|
// 通过上下文执行转换策略
|
||||||
|
loginRequestConverterContext.executeStrategy(
|
||||||
|
// 使用组合解析器解析HTTP请求
|
||||||
|
compositeLoginRequestResolver.resolve(request)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.handler
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.handler.DelegatingHandler
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.resolver.CompositeLoginRequestResolver
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象委派处理程序
|
||||||
|
*
|
||||||
|
* 该类为泛型抽象类,用于实现委派处理逻辑,通过解析请求获取登录类型,并使用上下文解析器获取对应的处理上下文。
|
||||||
|
*
|
||||||
|
* @param resolver 用于解析请求并获取登录类型的组件,不可为空
|
||||||
|
* @param contextResolver 根据登录类型生成对应上下文对象的函数式接口,不可为空
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 14:53:46
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
abstract class AbstractDelegatingHandler<T : Any>(
|
||||||
|
private val resolver: CompositeLoginRequestResolver,
|
||||||
|
private val contextResolver: (String) -> T,
|
||||||
|
): DelegatingHandler<T> {
|
||||||
|
/**
|
||||||
|
* 解析请求并获取对应的处理上下文
|
||||||
|
*
|
||||||
|
* 该方法会调用注入的 [resolver] 来解析传入的 HTTP 请求,获取当前请求的登录类型,
|
||||||
|
* 然后通过 [contextResolver] 函数将登录类型转换为具体地处理上下文对象。
|
||||||
|
*
|
||||||
|
* @param request HTTP 请求对象,用于解析登录类型
|
||||||
|
* @return 返回与登录类型对应的处理上下文对象
|
||||||
|
*/
|
||||||
|
override fun resolveDelegate(request: HttpServletRequest): T {
|
||||||
|
// 解析请求获取登录类型
|
||||||
|
val loginType = resolver.resolve(request).getType()
|
||||||
|
// 使用上下文解析器生成对应类型的处理上下文
|
||||||
|
return contextResolver(loginType)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.handler
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.core.extension.log
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.config.SecurityAuthenticateProperties
|
||||||
|
import com.gewuyou.forgeboot.webmvc.dto.R
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.security.core.AuthenticationException
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint
|
||||||
|
|
||||||
|
/**
|
||||||
|
*身份验证异常处理程序
|
||||||
|
*
|
||||||
|
* @since 2025-01-03 14:50:46
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class AuthenticationExceptionHandler(
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val securityAuthenticateProperties: SecurityAuthenticateProperties
|
||||||
|
) : AuthenticationEntryPoint {
|
||||||
|
override fun commence(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
authException: AuthenticationException
|
||||||
|
) {
|
||||||
|
log.error("身份验证异常: ", authException)
|
||||||
|
response.status = HttpStatus.OK.value()
|
||||||
|
response.contentType = MediaType.APPLICATION_JSON_VALUE
|
||||||
|
val writer = response.writer
|
||||||
|
writer.print(objectMapper.writeValueAsString(R.failure<String>(HttpStatus.UNAUTHORIZED.value(), authException.message?:securityAuthenticateProperties.defaultExceptionResponse)))
|
||||||
|
writer.flush()
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.handler
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.resolver.CompositeLoginRequestResolver
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.context.AuthenticationFailureHandlerContext
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组合身份验证故障处理程序
|
||||||
|
*
|
||||||
|
* 该类通过组合不同的身份验证失败处理器来处理认证失败场景,利用解析器和上下文策略来动态选择实际的处理器。
|
||||||
|
*
|
||||||
|
* @property resolver 用于解析请求并确定适用的身份验证失败处理器的组件
|
||||||
|
* @property context 提供身份验证失败处理策略的上下文对象
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 00:11:52
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class CompositeAuthenticationFailureHandler(
|
||||||
|
private val resolver: CompositeLoginRequestResolver,
|
||||||
|
private val context: AuthenticationFailureHandlerContext,
|
||||||
|
) : AbstractDelegatingHandler<AuthenticationFailureHandler>(
|
||||||
|
resolver, context::resolve
|
||||||
|
), AuthenticationFailureHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在身份验证失败时调用的方法
|
||||||
|
*
|
||||||
|
* 此方法会解析出适用于当前请求的具体身份验证失败处理器,并委托其进行失败处理。
|
||||||
|
*
|
||||||
|
* @param request 来自客户端的 HTTP 请求
|
||||||
|
* @param response 发送回客户端的 HTTP 响应
|
||||||
|
* @param exception 身份验证过程中抛出的异常信息
|
||||||
|
*/
|
||||||
|
override fun onAuthenticationFailure(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
exception: org.springframework.security.core.AuthenticationException,
|
||||||
|
) {
|
||||||
|
resolveDelegate(request).onAuthenticationFailure(request, response, exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.handler
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.resolver.CompositeLoginRequestResolver
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.context.AuthenticationSuccessHandlerContext
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一身份验证成功处理程序
|
||||||
|
*
|
||||||
|
* 该类实现了 [AuthenticationSuccessHandler] 接口,用于在身份验证成功后执行相应的处理逻辑。
|
||||||
|
* 它通过组合多个具体的处理器来实现灵活的身份验证成功响应策略。
|
||||||
|
*
|
||||||
|
* @property resolver 用于解析登录请求并确定使用哪个具体的成功处理器
|
||||||
|
* @property context 提供处理上下文信息,用于解析和执行成功处理逻辑
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 23:48:32
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class CompositeAuthenticationSuccessHandler(
|
||||||
|
private val resolver: CompositeLoginRequestResolver,
|
||||||
|
private val context: AuthenticationSuccessHandlerContext,
|
||||||
|
) : AbstractDelegatingHandler<AuthenticationSuccessHandler>(
|
||||||
|
resolver, context::resolve
|
||||||
|
), AuthenticationSuccessHandler {
|
||||||
|
/**
|
||||||
|
* 身份验证成功时的回调方法
|
||||||
|
*
|
||||||
|
* 此方法会在用户成功通过身份验证后被调用。它使用 [resolveDelegate] 方法根据当前请求解析出
|
||||||
|
* 合适的具体处理器,并委托给该处理器执行后续操作。
|
||||||
|
*
|
||||||
|
* @param request HTTP 请求对象,提供客户端的请求信息
|
||||||
|
* @param response HTTP 响应对象,用于向客户端发送响应
|
||||||
|
* @param authentication 包含身份验证成功的用户信息和权限等数据的对象
|
||||||
|
*/
|
||||||
|
override fun onAuthenticationSuccess(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
authentication: Authentication,
|
||||||
|
) {
|
||||||
|
resolveDelegate(request).onAuthenticationSuccess(request, response, authentication)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.handler
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.resolver.CompositeLoginRequestResolver
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.context.LogoutSuccessHandlerContext
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组合注销成功处理程序
|
||||||
|
*
|
||||||
|
* 该类通过组合多种注销成功处理器实现统一的注销成功处理逻辑。
|
||||||
|
*
|
||||||
|
* @property resolver 用于解析请求并确定适用的注销成功处理器的解析器实例
|
||||||
|
* @property context 提供具体地注销成功处理上下文,用于动态获取处理器
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 00:13:04
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class CompositeLogoutSuccessHandler(
|
||||||
|
private val resolver: CompositeLoginRequestResolver,
|
||||||
|
private val context: LogoutSuccessHandlerContext,
|
||||||
|
) : AbstractDelegatingHandler<LogoutSuccessHandler>(
|
||||||
|
resolver, context::resolve
|
||||||
|
), LogoutSuccessHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在用户成功注销时执行的具体操作
|
||||||
|
*
|
||||||
|
* 该方法通过解析当前请求获取对应的注销成功处理器,并委托其执行实际的注销后操作。
|
||||||
|
*
|
||||||
|
* @param request HTTP请求对象,提供客户端发送的请求信息
|
||||||
|
* @param response HTTP响应对象,用于向客户端发送响应
|
||||||
|
* @param authentication 包含认证信息的对象,在注销时通常表示已认证的用户信息
|
||||||
|
*/
|
||||||
|
override fun onLogoutSuccess(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
authentication: Authentication,
|
||||||
|
) {
|
||||||
|
resolveDelegate(request).onLogoutSuccess(request, response, authentication)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,162 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.provider
|
||||||
|
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.exception.ForgeBootAuthenticationException
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.service.UserCacheService
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.service.UserDetailsService
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.constants.ForgeBootSecurityAuthenticationResponseInformation
|
||||||
|
import org.springframework.security.authentication.AuthenticationProvider
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
import org.springframework.security.core.AuthenticationException
|
||||||
|
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用主体凭据认证提供器(抽象类)
|
||||||
|
* 负责封装认证流程中通用的逻辑,凭据校验和构建成功认证对象由子类完成。
|
||||||
|
*
|
||||||
|
* @author gewuyou
|
||||||
|
* @since 2025-06-11
|
||||||
|
*/
|
||||||
|
abstract class AbstractPrincipalCredentialsAuthenticationProvider(
|
||||||
|
private val userCacheService: UserCacheService,
|
||||||
|
private val userDetailsService: UserDetailsService
|
||||||
|
) : AuthenticationProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行认证的核心方法
|
||||||
|
*
|
||||||
|
* @param authentication 待认证的 Authentication 对象
|
||||||
|
* @return 成功认证后的 Authentication 对象
|
||||||
|
* @throws ForgeBootAuthenticationException 认证失败时抛出异常
|
||||||
|
*/
|
||||||
|
override fun authenticate(authentication: Authentication): Authentication {
|
||||||
|
val principal = getPrincipal(authentication)
|
||||||
|
val fromCache = AtomicBoolean(true)
|
||||||
|
|
||||||
|
// Step 1: 获取用户(缓存优先)
|
||||||
|
var user = userCacheService.getUserFromCache(principal) ?: run {
|
||||||
|
fromCache.set(false)
|
||||||
|
retrieveUser(principal)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
preAuthenticationCheck(user)
|
||||||
|
verifyCredentials(user, authentication)
|
||||||
|
postAuthenticationCheck(user)
|
||||||
|
} catch (e: AuthenticationException) {
|
||||||
|
if (fromCache.get()) {
|
||||||
|
// 如果缓存失败则尝试数据库
|
||||||
|
user = retrieveUser(principal)
|
||||||
|
preAuthenticationCheck(user)
|
||||||
|
verifyCredentials(user, authentication)
|
||||||
|
postAuthenticationCheck(user)
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fromCache.get()) {
|
||||||
|
userCacheService.putUserToCache(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
return createSuccessAuthentication(authentication, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断该 Provider 是否支持指定类型的认证对象
|
||||||
|
*
|
||||||
|
* @param authentication 需要判断的认证类型
|
||||||
|
* @return Boolean 表示是否支持该类型认证
|
||||||
|
*/
|
||||||
|
abstract override fun supports(authentication: Class<*>): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从数据源获取用户信息
|
||||||
|
*
|
||||||
|
* @param principal 用户标识
|
||||||
|
* @return UserDetails 用户详细信息
|
||||||
|
* @throws ForgeBootAuthenticationException 当 principal 为空或加载失败时抛出异常
|
||||||
|
*/
|
||||||
|
protected open fun retrieveUser(principal: String): UserDetails {
|
||||||
|
if (principal.isBlank()) {
|
||||||
|
ForgeBootAuthenticationException(ForgeBootSecurityAuthenticationResponseInformation.PRINCIPAL_NOT_PROVIDED)
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
userDetailsService.loadUserByPrincipal(principal)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw ForgeBootAuthenticationException(
|
||||||
|
ForgeBootSecurityAuthenticationResponseInformation.INTERNAL_SERVER_ERROR,
|
||||||
|
e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在验证凭据前进行账户状态检查
|
||||||
|
*
|
||||||
|
* @param user 用户详情对象
|
||||||
|
* @throws ForgeBootAuthenticationException 当账户锁定或禁用时抛出异常
|
||||||
|
*/
|
||||||
|
protected open fun preAuthenticationCheck(user: UserDetails) {
|
||||||
|
if (!user.isAccountNonLocked) {
|
||||||
|
throw ForgeBootAuthenticationException(ForgeBootSecurityAuthenticationResponseInformation.ACCOUNT_IS_LOCKED)
|
||||||
|
}
|
||||||
|
if (!user.isEnabled) {
|
||||||
|
throw ForgeBootAuthenticationException(ForgeBootSecurityAuthenticationResponseInformation.ACCOUNT_IS_DISABLED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在验证凭据后进行凭证有效期检查
|
||||||
|
*
|
||||||
|
* @param user 用户详情对象
|
||||||
|
* @throws ForgeBootAuthenticationException 当凭证已过期时抛出异常
|
||||||
|
*/
|
||||||
|
protected open fun postAuthenticationCheck(user: UserDetails) {
|
||||||
|
if (!user.isCredentialsNonExpired) {
|
||||||
|
throw ForgeBootAuthenticationException(ForgeBootSecurityAuthenticationResponseInformation.ACCOUNT_CREDENTIALS_HAVE_EXPIRED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查凭证是否为 null
|
||||||
|
*
|
||||||
|
* @param credentials 待检查的凭证对象
|
||||||
|
* @throws ForgeBootAuthenticationException 当凭证为 null 时抛出异常
|
||||||
|
*/
|
||||||
|
protected fun checkCredentialsNotNull(credentials: Any?) {
|
||||||
|
credentials ?: ForgeBootAuthenticationException(ForgeBootSecurityAuthenticationResponseInformation.PASSWORD_NOT_PROVIDED)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取认证对象中的用户标识
|
||||||
|
*
|
||||||
|
* @param authentication Authentication 对象
|
||||||
|
* @return String 用户标识字符串
|
||||||
|
*/
|
||||||
|
protected fun getPrincipal(authentication: Authentication): String =
|
||||||
|
authentication.principal.toString()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证用户凭据的具体实现,由子类完成
|
||||||
|
*
|
||||||
|
* @param user 用户详情对象
|
||||||
|
* @param authentication 待验证的 Authentication 对象
|
||||||
|
* @throws ForgeBootAuthenticationException 凭据错误时抛出异常
|
||||||
|
*/
|
||||||
|
protected abstract fun verifyCredentials(user: UserDetails, authentication: Authentication)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建成功认证后的 Authentication 对象,由子类实现
|
||||||
|
*
|
||||||
|
* @param authentication 原始的 Authentication 对象
|
||||||
|
* @param user 用户详情对象
|
||||||
|
* @return 成功认证后的 Authentication 对象
|
||||||
|
*/
|
||||||
|
protected abstract fun createSuccessAuthentication(
|
||||||
|
authentication: Authentication,
|
||||||
|
user: UserDetails
|
||||||
|
): Authentication
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.provider.impl
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.exception.ForgeBootAuthenticationException
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.service.UserCacheService
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.service.UserDetailsService
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.constants.ForgeBootSecurityAuthenticationResponseInformation
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.provider.AbstractPrincipalCredentialsAuthenticationProvider
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.token.UsernamePasswordAuthenticationToken
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名密码身份验证提供商
|
||||||
|
*
|
||||||
|
* 该类用于处理基于用户名和密码的身份验证流程,继承自 AbstractPrincipalCredentialsAuthenticationProvider。
|
||||||
|
* 提供了验证凭证、创建成功认证对象以及判断支持的认证类型的功能。
|
||||||
|
*
|
||||||
|
* @property userCacheService 用户缓存服务,用于获取用户缓存信息
|
||||||
|
* @property userDetailsService 用户详情服务,用于加载用户详细信息
|
||||||
|
* @property passwordEncoder 密码编码器,用于密码匹配校验
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 17:12:30
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class UsernamePasswordAuthenticationProvider(
|
||||||
|
userCacheService: UserCacheService,
|
||||||
|
userDetailsService: UserDetailsService,
|
||||||
|
private val passwordEncoder: PasswordEncoder
|
||||||
|
) : AbstractPrincipalCredentialsAuthenticationProvider(userCacheService, userDetailsService) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证用户提供的凭证是否有效。
|
||||||
|
*
|
||||||
|
* 此方法会检查认证对象中的凭证是否为空,并使用密码编码器对原始密码和编码后的密码进行比对。
|
||||||
|
*
|
||||||
|
* @param user 已加载的用户详情对象
|
||||||
|
* @param authentication 当前的认证对象,包含用户提交的凭证信息
|
||||||
|
* @throws com.gewuyou.forgeboot.security.authenticate.api.exception.ForgeBootAuthenticationException 如果凭证不匹配或为空
|
||||||
|
*/
|
||||||
|
override fun verifyCredentials(user: UserDetails, authentication: Authentication) {
|
||||||
|
checkCredentialsNotNull(authentication.credentials)
|
||||||
|
val raw = authentication.credentials.toString()
|
||||||
|
val encoded = user.password.toString()
|
||||||
|
if (!passwordEncoder.matches(raw, encoded)) {
|
||||||
|
throw ForgeBootAuthenticationException(ForgeBootSecurityAuthenticationResponseInformation.INCORRECT_USERNAME_OR_PASSWORD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个成功的认证对象。
|
||||||
|
*
|
||||||
|
* 在验证通过后,此方法将生成一个新的认证对象,表示已认证的用户状态。
|
||||||
|
*
|
||||||
|
* @param authentication 原始的认证请求对象
|
||||||
|
* @param user 已验证的用户详情对象
|
||||||
|
* @return 返回一个表示成功认证的新 Authentication 实例
|
||||||
|
*/
|
||||||
|
override fun createSuccessAuthentication(
|
||||||
|
authentication: Authentication,
|
||||||
|
user: UserDetails
|
||||||
|
): Authentication {
|
||||||
|
return UsernamePasswordAuthenticationToken(user, null, user.authorities)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前提供者是否支持给定的认证类型。
|
||||||
|
*
|
||||||
|
* 此方法确保只处理 UsernamePasswordAuthenticationToken 类型的认证请求。
|
||||||
|
*
|
||||||
|
* @param authentication 认证类类型
|
||||||
|
* @return Boolean 表示是否支持该认证类型
|
||||||
|
*/
|
||||||
|
override fun supports(authentication: Class<*>): Boolean {
|
||||||
|
return UsernamePasswordAuthenticationToken::class.java.isAssignableFrom(authentication)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.resolver
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.resolver.LoginRequestResolver
|
||||||
|
import com.gewuyou.forgeboot.security.core.authenticate.entities.request.LoginRequest
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import kotlin.collections.find
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复合登录请求解析器
|
||||||
|
*
|
||||||
|
* 该类用于根据请求的内容类型(contentType)动态选择合适的登录请求解析器。
|
||||||
|
* 它通过组合多种 LoginRequestResolver实现,支持不同格式的登录请求解析。
|
||||||
|
*
|
||||||
|
* @property resolvers 提供支持的各种登录请求解析器列表
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 16:46:57
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class CompositeLoginRequestResolver(
|
||||||
|
private val resolvers: List<LoginRequestResolver>
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* 解析登录请求
|
||||||
|
*
|
||||||
|
* 根据给定的 HTTP 请求中的内容类型(contentType),查找支持该类型的解析器,
|
||||||
|
* 并使用该解析器解析登录请求。
|
||||||
|
*
|
||||||
|
* @param request 需要解析的 HTTP 请求
|
||||||
|
* @return 解析后的登录请求对象(LoginRequest)
|
||||||
|
* @throws IllegalArgumentException 如果没有找到支持当前内容类型的解析器
|
||||||
|
*/
|
||||||
|
fun resolve(request: HttpServletRequest): LoginRequest {
|
||||||
|
val contentType = request.contentType ?: ""
|
||||||
|
val resolver = resolvers.find { it.supports(contentType) }
|
||||||
|
?: throw IllegalArgumentException("Unsupported content type: $contentType")
|
||||||
|
return resolver.resolve(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.resolver
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.registry.LoginRequestTypeRegistry
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.resolver.LoginRequestResolver
|
||||||
|
import com.gewuyou.forgeboot.security.core.authenticate.entities.request.AuthenticationRequest
|
||||||
|
import com.gewuyou.forgeboot.security.core.authenticate.entities.request.LoginRequest
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON登录请求解析器
|
||||||
|
*
|
||||||
|
* 该解析器用于处理JSON格式的登录请求数据,通过两次反序列化过程来支持动态类型的登录请求。
|
||||||
|
* 首先读取请求中的type字段以确定具体的登录类型,然后根据该类型再次反序列化为对应的对象。
|
||||||
|
*
|
||||||
|
* @property objectMapper 用于JSON序列化和反序列化的Jackson ObjectMapper实例
|
||||||
|
* @property loginRequestTypeRegistry 登录请求类型的注册表,用于根据登录类型获取实际类
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 16:37:22
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class JsonLoginRequestResolver(
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val loginRequestTypeRegistry: LoginRequestTypeRegistry, // 动态注册哪些类型
|
||||||
|
) : LoginRequestResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否支持给定的内容类型
|
||||||
|
*
|
||||||
|
* 该方法检查内容类型字符串中是否包含JSON媒体类型(application/json),
|
||||||
|
* 表示当前解析器可以处理该类型的内容。
|
||||||
|
*
|
||||||
|
* @param contentType 请求的内容类型字符串
|
||||||
|
* @return 如果支持返回true,否则返回false
|
||||||
|
*/
|
||||||
|
override fun supports(contentType: String): Boolean {
|
||||||
|
return contentType.contains(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析登录请求
|
||||||
|
*
|
||||||
|
* 从HttpServletRequest中读取JSON输入流,并进行两次反序列化操作:
|
||||||
|
* 1. 首次反序列化为AuthenticationRequest以提取type字段;
|
||||||
|
* 2. 根据type字段从注册表中查找对应的实际类;
|
||||||
|
* 3. 再次反序列化为实际类的对象。
|
||||||
|
*
|
||||||
|
* @param request HTTP请求对象
|
||||||
|
* @return 解析后的登录请求对象
|
||||||
|
* @throws IllegalArgumentException 如果不支持的登录类型
|
||||||
|
*/
|
||||||
|
override fun resolve(request: HttpServletRequest): LoginRequest {
|
||||||
|
val json = request.inputStream.reader().readText()
|
||||||
|
// 1. 先反序列化为 AuthenticationRequest,读取 type 字段
|
||||||
|
val tempRequest = objectMapper.readValue(json, AuthenticationRequest::class.java)
|
||||||
|
val loginType = tempRequest.type
|
||||||
|
// 2. 从注册表中获取真正对应的类型
|
||||||
|
val clazz = loginRequestTypeRegistry.getTypeForLoginType(loginType)
|
||||||
|
?: throw IllegalArgumentException("Unsupported login type: $loginType")
|
||||||
|
// 3. 再次反序列化为真正请求体类型
|
||||||
|
return objectMapper.readValue(json, clazz)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.config.SecurityAuthenticateProperties
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.AuthenticationFailureStrategy
|
||||||
|
import com.gewuyou.forgeboot.webmvc.dto.R
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.security.core.AuthenticationException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象验证失败策略基类
|
||||||
|
* 提供统一的认证失败处理机制实现模板
|
||||||
|
*
|
||||||
|
* @property objectMapper 用于JSON序列化响应结果
|
||||||
|
* @property properties 安全认证配置属性
|
||||||
|
* @author gewuyou
|
||||||
|
* @since 2025-06-14 20:14:14
|
||||||
|
*/
|
||||||
|
abstract class AbstractAuthenticationFailureStrategy(
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val properties: SecurityAuthenticateProperties
|
||||||
|
) : AuthenticationFailureStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理认证失败的通用实现
|
||||||
|
* 将认证失败结果转换为JSON格式的HTTP响应
|
||||||
|
*
|
||||||
|
* @param request HTTP请求对象
|
||||||
|
* @param response HTTP响应对象
|
||||||
|
* @param exception 认证异常信息
|
||||||
|
*/
|
||||||
|
override fun onAuthenticationFailure(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
exception: AuthenticationException,
|
||||||
|
) {
|
||||||
|
// 设置响应内容类型为JSON
|
||||||
|
response.contentType = MediaType.APPLICATION_JSON_VALUE
|
||||||
|
|
||||||
|
// 获取响应输出流并写入序列化后的失败结果
|
||||||
|
val writer = response.writer
|
||||||
|
writer.print(
|
||||||
|
objectMapper.writeValueAsString(buildFailureResult(exception))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建认证失败的响应数据
|
||||||
|
* 可被子类重写以提供自定义失败响应
|
||||||
|
*
|
||||||
|
* @param authenticationException 认证异常信息
|
||||||
|
* @return 序列化为JSON的响应数据
|
||||||
|
*/
|
||||||
|
protected open fun buildFailureResult(authenticationException: AuthenticationException): Any {
|
||||||
|
// 获取异常消息,若为空则使用默认配置的失败响应
|
||||||
|
val message = authenticationException.message
|
||||||
|
return R.failure<String>(
|
||||||
|
HttpStatus.UNAUTHORIZED.value(),
|
||||||
|
message?.let {
|
||||||
|
message
|
||||||
|
} ?: properties.defaultAuthenticationFailureResponse
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.AuthenticationSuccessStrategy
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.constants.ForgeBootSecurityAuthenticationResponseInformation
|
||||||
|
import com.gewuyou.forgeboot.webmvc.dto.R
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象认证成功策略
|
||||||
|
*
|
||||||
|
* 该基础类为认证成功后的处理提供统一模板
|
||||||
|
*
|
||||||
|
* @property objectMapper 用于将响应对象序列化为 JSON 字符串
|
||||||
|
* @since 2025-06-12 21:26:11
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
abstract class AbstractAuthenticationSuccessStrategy(
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
) : AuthenticationSuccessStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证成功时触发的方法
|
||||||
|
*
|
||||||
|
* 处理 HTTP 响应的设置,包括状态码、内容类型和写入响应数据
|
||||||
|
*
|
||||||
|
* @param request 本次请求的 HttpServletRequest 对象
|
||||||
|
* @param response 本次响应的 HttpServletResponse 对象
|
||||||
|
* @param authentication 包含认证信息的对象
|
||||||
|
*/
|
||||||
|
override fun onAuthenticationSuccess(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
authentication: Authentication,
|
||||||
|
) {
|
||||||
|
// 构建认证成功响应数据
|
||||||
|
val result = buildSuccessResponse(authentication)
|
||||||
|
// 设置响应状态码为 200 OK
|
||||||
|
response.status = HttpStatus.OK.value()
|
||||||
|
// 设置响应内容类型为 application/json
|
||||||
|
response.contentType = MediaType.APPLICATION_JSON_VALUE
|
||||||
|
// 将响应结果序列化为 JSON 并写入响应输出流
|
||||||
|
response.writer.write(objectMapper.writeValueAsString(result))
|
||||||
|
response.writer.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建认证成功响应体对象
|
||||||
|
*
|
||||||
|
* @param authentication 包含认证信息的对象
|
||||||
|
* @return 返回准备序列化并发送给客户端的响应对象
|
||||||
|
*/
|
||||||
|
protected open fun buildSuccessResponse(authentication: Authentication): Any {
|
||||||
|
return R.success(
|
||||||
|
HttpStatus.OK.value(),
|
||||||
|
ForgeBootSecurityAuthenticationResponseInformation.LOGIN_SUCCESS, authentication.principal
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.LogoutSuccessStrategy
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.constants.ForgeBootSecurityAuthenticationResponseInformation
|
||||||
|
import com.gewuyou.forgeboot.webmvc.dto.R
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象注销成功策略
|
||||||
|
*
|
||||||
|
* 该抽象类为注销成功时的响应处理提供了统一的策略框架。
|
||||||
|
* 子类应继承此类并根据需要定制具体注销成功逻辑。
|
||||||
|
*
|
||||||
|
* @property objectMapper 用于将响应结果序列化为 JSON 格式
|
||||||
|
* @constructor 接收一个 ObjectMapper 实例作为参数
|
||||||
|
*
|
||||||
|
* @since 2025-06-14 20:29:25
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
abstract class AbstractLogoutSuccessStrategy(
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
): LogoutSuccessStrategy {
|
||||||
|
/**
|
||||||
|
* 注销成功回调方法
|
||||||
|
*
|
||||||
|
* 当用户成功注销时,该方法会被调用以处理响应。默认实现会返回一个包含成功状态码和消息的 JSON 响应。
|
||||||
|
*
|
||||||
|
* @param request 表示 HTTP 请求对象,提供有关客户端请求的信息
|
||||||
|
* @param response 表示 HTTP 响应对象,用于向客户端发送响应
|
||||||
|
* @param authentication 包含认证信息的对象,在注销时提供额外上下文数据
|
||||||
|
*/
|
||||||
|
override fun onLogoutSuccess(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
authentication: Authentication,
|
||||||
|
) {
|
||||||
|
// 创建表示注销成功响应数据对象
|
||||||
|
val result = R.success<String>(
|
||||||
|
HttpStatus.OK.value(),
|
||||||
|
ForgeBootSecurityAuthenticationResponseInformation.LOGOUT_SUCCESS
|
||||||
|
)
|
||||||
|
|
||||||
|
// 设置响应状态码为 200 OK
|
||||||
|
response.status = HttpStatus.OK.value()
|
||||||
|
|
||||||
|
// 设置响应内容类型为 application/json
|
||||||
|
response.contentType = MediaType.APPLICATION_JSON_VALUE
|
||||||
|
|
||||||
|
// 将响应结果序列化为 JSON 并写入响应输出流
|
||||||
|
response.writer.write(objectMapper.writeValueAsString(result))
|
||||||
|
response.writer.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy.context
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.core.extension.log
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.enums.LoginTypes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象处理程序上下文
|
||||||
|
* 用于构建策略处理器的通用框架,支持通过类型解析对应的处理器实例
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 21:59:40
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
abstract class AbstractHandlerContext<T : Any, H : Any>(
|
||||||
|
/**
|
||||||
|
* 策略对象列表,用于从中提取类型和对应的处理器
|
||||||
|
*/
|
||||||
|
strategies: List<T>,
|
||||||
|
/**
|
||||||
|
* 当前处理器类型的名称,用于日志输出时标识处理器种类
|
||||||
|
*/
|
||||||
|
private val typeName: String,
|
||||||
|
/**
|
||||||
|
* 提取策略对应类型的函数,用于确定每个策略的标识
|
||||||
|
*/
|
||||||
|
extractType: (T) -> String,
|
||||||
|
/**
|
||||||
|
* 提取策略对应处理器的函数,用于获取实际可执行的处理器逻辑
|
||||||
|
*/
|
||||||
|
extractHandler: (T) -> H
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* 默认处理器,用于处理未指定登录类型的请求
|
||||||
|
*/
|
||||||
|
private var defaultStrategy: T? = null
|
||||||
|
/**
|
||||||
|
* 存储登录类型与处理器之间的映射关系
|
||||||
|
*/
|
||||||
|
private val handlerMap: Map<String, H>
|
||||||
|
|
||||||
|
init {
|
||||||
|
val map = mutableMapOf<String, H>()
|
||||||
|
for (strategy in strategies) {
|
||||||
|
// 提取当前策略的登录类型标识
|
||||||
|
val loginType = extractType(strategy)
|
||||||
|
if (loginType == LoginTypes.DEFAULT) {
|
||||||
|
defaultStrategy = strategy
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 获取当前策略对应的处理器实例
|
||||||
|
val handler = extractHandler(strategy)
|
||||||
|
// 检查是否已存在相同类型的处理器
|
||||||
|
val existingHandlerName = map[loginType]?.javaClass?.name
|
||||||
|
existingHandlerName?.let {
|
||||||
|
log.warn("重复注册登录类型 [$loginType] 的$typeName,已存在 $existingHandlerName,被 ${strategy::class.java.name} 覆盖")
|
||||||
|
}?.run {
|
||||||
|
log.info("注册${typeName}策略: $loginType -> ${strategy::class.java.name}")
|
||||||
|
}
|
||||||
|
// 注册或覆盖处理器映射
|
||||||
|
map[loginType] = handler
|
||||||
|
}
|
||||||
|
// 不可变化处理器映射表
|
||||||
|
this.handlerMap = map.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指定的登录类型解析对应的处理器实例
|
||||||
|
*
|
||||||
|
* 该方法首先尝试通过给定的登录类型从处理器映射中查找对应的策略处理器。
|
||||||
|
* 如果找不到匹配项,则尝试使用默认类型的处理器。
|
||||||
|
* 如果仍然无法找到,默认处理器也不存在,则抛出异常。
|
||||||
|
*
|
||||||
|
* @param loginType 登录类型标识,用于查找对应的处理器
|
||||||
|
* @return H 类型的处理器实例,与给定的登录类型或默认类型匹配
|
||||||
|
* @throws IllegalArgumentException 如果既没有找到与登录类型匹配的处理器,也没有定义默认处理器
|
||||||
|
*/
|
||||||
|
fun resolve(loginType: String): H {
|
||||||
|
return handlerMap[loginType]
|
||||||
|
?: handlerMap[LoginTypes.DEFAULT]
|
||||||
|
?: throw IllegalArgumentException("未找到 loginType 为 [$loginType] 的$typeName,且未定义默认$typeName")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy.context
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.AuthenticationFailureStrategy
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
*身份验证故障处理程序上下文
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 23:48:02
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class AuthenticationFailureHandlerContext(
|
||||||
|
strategies: List<AuthenticationFailureStrategy>
|
||||||
|
) : AbstractHandlerContext<AuthenticationFailureStrategy, AuthenticationFailureHandler>(
|
||||||
|
strategies,
|
||||||
|
"认证失败处理器",
|
||||||
|
{ it.supportedLoginType() },
|
||||||
|
{ it }
|
||||||
|
)
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy.context
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.AuthenticationSuccessStrategy
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
*身份验证成功处理程序上下文
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 23:45:40
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class AuthenticationSuccessHandlerContext(
|
||||||
|
strategies: List<AuthenticationSuccessStrategy>,
|
||||||
|
) : AbstractHandlerContext<AuthenticationSuccessStrategy, AuthenticationSuccessHandler>(
|
||||||
|
strategies,
|
||||||
|
"认证成功处理器"
|
||||||
|
,
|
||||||
|
{ it.supportedLoginType() },
|
||||||
|
{ it }
|
||||||
|
)
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy.context
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.core.extension.log
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.LoginRequestConverterStrategy
|
||||||
|
import com.gewuyou.forgeboot.security.core.authenticate.entities.request.LoginRequest
|
||||||
|
import jakarta.annotation.PostConstruct
|
||||||
|
import org.springframework.context.ApplicationContext
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
import kotlin.collections.component1
|
||||||
|
import kotlin.collections.component2
|
||||||
|
import kotlin.collections.iterator
|
||||||
|
import kotlin.jvm.java
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录请求转换器上下文
|
||||||
|
* 负责管理不同登录类型的请求转换策略,实现策略模式的上下文类
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 20:48:09
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class LoginRequestConverterContext(
|
||||||
|
private val applicationContext: ApplicationContext
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* 存储登录类型与对应转换策略的映射关系
|
||||||
|
*/
|
||||||
|
private val converterStrategies = mutableMapOf<String, LoginRequestConverterStrategy>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化转换器策略
|
||||||
|
* 通过Spring容器获取所有LoginRequestConverterStrategy实例
|
||||||
|
* 并根据支持的登录类型注册到converterStrategies中
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
fun initConverterStrategies() {
|
||||||
|
val strategies = applicationContext.getBeansOfType(LoginRequestConverterStrategy::class.java).toMap()
|
||||||
|
for ((_, strategy) in strategies) {
|
||||||
|
val loginType = strategy.getSupportedLoginType()
|
||||||
|
addConverter(loginType, strategy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加登录请求转换器策略
|
||||||
|
* @param loginType 登录类型标识符
|
||||||
|
* @param strategy 转换器策略实现
|
||||||
|
*/
|
||||||
|
private fun addConverter(loginType: String, strategy: LoginRequestConverterStrategy) {
|
||||||
|
converterStrategies[loginType] = strategy
|
||||||
|
log.info("为登录类型: $loginType 添加登录请求转换器策略 ${strategy::class.java.name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行对应的转换策略
|
||||||
|
* 根据登录请求类型查找并执行对应的转换器策略
|
||||||
|
*
|
||||||
|
* @param loginRequest 登录请求对象
|
||||||
|
* @return Authentication 认证对象
|
||||||
|
* @throws IllegalArgumentException 当找不到匹配的转换策略时抛出异常
|
||||||
|
*/
|
||||||
|
fun executeStrategy(loginRequest: LoginRequest): Authentication {
|
||||||
|
return converterStrategies[loginRequest.getType()]?.convert(loginRequest)
|
||||||
|
?: throw IllegalArgumentException("No converter strategy found for login type ${loginRequest.getType()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy.context
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.LogoutSuccessStrategy
|
||||||
|
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
*注销成功处理程序上下文
|
||||||
|
*
|
||||||
|
* @since 2025-06-11 00:12:33
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class LogoutSuccessHandlerContext(
|
||||||
|
strategies: List<LogoutSuccessStrategy>,
|
||||||
|
) : AbstractHandlerContext<LogoutSuccessStrategy, LogoutSuccessHandler>(
|
||||||
|
strategies,
|
||||||
|
"登出处理器",
|
||||||
|
{ it.supportedLoginType() },
|
||||||
|
{ it }
|
||||||
|
)
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy.impl
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.config.SecurityAuthenticateProperties
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.enums.LoginTypes
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.AbstractAuthenticationFailureStrategy
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
/**
|
||||||
|
*默认身份验证失败策略
|
||||||
|
*
|
||||||
|
* @since 2025-06-14 20:26:46
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Component("defaultAuthenticationFailureStrategy")
|
||||||
|
class DefaultAuthenticationFailureStrategy(
|
||||||
|
objectMapper: ObjectMapper,
|
||||||
|
properties: SecurityAuthenticateProperties
|
||||||
|
): AbstractAuthenticationFailureStrategy(objectMapper,properties) {
|
||||||
|
/**
|
||||||
|
* 获取当前策略支持的登录类型标识符。
|
||||||
|
*
|
||||||
|
* @return 返回当前策略支持的登录类型字符串
|
||||||
|
*/
|
||||||
|
override fun supportedLoginType(): String {
|
||||||
|
return LoginTypes.DEFAULT
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy.impl
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.enums.LoginTypes
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.AbstractAuthenticationSuccessStrategy
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认身份验证成功策略
|
||||||
|
*
|
||||||
|
* 该类实现了一个基础的身份验证成功处理策略,用于处理默认类型的登录认证。
|
||||||
|
* 继承自 [AbstractAuthenticationSuccessStrategy],提供具体的登录类型标识。
|
||||||
|
*
|
||||||
|
* @property objectMapper 用于序列化/反序列化 JSON 数据的对象
|
||||||
|
* @since 2025-06-12 22:13:42
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Component("defaultAuthenticationSuccessStrategy")
|
||||||
|
class DefaultAuthenticationSuccessStrategy(
|
||||||
|
objectMapper: ObjectMapper,
|
||||||
|
) : AbstractAuthenticationSuccessStrategy(objectMapper) {
|
||||||
|
/**
|
||||||
|
* 获取当前策略支持的登录类型标识
|
||||||
|
*
|
||||||
|
* 此方法返回 "default" 字符串,表示该策略适用于默认登录类型。
|
||||||
|
* 在多策略环境下,通过此标识来匹配相应的处理逻辑。
|
||||||
|
*
|
||||||
|
* @return 返回支持的登录类型标识字符串
|
||||||
|
*/
|
||||||
|
override fun supportedLoginType(): String = LoginTypes.DEFAULT
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy.impl
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.enums.LoginTypes
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.impl.strategy.AbstractLogoutSuccessStrategy
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
/**
|
||||||
|
*默认注销成功策略
|
||||||
|
*
|
||||||
|
* @since 2025-06-14 20:38:16
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Component("defaultLogoutSuccessStrategy")
|
||||||
|
class DefaultLogoutSuccessStrategy(
|
||||||
|
objectMapper: ObjectMapper
|
||||||
|
): AbstractLogoutSuccessStrategy(objectMapper) {
|
||||||
|
/**
|
||||||
|
* 获取当前策略支持的登录类型标识符。
|
||||||
|
*
|
||||||
|
* @return 返回当前策略支持的登录类型字符串
|
||||||
|
*/
|
||||||
|
override fun supportedLoginType(): String {
|
||||||
|
return LoginTypes.DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authenticate.impl.strategy.impl
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.enums.LoginTypes
|
||||||
|
import com.gewuyou.forgeboot.security.authenticate.api.strategy.LoginRequestConverterStrategy
|
||||||
|
import com.gewuyou.forgeboot.security.core.authenticate.entities.request.LoginRequest
|
||||||
|
import com.gewuyou.forgeboot.security.core.authenticate.entities.request.UsernamePasswordAuthenticationRequest
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.token.UsernamePasswordAuthenticationToken
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名 密码 认证 Token 转换器策略
|
||||||
|
* @since 2025-02-15 03:25:14
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Component("usernamePasswordLoginRequestConverterStrategy")
|
||||||
|
class UsernamePasswordLoginRequestConverterStrategy : LoginRequestConverterStrategy {
|
||||||
|
/**
|
||||||
|
* 转换登录请求为认证对象
|
||||||
|
* @param loginRequest 登录请求
|
||||||
|
* @return 认证对象
|
||||||
|
*/
|
||||||
|
override fun convert(loginRequest: LoginRequest): Authentication {
|
||||||
|
if (loginRequest is UsernamePasswordAuthenticationRequest) {
|
||||||
|
return UsernamePasswordAuthenticationToken.Companion.unauthenticated(loginRequest.username, loginRequest.password)
|
||||||
|
}
|
||||||
|
throw IllegalArgumentException("Unsupported login request type: ${loginRequest.javaClass.name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取支持的登录类型
|
||||||
|
* @return 请求的登录类型
|
||||||
|
*/
|
||||||
|
override fun getSupportedLoginType(): String {
|
||||||
|
return LoginTypes.USERNAME_PASSWORD
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(project(Modules.Security.CORE))
|
||||||
|
compileOnly(libs.springBootStarter.web)
|
||||||
|
compileOnly(libs.springBootStarter.security)
|
||||||
|
kapt(libs.springBoot.configuration.processor)
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.api.annotations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验注解,用于标记需要特定权限才能访问的方法
|
||||||
|
*
|
||||||
|
* @property value 需要校验的权限字符串,例如:"user:read"
|
||||||
|
* @property dynamic 是否为动态权限,默认为 false。若为 true 表示该权限需要在运行时动态解析
|
||||||
|
*
|
||||||
|
* 注解作用于方法级别,运行时生效,常用于接口权限控制。
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class RequiresPermission(
|
||||||
|
val value: String,
|
||||||
|
val dynamic: Boolean = false
|
||||||
|
)
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.api.config
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
|
||||||
|
/**
|
||||||
|
*security 授权属性
|
||||||
|
*
|
||||||
|
* @since 2025-06-15 19:26:20
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(prefix = "forgeboot.security.authorize")
|
||||||
|
class SecurityAuthorizeProperties {
|
||||||
|
var defaultExceptionResponse: String = "Sorry, you don't have access to the resource!"
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.api.manager
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问管理器接口,用于定义鉴权逻辑
|
||||||
|
*
|
||||||
|
* @since 2025-06-15 18:38:07
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface AccessManager {
|
||||||
|
/**
|
||||||
|
* 判断用户是否具有指定权限
|
||||||
|
*
|
||||||
|
* @param authentication Spring Security提供的身份认证信息
|
||||||
|
* @param permission 需要验证的权限字符串
|
||||||
|
* @return Boolean 返回是否有权限的布尔值
|
||||||
|
*/
|
||||||
|
fun hasPermission(authentication: Authentication, permission: String): Boolean
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.api.manager
|
||||||
|
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态授权管理器接口
|
||||||
|
*
|
||||||
|
* 该接口用于处理请求级别的动态权限控制逻辑,基于Spring Security的AuthorizationManager接口进行扩展。
|
||||||
|
* 通过泛型类型T定义需要处理的授权上下文类型,例如Web请求或方法调用等。
|
||||||
|
*
|
||||||
|
* @param <T> 授权操作涉及的具体上下文类型,如RequestAuthorizationContext或其他自定义上下文对象
|
||||||
|
*
|
||||||
|
* @since 2025-06-24 15:52:01
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface DynamicAuthorizationManager<T> : AuthorizationManager<T>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.api.manager
|
||||||
|
|
||||||
|
import org.springframework.security.authorization.ReactiveAuthorizationManager
|
||||||
|
|
||||||
|
/**
|
||||||
|
*动态反应式授权管理器
|
||||||
|
*
|
||||||
|
* @since 2025-06-24 13:16:25
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface DynamicReactiveAuthorizationManager<T> : ReactiveAuthorizationManager<T>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.api.provider
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限提供程序接口,用于定义权限集合的生成逻辑。
|
||||||
|
*
|
||||||
|
* @since 2025-06-15 13:08:18
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface PermissionProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供基于认证信息的权限集合。
|
||||||
|
*
|
||||||
|
* @param authentication 包含用户认证信息的对象,用于权限判断
|
||||||
|
* @return 返回与当前认证信息关联的权限字符串集合
|
||||||
|
*/
|
||||||
|
fun listPermissions(authentication: Authentication): Collection<String>
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.api.resolver
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限解析程序接口,用于将请求路径和HTTP方法转换为具体的权限标识符。
|
||||||
|
*
|
||||||
|
* @since 2025-06-24 13:23:29
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface PermissionResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析给定的请求路径和HTTP方法,生成对应的权限标识字符串。
|
||||||
|
*
|
||||||
|
* 此方法通常用于安全框架中,用来判断用户是否拥有执行特定操作的权限。
|
||||||
|
*
|
||||||
|
* @param path 请求的URL路径,例如 "/api/users"
|
||||||
|
* @param method HTTP请求方法,如 GET, POST 等
|
||||||
|
* @return 返回一个表示权限的字符串,如 "user:read", "user:write" 等
|
||||||
|
*/
|
||||||
|
fun resolve(path: String, method: String): String
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.api.strategy
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.provider.PermissionProvider
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权策略接口
|
||||||
|
*
|
||||||
|
* 定义了实现授权逻辑的统一接口,所有具体授权策略都需要实现该接口。
|
||||||
|
* 该接口设计为函数式接口,便于通过Lambda表达式或函数引用实现简洁的策略定义。
|
||||||
|
*
|
||||||
|
* @since 2025-06-15 13:01:27
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface AuthorizationStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行授权判断
|
||||||
|
*
|
||||||
|
* 根据提供的认证信息、权限标识和权限提供者列表,判断当前认证是否具备指定权限。
|
||||||
|
*
|
||||||
|
* @param authentication 当前用户的认证信息,包含主体身份和凭证等
|
||||||
|
* @param permission 需要验证的权限标识,通常为特定资源的操作权限字符串
|
||||||
|
* @param providers 权限提供者列表,用于获取与权限相关的数据源或校验逻辑
|
||||||
|
* @return Boolean 返回授权结果,true表示授权通过,false表示拒绝访问
|
||||||
|
*/
|
||||||
|
fun authorize(
|
||||||
|
authentication: Authentication,
|
||||||
|
permission: String,
|
||||||
|
providers: List<PermissionProvider>
|
||||||
|
): Boolean
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlin.plugin.spring)
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
compileOnly(libs.springBootStarter.web)
|
||||||
|
// webflux
|
||||||
|
compileOnly(libs.springBootStarter.webflux)
|
||||||
|
compileOnly(libs.springBootStarter.security)
|
||||||
|
compileOnly(project(Modules.Security.Authorize.IMPL))
|
||||||
|
compileOnly(project(Modules.Security.Authorize.API))
|
||||||
|
implementation(project(Modules.Security.CORE))
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.autoconfigure
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.config.SecurityAuthorizeProperties
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
|
||||||
|
/**
|
||||||
|
*forge security 授权自动配置
|
||||||
|
*
|
||||||
|
* @since 2025-06-15 19:28:11
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(SecurityAuthorizeProperties::class)
|
||||||
|
class ForgeSecurityAuthorizeAutoConfiguration
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
com.gewuyou.forgeboot.security.authorize.autoconfigure.ForgeSecurityAuthorizeCoreConfiguration
|
||||||
|
com.gewuyou.forgeboot.security.authorize.autoconfigure.ReactiveAuthorizeSecurityConfiguration
|
||||||
|
com.gewuyou.forgeboot.security.authorize.autoconfigure.ForgeSecurityAuthorizeAutoConfiguration
|
||||||
|
com.gewuyou.forgeboot.security.authorize.autoconfigure.ServletAuthorizeSecurityConfiguration
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
extra{
|
||||||
|
setProperty(ProjectFlags.IS_ROOT_MODULE,true)
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlin.plugin.spring)
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.springBootStarter.aop)
|
||||||
|
implementation(libs.springExpression)
|
||||||
|
implementation(project(Modules.Core.EXTENSION))
|
||||||
|
implementation(project(Modules.Cache.STARTER))
|
||||||
|
implementation(project(Modules.Webmvc.DTO)) // webflux
|
||||||
|
compileOnly(libs.springBootStarter.webflux)
|
||||||
|
compileOnly(project(Modules.Security.Authorize.API))
|
||||||
|
compileOnly(libs.springBootStarter.web)
|
||||||
|
compileOnly(libs.springBootStarter.security)
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.adapter
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.core.extension.log
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.manager.AccessManager
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.resolver.PermissionResolver
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权管理器适配器,用于将 AccessManager 适配为 Spring Security 的 AuthorizationManager。
|
||||||
|
*
|
||||||
|
* @property accessManager 提供权限判断的访问管理器实例
|
||||||
|
* @property permissionResolver 用于解析请求路径和方法对应的权限需求
|
||||||
|
* @since 2025-06-23 21:42:19
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class DynamicAuthorizationManagerAdapter(
|
||||||
|
private val accessManager: AccessManager,
|
||||||
|
private val permissionResolver: PermissionResolver,
|
||||||
|
) : AuthorizationManager<RequestAuthorizationContext> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行权限校验的核心方法。
|
||||||
|
*
|
||||||
|
* @param authentication 提供认证信息的 Supplier,从中获取当前用户的身份认证对象
|
||||||
|
* @param context 请求上下文,包含请求相关的额外信息(未在此实现中使用)
|
||||||
|
* @return 返回一个 AuthorizationDecision 对象,表示是否通过授权
|
||||||
|
*/
|
||||||
|
override fun check(
|
||||||
|
authentication: Supplier<Authentication>,
|
||||||
|
context: RequestAuthorizationContext,
|
||||||
|
): AuthorizationDecision {
|
||||||
|
try {
|
||||||
|
// 获取 HTTP 请求的基本信息
|
||||||
|
val request = context.request
|
||||||
|
val method = request.method
|
||||||
|
val path = request.requestURI
|
||||||
|
// 解析当前请求所需的权限
|
||||||
|
val requiredPermission = permissionResolver.resolve(path, method)
|
||||||
|
|
||||||
|
// 校验用户是否拥有该权限
|
||||||
|
val granted = accessManager.hasPermission(authentication.get(), requiredPermission)
|
||||||
|
|
||||||
|
// 返回授权决策结果
|
||||||
|
return AuthorizationDecision(granted)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.error("权限校验异常", e)
|
||||||
|
return AuthorizationDecision(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.adapter
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.manager.AccessManager
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.manager.DynamicReactiveAuthorizationManager
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.resolver.PermissionResolver
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
import org.springframework.security.web.server.authorization.AuthorizationContext
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反应式授权管理器适配器
|
||||||
|
* 用于将访问控制逻辑适配为 Spring Security 的 ReactiveAuthorizationManager 接口规范
|
||||||
|
*
|
||||||
|
* @property accessManager 访问控制管理器,用于执行具体的权限判断逻辑
|
||||||
|
* @property permissionResolver 权限解析器,用于从请求中动态解析所需权限
|
||||||
|
* @since 2025-06-24 13:13:40
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class DynamicReactiveAuthorizationManagerAdapter(
|
||||||
|
private val accessManager: AccessManager,
|
||||||
|
private val permissionResolver: PermissionResolver
|
||||||
|
) : DynamicReactiveAuthorizationManager<AuthorizationContext> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查当前请求是否满足所需的授权条件
|
||||||
|
*
|
||||||
|
* 此方法从认证信息和授权上下文中提取关键数据,通过权限解析器获取所需权限,
|
||||||
|
* 并使用访问控制管理器验证用户是否具备该权限。
|
||||||
|
*
|
||||||
|
* @param authentication 当前的认证信息流,包含用户身份和权限
|
||||||
|
* @param context 授权上下文,包含请求和交换信息
|
||||||
|
* @return Mono<AuthorizationDecision> 返回一个授权决策对象的流
|
||||||
|
*/
|
||||||
|
override fun check(
|
||||||
|
authentication: Mono<Authentication>,
|
||||||
|
context: AuthorizationContext
|
||||||
|
): Mono<AuthorizationDecision> {
|
||||||
|
return authentication
|
||||||
|
.map { auth ->
|
||||||
|
// 获取请求相关的信息
|
||||||
|
val exchange = context.exchange
|
||||||
|
val request = exchange.request
|
||||||
|
|
||||||
|
// 提取请求路径和方法
|
||||||
|
val path = request.path.toString()
|
||||||
|
val method = request.method
|
||||||
|
|
||||||
|
// 动态获取要校验的权限(可从 path、header、注解、上下文等推断)
|
||||||
|
val requiredPermission = permissionResolver.resolve(path, method.name())
|
||||||
|
|
||||||
|
// 判断用户是否拥有该权限
|
||||||
|
val granted = accessManager.hasPermission(auth, requiredPermission)
|
||||||
|
// 创建授权决策结果
|
||||||
|
AuthorizationDecision(granted)
|
||||||
|
}
|
||||||
|
.defaultIfEmpty(AuthorizationDecision(false))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.aspect
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.annotations.RequiresPermission
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.manager.AccessManager
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.strategy.AuthorizationStrategy
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.impl.resolver.SpELResolver
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint
|
||||||
|
import org.aspectj.lang.annotation.Around
|
||||||
|
import org.aspectj.lang.annotation.Aspect
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature
|
||||||
|
import org.springframework.security.access.AccessDeniedException
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权切面
|
||||||
|
*
|
||||||
|
* 该切面用于处理带有 [RequiresPermission] 注解的方法,执行权限校验逻辑。
|
||||||
|
* 通过查找适用的 [AuthorizationStrategy] 来决定是否允许当前用户执行目标方法。
|
||||||
|
*
|
||||||
|
* @property spELResolver SpEL 表达式解析器,用于动态解析权限字符串。
|
||||||
|
* @property accessManager 访问控制管理器,用于最终的权限判断。
|
||||||
|
*
|
||||||
|
* @since 2025-06-15 13:17:18
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
class RequiresPermissionAspect(
|
||||||
|
private val spELResolver: SpELResolver,
|
||||||
|
private val accessManager: AccessManager
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 环绕通知方法,处理带有 [RequiresPermission] 注解的方法调用。
|
||||||
|
*
|
||||||
|
* 执行流程:
|
||||||
|
* 1. 获取当前用户的认证信息;
|
||||||
|
* 2. 获取目标方法及其参数;
|
||||||
|
* 3. 根据注解配置决定是否解析 SpEL 表达式获取实际权限标识;
|
||||||
|
* 4. 查找适用的授权策略并执行授权逻辑;
|
||||||
|
* 5. 如果授权成功,则继续执行目标方法。
|
||||||
|
*
|
||||||
|
* @param joinPoint ProceedingJoinPoint,代表被拦截的方法执行连接点。
|
||||||
|
* @param permission 方法上标注的 [RequiresPermission] 注解实例。
|
||||||
|
* @return 目标方法的返回值。
|
||||||
|
*/
|
||||||
|
@Around("@annotation(permission)")
|
||||||
|
fun around(joinPoint: ProceedingJoinPoint, permission: RequiresPermission): Any {
|
||||||
|
// 获取当前用户的安全上下文认证信息
|
||||||
|
val auth = SecurityContextHolder.getContext().authentication
|
||||||
|
|
||||||
|
// 根据 dynamic 配置决定是否解析 SpEL 表达式,得到最终权限标识
|
||||||
|
val perm = if (permission.dynamic) {
|
||||||
|
resolveSpEl(permission.value, joinPoint)
|
||||||
|
} else permission.value
|
||||||
|
|
||||||
|
// 使用 accessManager 进行权限校验,失败时抛出访问拒绝异常
|
||||||
|
if (!accessManager.hasPermission(auth, perm))
|
||||||
|
throw AccessDeniedException("权限不足,需要 [$perm]")
|
||||||
|
|
||||||
|
// 权限校验通过,继续执行目标方法
|
||||||
|
return joinPoint.proceed()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 SpEL 表达式,形参名可直接用 #变量 方式引用
|
||||||
|
*
|
||||||
|
* 例:@RequiresPermission(dynamic = true, value = "'article:read:' + #articleId")
|
||||||
|
*
|
||||||
|
* @param expression 要解析的 SpEL 表达式。
|
||||||
|
* @param joinPoint ProceedingJoinPoint,从中提取方法参数。
|
||||||
|
* @return 解析后的实际权限字符串。
|
||||||
|
*/
|
||||||
|
private fun resolveSpEl(expression: String, joinPoint: ProceedingJoinPoint): String {
|
||||||
|
val methodSignature = joinPoint.signature as MethodSignature
|
||||||
|
val paramNames = methodSignature.parameterNames // ["articleId", "commentId", …]
|
||||||
|
val args = joinPoint.args // [123L, 456L, …]
|
||||||
|
|
||||||
|
// 组装成 Map<String, Any?>
|
||||||
|
val argsMap = paramNames
|
||||||
|
.mapIndexed { index, name -> name to args[index] }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
return spELResolver.parse(expression, argsMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.builder
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.builder.SecurityFilterChainRegistrar
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.customizer.HttpSecurityCustomizer
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.extension.cleanUnNeedConfig
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.wrapper.SecurityFilterChainWrapper
|
||||||
|
import org.springframework.http.HttpMethod
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler
|
||||||
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无状态安全过滤器链注册商
|
||||||
|
*
|
||||||
|
* 此类用于注册和构建无状态的安全过滤器链,适用于基于令牌的身份验证场景。
|
||||||
|
* 它通过定制 HttpSecurity 配置来实现特定的访问控制策略。
|
||||||
|
*
|
||||||
|
* @property httpSecurityCustomizer 提供针对特定链的自定义配置逻辑
|
||||||
|
* @property authorizationManager 负责处理请求的授权逻辑
|
||||||
|
* @property accessDeniedHandler 处理未授权访问时的响应逻辑
|
||||||
|
* @since 2025-06-24 16:14:55
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class StatelessSecurityFilterChainRegistrar(
|
||||||
|
private val httpSecurityCustomizer: List<HttpSecurityCustomizer>,
|
||||||
|
private val authorizationManager: AuthorizationManager<RequestAuthorizationContext>,
|
||||||
|
private val accessDeniedHandler: AccessDeniedHandler,
|
||||||
|
) : SecurityFilterChainRegistrar {
|
||||||
|
/**
|
||||||
|
* 构建安全过滤器链
|
||||||
|
*
|
||||||
|
* 此方法用于基于提供的匹配器和配置逻辑创建一个定制化的安全过滤器链。
|
||||||
|
*
|
||||||
|
* @param chainId 唯一标识此过滤器链的字符串,用于后续引用或管理
|
||||||
|
* @param http 提供基础配置的 HttpSecurity 对象,用于构建 Web 安全策略
|
||||||
|
* @param matcher 用于匹配请求的 RequestMatcher 对象,决定是否应用该过滤器链
|
||||||
|
* @param config 用于配置安全策略的函数,接受 ServerHttpSecurity 对象作为参数进行自定义配置
|
||||||
|
* @return 返回构建完成的 SecurityWebFilterChainWrapper 实例,封装了最终的安全过滤器链
|
||||||
|
*/
|
||||||
|
override fun buildChain(
|
||||||
|
chainId: String,
|
||||||
|
http: HttpSecurity,
|
||||||
|
matcher: RequestMatcher,
|
||||||
|
config: (HttpSecurity) -> Unit,
|
||||||
|
): SecurityFilterChainWrapper {
|
||||||
|
// 清除不必要的默认安全配置,优化无状态场景下的行为
|
||||||
|
// 设置请求匹配器以确定过滤器链适用范围
|
||||||
|
// 配置 HTTP 请求的授权规则:
|
||||||
|
// - 允许所有 OPTIONS 请求(通常用于跨域预检)
|
||||||
|
// - 所有其他请求都需要通过授权管理器验证
|
||||||
|
// 配置异常处理逻辑,使用指定的访问拒绝处理器
|
||||||
|
// 应用额外的自定义配置闭包
|
||||||
|
http
|
||||||
|
.cleanUnNeedConfig()
|
||||||
|
.securityMatcher(matcher)
|
||||||
|
.authorizeHttpRequests {
|
||||||
|
it.requestMatchers(HttpMethod.OPTIONS).permitAll()
|
||||||
|
it.anyRequest().access(authorizationManager)
|
||||||
|
}
|
||||||
|
.exceptionHandling { it.accessDeniedHandler(accessDeniedHandler) }
|
||||||
|
.also { config(it) }
|
||||||
|
|
||||||
|
// 过滤出支持当前链 ID 的定制器并依次应用它们的自定义逻辑
|
||||||
|
httpSecurityCustomizer.filter { it.supports(chainId) }.forEach { it.customize(http) }
|
||||||
|
|
||||||
|
// 构建并返回封装好的安全过滤器链包装对象
|
||||||
|
return SecurityFilterChainWrapper(http.build(), chainId)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.builder
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.builder.SecurityWebFilterChainRegistrar
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.customizer.ServerHttpSecurityCustomizer
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.extension.cleanUnNeedConfig
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.wrapper.SecurityWebFilterChainWrapper
|
||||||
|
import org.springframework.http.HttpMethod
|
||||||
|
import org.springframework.security.authorization.ReactiveAuthorizationManager
|
||||||
|
import org.springframework.security.config.web.server.ServerHttpSecurity
|
||||||
|
import org.springframework.security.web.server.authorization.AuthorizationContext
|
||||||
|
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler
|
||||||
|
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher
|
||||||
|
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无状态安全 Web 过滤器链注册商
|
||||||
|
*
|
||||||
|
* 用于创建和管理无状态认证场景下的安全过滤器链。
|
||||||
|
*
|
||||||
|
* @property serverHttpSecurityCustomizer 安全配置定制器列表,按需对安全配置进行个性化调整
|
||||||
|
* @since 2025-06-24 12:40:01
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class StatelessSecurityWebFilterChainRegistrar(
|
||||||
|
private val serverHttpSecurityCustomizer: List<ServerHttpSecurityCustomizer>,
|
||||||
|
private val reactiveAuthorizationManager: ReactiveAuthorizationManager<AuthorizationContext>,
|
||||||
|
private val reactiveAccessDeniedHandler: ServerAccessDeniedHandler,
|
||||||
|
) : SecurityWebFilterChainRegistrar {
|
||||||
|
/**
|
||||||
|
* 构建安全过滤器链
|
||||||
|
*
|
||||||
|
* 此方法基于传入的匹配器和配置逻辑创建一个定制化的安全策略,并将其封装为可管理的安全过滤器链实例。
|
||||||
|
* 方法内部主要完成基础安全配置清理、请求匹配规则设置、权限验证管理、异常处理机制以及自定义配置扩展,
|
||||||
|
* 最终构建并返回封装好的安全过滤器链对象。
|
||||||
|
*
|
||||||
|
* @param chainId 唯一标识此过滤器链的字符串,用于后续引用或管理
|
||||||
|
* @param http 提供基础配置的 ServerHttpSecurity 对象,用于构建安全策略
|
||||||
|
* @param matcher 用于匹配请求的 ServerWebExchangeMatcher 对象
|
||||||
|
* @param config 用于配置安全策略的函数,接受 ServerHttpSecurity 对象作为参数
|
||||||
|
* @return 返回构建完成的 SecurityWebFilterChainWrapper 实例,包含已配置的安全策略及链标识
|
||||||
|
*/
|
||||||
|
override fun buildChain(
|
||||||
|
chainId: String,
|
||||||
|
http: ServerHttpSecurity,
|
||||||
|
matcher: ServerWebExchangeMatcher,
|
||||||
|
config: (ServerHttpSecurity) -> Unit,
|
||||||
|
): SecurityWebFilterChainWrapper {
|
||||||
|
/**
|
||||||
|
* 安全配置流程:
|
||||||
|
* 1. 清理不必要且默认的安全配置;
|
||||||
|
* 2. 设置请求匹配规则;
|
||||||
|
* 3. 配置访问控制策略(允许 OPTIONS 请求并限制其他请求);
|
||||||
|
* 4. 设置访问被拒绝时的异常处理器;
|
||||||
|
* 5. 应用额外的自定义配置。
|
||||||
|
*/
|
||||||
|
http
|
||||||
|
.cleanUnNeedConfig()
|
||||||
|
.securityMatcher(matcher)
|
||||||
|
.authorizeExchange {
|
||||||
|
/**
|
||||||
|
* 显式允许所有 OPTIONS 预检请求,确保跨域请求能正常进行预检。
|
||||||
|
*/
|
||||||
|
it.matchers(ServerWebExchangeMatchers.pathMatchers(HttpMethod.OPTIONS, "/**"))
|
||||||
|
.permitAll()
|
||||||
|
/**
|
||||||
|
* 所有其他类型的请求都需要通过 reactiveAuthorizationManager 进行访问控制。
|
||||||
|
*/
|
||||||
|
it
|
||||||
|
.anyExchange()
|
||||||
|
.access(reactiveAuthorizationManager)
|
||||||
|
}
|
||||||
|
.exceptionHandling {
|
||||||
|
/**
|
||||||
|
* 设置全局访问被拒绝处理器,用于统一处理无权访问的请求。
|
||||||
|
*/
|
||||||
|
it.accessDeniedHandler(reactiveAccessDeniedHandler)
|
||||||
|
}
|
||||||
|
.also { config(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 筛选出支持当前 chainId 的定制器,并依次对当前 http 配置进行扩展定制,
|
||||||
|
* 实现更细粒度的链级安全策略调整。
|
||||||
|
*/
|
||||||
|
serverHttpSecurityCustomizer.filter { it.supports(chainId) }.forEach { it.customize(http) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用构建完成的 SecurityWebFilterChain 实例和链 ID 创建包装对象并返回,
|
||||||
|
* 便于上层组件管理和使用该安全链。
|
||||||
|
*/
|
||||||
|
return SecurityWebFilterChainWrapper(
|
||||||
|
http.build(),
|
||||||
|
chainId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.handler
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.core.extension.log
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.config.SecurityAuthorizeProperties
|
||||||
|
import com.gewuyou.forgeboot.webmvc.dto.R
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.security.access.AccessDeniedException
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权异常处理程序,用于处理访问被拒绝的请求并返回对应的 JSON 响应。
|
||||||
|
*
|
||||||
|
* @property objectMapper 用于将响应对象序列化为 JSON 字符串
|
||||||
|
* @property securityAuthorizeProperties 安全授权相关配置属性
|
||||||
|
* @author gewuyou
|
||||||
|
* @since 2025-06-24 16:35:48
|
||||||
|
*/
|
||||||
|
class AuthorizationExceptionHandler(
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val securityAuthorizeProperties: SecurityAuthorizeProperties
|
||||||
|
) : AccessDeniedHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理访问被拒绝的异常,并写入 JSON 格式的响应体。
|
||||||
|
*
|
||||||
|
* @param request 发生异常的 HTTP 请求
|
||||||
|
* @param response 需要返回的 HTTP 响应
|
||||||
|
* @param accessDeniedException 具体访问被拒绝异常
|
||||||
|
*/
|
||||||
|
override fun handle(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
accessDeniedException: AccessDeniedException
|
||||||
|
) {
|
||||||
|
// 记录访问被拒绝的异常日志,包含请求路径和异常信息
|
||||||
|
log.error("授权异常:路径=${request.requestURI}", accessDeniedException)
|
||||||
|
|
||||||
|
response.status = HttpStatus.OK.value()
|
||||||
|
// 设置响应内容类型为 JSON 格式
|
||||||
|
response.contentType = MediaType.APPLICATION_JSON_VALUE
|
||||||
|
// 设置字符编码为 UTF-8
|
||||||
|
response.characterEncoding = "UTF-8"
|
||||||
|
|
||||||
|
// 构建错误响应 JSON 字符串
|
||||||
|
val errorJson = objectMapper.writeValueAsString(
|
||||||
|
R.failure<String>(
|
||||||
|
HttpStatus.FORBIDDEN.value(),
|
||||||
|
accessDeniedException.message ?: securityAuthorizeProperties.defaultExceptionResponse
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// 将错误响应写入 HTTP 响应输出流
|
||||||
|
response.writer.write(errorJson)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.handler
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.gewuyou.forgeboot.core.extension.log
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.config.SecurityAuthorizeProperties
|
||||||
|
import com.gewuyou.forgeboot.webmvc.dto.R
|
||||||
|
import org.springframework.http.HttpHeaders
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.security.access.AccessDeniedException
|
||||||
|
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler
|
||||||
|
import org.springframework.web.server.ServerWebExchange
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反应式授权异常处理程序
|
||||||
|
*
|
||||||
|
* 处理访问被拒绝的授权异常,生成相应的错误响应。
|
||||||
|
*
|
||||||
|
* @property objectMapper 用于将响应对象序列化为 JSON 字符串
|
||||||
|
* @property securityAuthorizeProperties 安全授权配置属性
|
||||||
|
* @since 2025-06-23 22:03:09
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class ReactiveAuthorizationExceptionHandler(
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val securityAuthorizeProperties: SecurityAuthorizeProperties,
|
||||||
|
) : ServerAccessDeniedHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理访问被拒绝的异常,并返回自定义错误响应。
|
||||||
|
*
|
||||||
|
* 记录异常日志,设置响应状态码和内容类型,并将错误信息以 JSON 格式写入响应。
|
||||||
|
*
|
||||||
|
* @param exchange 当前的服务器网络交换对象
|
||||||
|
* @param denied 抛出的访问被拒绝异常
|
||||||
|
* @return 返回一个 Mono<Void> 表示处理完成
|
||||||
|
*/
|
||||||
|
override fun handle(exchange: ServerWebExchange, denied: AccessDeniedException): Mono<Void> {
|
||||||
|
// 记录访问被拒绝的异常信息及请求路径
|
||||||
|
log.error("反应式授权异常 路径:${exchange.request.uri}", denied)
|
||||||
|
|
||||||
|
// 设置响应状态码为 200 OK
|
||||||
|
exchange.response.statusCode = HttpStatus.OK
|
||||||
|
|
||||||
|
// 设置响应内容类型为 application/json
|
||||||
|
exchange.response.headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
|
||||||
|
// 将错误响应体写入响应
|
||||||
|
return exchange.response.writeWith(
|
||||||
|
Mono.just(
|
||||||
|
exchange.response.bufferFactory().wrap(
|
||||||
|
objectMapper.writeValueAsBytes(
|
||||||
|
R.failure<String>(
|
||||||
|
HttpStatus.FORBIDDEN.value(),
|
||||||
|
denied.message ?: securityAuthorizeProperties.defaultExceptionResponse
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.manager
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.manager.AccessManager
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.provider.PermissionProvider
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.strategy.AuthorizationStrategy
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
|
||||||
|
/**
|
||||||
|
*默认 Access Manager
|
||||||
|
*
|
||||||
|
* @since 2025-06-15 18:41:22
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class DefaultAccessManager(
|
||||||
|
private val providers: List<PermissionProvider>,
|
||||||
|
private val strategy : AuthorizationStrategy
|
||||||
|
) : AccessManager {
|
||||||
|
/**
|
||||||
|
* 判断用户是否具有指定权限
|
||||||
|
*
|
||||||
|
* @param authentication Spring Security提供的身份认证信息
|
||||||
|
* @param permission 需要验证的权限字符串
|
||||||
|
* @return Boolean 返回是否有权限的布尔值
|
||||||
|
*/
|
||||||
|
override fun hasPermission(authentication: Authentication, permission: String): Boolean =
|
||||||
|
strategy.authorize(authentication, permission, providers)
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.provider
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.core.extension.log
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.provider.PermissionProvider
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
|
||||||
|
/**
|
||||||
|
*抽象权限提供程序
|
||||||
|
*
|
||||||
|
* @since 2025-06-23 21:45:49
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
abstract class AbstractPermissionProvider : PermissionProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取权限集合模板方法
|
||||||
|
*/
|
||||||
|
override fun listPermissions(authentication: Authentication): Collection<String> {
|
||||||
|
if (!supports(authentication)) return emptySet()
|
||||||
|
return try {
|
||||||
|
doProvide(authentication).toSet()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
log.error("获取权限失败", ex)
|
||||||
|
emptySet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子类实现:获取权限的具体逻辑
|
||||||
|
*/
|
||||||
|
protected abstract fun doProvide(authentication: Authentication): Collection<String>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否支持当前 Authentication 类型
|
||||||
|
*/
|
||||||
|
protected open fun supports(authentication: Authentication): Boolean = true
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.resolver
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.resolver.PermissionResolver
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认权限解析程序
|
||||||
|
*
|
||||||
|
* @since 2025-06-24 13:42:20
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class DefaultPermissionResolver : PermissionResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将请求路径和HTTP方法解析为标准化的权限标识符
|
||||||
|
*
|
||||||
|
* 此方法会对原始路径进行标准化处理,替换其中的动态参数部分为统一占位符,
|
||||||
|
* 然后将HTTP方法和标准化后的路径组合成最终的权限标识符。
|
||||||
|
*
|
||||||
|
* @param path 请求路径
|
||||||
|
* @param method HTTP请求方法
|
||||||
|
* @return 标准化的权限标识符,格式为"METHOD:/path/template"
|
||||||
|
*/
|
||||||
|
override fun resolve(path: String, method: String): String {
|
||||||
|
// 对路径进行标准化处理:
|
||||||
|
// 1. 转换为小写
|
||||||
|
// 2. 替换数字ID为{id}通配符
|
||||||
|
// 3. 替换MongoDB ObjectId为{objectId}
|
||||||
|
// 4. 替换UUID为{uuid}
|
||||||
|
val normalizedPath = path
|
||||||
|
.lowercase()
|
||||||
|
.replace(Regex("/\\d+"), "/{id}")
|
||||||
|
.replace(Regex("/[a-f0-9]{24}"), "/{objectId}")
|
||||||
|
.replace(Regex("/[a-z0-9\\-]{36}"), "/{uuid}")
|
||||||
|
|
||||||
|
return "${method}:$normalizedPath"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.resolver
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory
|
||||||
|
import org.springframework.context.expression.BeanFactoryResolver
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpEL 解析器,用于解析和执行 Spring Expression Language (SpEL) 表达式。
|
||||||
|
*
|
||||||
|
* 该类封装了 SpEL 的解析和求值过程,结合 Spring 的 BeanFactory 支持表达式中对 Bean 的引用。
|
||||||
|
*
|
||||||
|
* @property beanFactory 提供 Spring 容器的 Bean 工厂,用于解析表达式中的 Bean 引用。
|
||||||
|
* @since 2025-06-15 13:32:31
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class SpELResolver(private val beanFactory: ConfigurableBeanFactory) {
|
||||||
|
// 初始化 SpEL 表达式解析器
|
||||||
|
private val parser = SpelExpressionParser()
|
||||||
|
// 初始化 SpEL 求值上下文,并设置 Bean 解析器
|
||||||
|
private val context = StandardEvaluationContext()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在初始化阶段配置上下文,使其能够通过 BeanFactory 解析表达式中的 Bean。
|
||||||
|
*/
|
||||||
|
init {
|
||||||
|
context.beanResolver = BeanFactoryResolver(beanFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析并计算给定的 SpEL 表达式,返回结果作为字符串。
|
||||||
|
*
|
||||||
|
* @param expression 表达式字符串,例如 `#user.name` 或 `'Hello ' + #name`
|
||||||
|
* @param rootObject 根对象,表达式中的属性访问将基于此对象进行解析
|
||||||
|
* @return 解析后的字符串结果,如果表达式结果为 null,则返回空字符串
|
||||||
|
*/
|
||||||
|
fun parse(expression: String, rootObject: Any): String {
|
||||||
|
context.setRootObject(rootObject)
|
||||||
|
|
||||||
|
// 如果是 Map,则把每个键当作变量注入,支持 #varName 直接取值
|
||||||
|
if (rootObject is Map<*, *>) {
|
||||||
|
rootObject.forEach { (k, v) ->
|
||||||
|
if (k is String) context.setVariable(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parser
|
||||||
|
.parseExpression(expression)
|
||||||
|
.getValue(context, String::class.java) ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.authorize.impl.strategy
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.provider.PermissionProvider
|
||||||
|
import com.gewuyou.forgeboot.security.authorize.api.strategy.AuthorizationStrategy
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任何匹配策略 - 实现授权策略接口,采用任意权限匹配机制
|
||||||
|
*
|
||||||
|
* @since 2025-06-15 18:51:23
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class AnyMatchStrategy : AuthorizationStrategy {
|
||||||
|
/**
|
||||||
|
* 执行授权验证
|
||||||
|
*
|
||||||
|
* @param authentication 当前用户身份认证信息
|
||||||
|
* @param permission 需要验证的权限标识
|
||||||
|
* @param providers 权限提供者列表
|
||||||
|
* @return Boolean 返回授权结果,true表示授权通过,false表示拒绝访问
|
||||||
|
*
|
||||||
|
* 该方法通过遍历所有权限提供者,只要有一个提供者包含目标权限即视为授权成功
|
||||||
|
*/
|
||||||
|
override fun authorize(
|
||||||
|
authentication: Authentication,
|
||||||
|
permission: String,
|
||||||
|
providers: List<PermissionProvider>
|
||||||
|
) = providers.any { permission in it.listPermissions(authentication) }
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly(libs.springBootStarter.security)
|
||||||
|
compileOnly(libs.springBootStarter.web)
|
||||||
|
kapt(libs.springBoot.configuration.processor)
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.authenticate.entities.request
|
||||||
|
|
||||||
|
/**
|
||||||
|
*认证请求
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 15:39:04
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
open class AuthenticationRequest(
|
||||||
|
/**
|
||||||
|
* 认证类型
|
||||||
|
*/
|
||||||
|
open val type: String
|
||||||
|
): LoginRequest {
|
||||||
|
override fun getType(): String {
|
||||||
|
return type
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.authenticate.entities.request
|
||||||
|
|
||||||
|
/**
|
||||||
|
*默认登录请求
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 16:29:43
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class DefaultLoginRequest {
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.authenticate.entities.request
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT身份验证请求数据类
|
||||||
|
*
|
||||||
|
* 该类用于封装基于JWT的身份验证请求信息,继承自[AuthenticationRequest]。
|
||||||
|
* 主要包含身份验证类型和JWT令牌两个属性,适用于系统中的身份认证流程。
|
||||||
|
*
|
||||||
|
* @property type 身份验证的类型,覆盖父类属性,默认值为"jwt"
|
||||||
|
* @property token JWT访问令牌,用于身份验证和授权
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 15:41:22
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
data class JwtAuthenticationRequest(
|
||||||
|
override val type: String = "jwt",
|
||||||
|
val token: String
|
||||||
|
) : AuthenticationRequest(type)
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.authenticate.entities.request
|
||||||
|
|
||||||
|
/**
|
||||||
|
*登录请求
|
||||||
|
*
|
||||||
|
* @since 2025-02-15 02:13:16
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface LoginRequest {
|
||||||
|
fun getType(): String
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.authenticate.entities.request
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名密码认证请求类
|
||||||
|
*
|
||||||
|
* 该类用于封装基于用户名和密码的认证请求数据,继承自[AuthenticationRequest]。
|
||||||
|
* 提供了用户名和密码字段,并设置默认认证类型为"password"。
|
||||||
|
*
|
||||||
|
* @property type 认证类型,默认值为"password"
|
||||||
|
* @property username 用户输入的用户名
|
||||||
|
* @property password 用户输入的密码
|
||||||
|
*
|
||||||
|
* @author gewuyou
|
||||||
|
* @since 2025-06-10 15:40:17
|
||||||
|
*/
|
||||||
|
data class UsernamePasswordAuthenticationRequest(
|
||||||
|
override val type: String = "username",
|
||||||
|
val username: String,
|
||||||
|
val password: String
|
||||||
|
) : AuthenticationRequest(type)
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.authenticate.entities.response
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 身份验证响应基类
|
||||||
|
*
|
||||||
|
* 该类用于封装身份验证成功后的基础响应数据,包含认证主体和凭证信息。
|
||||||
|
* 主要用于框架内部在完成身份验证后构造统一的响应结构。
|
||||||
|
*
|
||||||
|
* @property principal 认证主体对象,可以是用户信息或其他认证实体
|
||||||
|
* @property credentials 认证凭证信息,例如密码、令牌等敏感数据载体
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 15:49:03
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
open class AuthenticationResponse(
|
||||||
|
open val principal: Any? = null,
|
||||||
|
open val credentials: Any? = null,
|
||||||
|
)
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.authenticate.entities.response
|
||||||
|
|
||||||
|
/**
|
||||||
|
*JWT身份验证响应
|
||||||
|
*
|
||||||
|
* @since 2025-06-10 15:50:10
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
data class JwtAuthenticationResponse(
|
||||||
|
override val principal: Any?,
|
||||||
|
val accessToken: String,
|
||||||
|
val refreshToken: String,
|
||||||
|
val expiresIn: Long,
|
||||||
|
) : AuthenticationResponse(
|
||||||
|
principal = principal,
|
||||||
|
credentials = accessToken
|
||||||
|
)
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.builder
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.wrapper.SecurityFilterChainWrapper
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全过滤器链注册商
|
||||||
|
*
|
||||||
|
* 该函数接口用于定义构建安全过滤器链的契约,通过提供唯一的链标识、基础配置对象、请求匹配规则以及配置逻辑,
|
||||||
|
* 可以创建一个定制化的安全过滤器链实例。
|
||||||
|
*
|
||||||
|
* @since 2025-06-24 16:07:19
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface SecurityFilterChainRegistrar {
|
||||||
|
/**
|
||||||
|
* 构建安全过滤器链
|
||||||
|
*
|
||||||
|
* 此方法用于基于提供的匹配器和配置逻辑创建一个定制化的安全过滤器链。
|
||||||
|
* 方法内部应实现将给定的配置应用到 HTTP 安全对象,并根据 matcher 决定是否启用该链。
|
||||||
|
*
|
||||||
|
* @param chainId 唯一标识此过滤器链的字符串,用于后续引用或管理
|
||||||
|
* @param http 提供基础配置的 HttpSecurity 对象,用于构建 Web 安全策略
|
||||||
|
* @param matcher 用于匹配请求的 RequestMatcher 对象,决定是否应用该过滤器链
|
||||||
|
* @param config 用于配置安全策略的函数,接受 HttpSecurity 对象作为参数进行自定义配置
|
||||||
|
* @return 返回构建完成的 SecurityFilterChainWrapper 实例,封装了最终的安全过滤器链
|
||||||
|
*/
|
||||||
|
fun buildChain(
|
||||||
|
chainId: String,
|
||||||
|
http: HttpSecurity,
|
||||||
|
matcher: RequestMatcher,
|
||||||
|
config: (HttpSecurity) -> Unit
|
||||||
|
): SecurityFilterChainWrapper
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.builder
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.wrapper.SecurityWebFilterChainWrapper
|
||||||
|
import org.springframework.security.config.web.server.ServerHttpSecurity
|
||||||
|
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全 Web 过滤器链注册商接口
|
||||||
|
*
|
||||||
|
* 用于构建和注册自定义的安全过滤器链,适用于响应式 Spring Security 配置。
|
||||||
|
*
|
||||||
|
* @since 2025-06-24 12:35:00
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
fun interface SecurityWebFilterChainRegistrar {
|
||||||
|
/**
|
||||||
|
* 构建安全过滤器链
|
||||||
|
*
|
||||||
|
* 此方法用于基于提供的匹配器和配置逻辑创建一个定制化的安全过滤器链。
|
||||||
|
*
|
||||||
|
* @param chainId 唯一标识此过滤器链的字符串,用于后续引用或管理
|
||||||
|
* @param http 提供基础配置的 ServerHttpSecurity 对象,用于构建安全策略
|
||||||
|
* @param matcher 用于匹配请求的 ServerWebExchangeMatcher 对象
|
||||||
|
* @param config 用于配置安全策略的函数,接受 ServerHttpSecurity 对象作为参数
|
||||||
|
* @return 返回构建完成的 ForgeBootSecurityWebFilterChain 实例
|
||||||
|
*/
|
||||||
|
fun buildChain(
|
||||||
|
chainId: String,
|
||||||
|
http: ServerHttpSecurity,
|
||||||
|
matcher: ServerWebExchangeMatcher,
|
||||||
|
config: (ServerHttpSecurity) -> Unit
|
||||||
|
): SecurityWebFilterChainWrapper
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.customizer
|
||||||
|
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP 安全定制器接口
|
||||||
|
*
|
||||||
|
* 该函数式接口用于定义 HTTP 安全配置的定制逻辑,通常用于
|
||||||
|
* Spring Security 的 HttpSecurity 配置阶段。
|
||||||
|
*
|
||||||
|
* @since 2025-06-24 10:46:32
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface HttpSecurityCustomizer {
|
||||||
|
/**
|
||||||
|
* 判断当前定制器是否支持处理指定的安全链配置
|
||||||
|
*
|
||||||
|
* 此方法用于标识该定制器是否适用于特定的安全链配置。
|
||||||
|
* 实现类应根据 chainId 参数决定是否启用此定制器的逻辑。
|
||||||
|
*
|
||||||
|
* @param chainId 安全链的唯一标识符,用于区分不同的安全配置场景
|
||||||
|
* @return Boolean 返回 true 表示支持该 chainId,否则不支持
|
||||||
|
*/
|
||||||
|
fun supports(chainId: String): Boolean
|
||||||
|
/**
|
||||||
|
* 执行安全配置的定制逻辑
|
||||||
|
*
|
||||||
|
* @param http 用于构建 HTTP 安全策略的 HttpSecurity 实例
|
||||||
|
* 通过此参数可添加或修改安全规则,如认证、授权等
|
||||||
|
*/
|
||||||
|
fun customize(http: HttpSecurity)
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.customizer
|
||||||
|
|
||||||
|
import org.springframework.security.config.web.server.ServerHttpSecurity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器 HTTP 安全定制器接口
|
||||||
|
*
|
||||||
|
* 该接口用于定义对 Spring Security 的 ServerHttpSecurity 配置的自定义逻辑。
|
||||||
|
* 实现类可以通过重写 customize 方法来添加或修改安全配置规则。
|
||||||
|
*
|
||||||
|
* @since 2025-06-24 10:45:42
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface ServerHttpSecurityCustomizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前定制器是否支持处理指定的安全链配置
|
||||||
|
*
|
||||||
|
* 此方法用于标识该定制器是否适用于特定的安全链配置。
|
||||||
|
* 实现类应根据 chainId 参数决定是否启用此定制器的逻辑。
|
||||||
|
*
|
||||||
|
* @param chainId 安全链的唯一标识符,用于区分不同的安全配置场景
|
||||||
|
* @return Boolean 返回 true 表示支持该 chainId,否则不支持
|
||||||
|
*/
|
||||||
|
fun supports(chainId: String): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义 ServerHttpSecurity 配置的方法
|
||||||
|
*
|
||||||
|
* 此方法由框架调用,允许开发者插入自定义的安全配置逻辑。
|
||||||
|
* 方法参数提供了 ServerHttpSecurity 实例,可用于链式配置。
|
||||||
|
*
|
||||||
|
* @param http ServerHttpSecurity 实例,用于构建 WebFlux 安全配置
|
||||||
|
*/
|
||||||
|
fun customize(http: ServerHttpSecurity)
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.extension
|
||||||
|
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy
|
||||||
|
import org.springframework.security.web.context.NullSecurityContextRepository
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理不需要的配置,禁用指定的安全功能模块。
|
||||||
|
*
|
||||||
|
* 此方法用于关闭一系列默认的安全配置项,适用于无状态(stateless)认证场景,
|
||||||
|
* 例如基于Token或API密钥的身份验证架构。
|
||||||
|
*
|
||||||
|
* @return 返回当前 HttpSecurity 实例,用于链式调用
|
||||||
|
*/
|
||||||
|
fun HttpSecurity.cleanUnNeedConfig(): HttpSecurity {
|
||||||
|
return this
|
||||||
|
/**
|
||||||
|
* 禁用 CSRF 保护机制。
|
||||||
|
* 在无状态 API 场景中通常不需要 CSRF 保护。
|
||||||
|
*/
|
||||||
|
.csrf { it.disable() }
|
||||||
|
/**
|
||||||
|
* 禁用表单登录认证机制。
|
||||||
|
* 表单登录不适用于 RESTful API 或 Token 认证场景。
|
||||||
|
*/
|
||||||
|
.formLogin { it.disable() }
|
||||||
|
/**
|
||||||
|
* 设置 SecurityContextRepository 为 NullSecurityContextRepository。
|
||||||
|
* 避免将安全上下文存储到 HTTP 请求中。
|
||||||
|
*/
|
||||||
|
.securityContext { it.securityContextRepository(NullSecurityContextRepository()) }
|
||||||
|
/**
|
||||||
|
* 设置会话创建策略为 STATELESS。
|
||||||
|
* 表示应用不会创建或使用 HTTP 会话进行身份识别。
|
||||||
|
*/
|
||||||
|
.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
|
||||||
|
/**
|
||||||
|
* 禁用匿名用户访问支持。
|
||||||
|
* 所有未认证用户将被视为无权限。
|
||||||
|
*/
|
||||||
|
.anonymous { it.disable() }
|
||||||
|
/**
|
||||||
|
* 禁用请求缓存。
|
||||||
|
* 在无状态认证流程中不需要缓存请求。
|
||||||
|
*/
|
||||||
|
.requestCache { it.disable() }
|
||||||
|
/**
|
||||||
|
* 禁用 HTTP Basic 认证。
|
||||||
|
* 使用 Token 或其他自定义认证方式时可安全禁用此项。
|
||||||
|
*/
|
||||||
|
.httpBasic { it.disable() }
|
||||||
|
/**
|
||||||
|
* 禁用 Remember-Me 自动登录功能。
|
||||||
|
* 无状态认证场景下此功能不再适用。
|
||||||
|
*/
|
||||||
|
.rememberMe { it.disable() }
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.extension
|
||||||
|
|
||||||
|
import org.springframework.security.config.web.server.ServerHttpSecurity
|
||||||
|
import org.springframework.security.web.server.context.NoOpServerSecurityContextRepository
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理不需要的配置,关闭清单如下:
|
||||||
|
*
|
||||||
|
* 禁用以下 Spring Security 功能以简化安全配置:
|
||||||
|
* - CSRF 保护
|
||||||
|
* - 表单登录认证
|
||||||
|
* - 匿名用户支持
|
||||||
|
* - 请求缓存
|
||||||
|
* - HTTP Basic 认证
|
||||||
|
* - 登出功能
|
||||||
|
*
|
||||||
|
* 同时设置一个无操作的安全上下文仓库,适用于无需持久化安全上下文的场景。
|
||||||
|
*
|
||||||
|
* @return 返回配置修改后的 [ServerHttpSecurity] 实例
|
||||||
|
*/
|
||||||
|
fun ServerHttpSecurity.cleanUnNeedConfig(): ServerHttpSecurity {
|
||||||
|
return this
|
||||||
|
.csrf { it.disable() }
|
||||||
|
.formLogin { it.disable() }
|
||||||
|
.anonymous { it.disable() }
|
||||||
|
.requestCache { it.disable() }
|
||||||
|
.httpBasic { it.disable() }
|
||||||
|
.logout { it.disable() }
|
||||||
|
.securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.identifier
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全过滤器链标识符函数式接口
|
||||||
|
*
|
||||||
|
* 该接口用于定义获取安全过滤器链唯一标识的方法。
|
||||||
|
* 在多安全链环境中,每个实现类应提供一个唯一且可读的标识符,
|
||||||
|
* 以便于日志记录、调试和配置识别。
|
||||||
|
*/
|
||||||
|
fun interface SecurityFilterChainIdentifier {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前过滤器链的唯一标识符。
|
||||||
|
*
|
||||||
|
* 在复杂的多安全链环境中,此方法返回的标识符用于唯一标识一个过滤器链实例。
|
||||||
|
* 标识符应在实现类中确保其唯一性和可读性,通常用于日志记录、调试和配置识别。
|
||||||
|
*
|
||||||
|
* @return 当前过滤器链实例的唯一标识字符串,非空且不应重复。
|
||||||
|
*/
|
||||||
|
fun getChainId(): String
|
||||||
|
}
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.token
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken
|
||||||
|
import org.springframework.security.core.GrantedAuthority
|
||||||
|
import org.springframework.util.Assert
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主体凭据身份验证令牌
|
||||||
|
*
|
||||||
|
* @author gewuyou
|
||||||
|
* @since 2025-02-16 15:46:05
|
||||||
|
*/
|
||||||
|
open class PrincipalCredentialsAuthenticationToken(
|
||||||
|
/**
|
||||||
|
* 主体
|
||||||
|
*/
|
||||||
|
@Transient
|
||||||
|
private val principal: Any,
|
||||||
|
/**
|
||||||
|
* 凭证
|
||||||
|
*/
|
||||||
|
@Transient
|
||||||
|
private val credentials: Any?,
|
||||||
|
/**
|
||||||
|
* 权限列表
|
||||||
|
*/
|
||||||
|
authorities: Collection<GrantedAuthority>? = null
|
||||||
|
) : AbstractAuthenticationToken(authorities) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (authorities == null) {
|
||||||
|
isAuthenticated = false
|
||||||
|
} else {
|
||||||
|
super.setAuthenticated(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* 创建未认证的令牌
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun unauthenticated(principal: Any, credentials: Any): PrincipalCredentialsAuthenticationToken {
|
||||||
|
return PrincipalCredentialsAuthenticationToken(principal, credentials)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建已认证的令牌
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun authenticated(
|
||||||
|
principal: Any,
|
||||||
|
credentials: Any?,
|
||||||
|
authorities: Collection<GrantedAuthority>
|
||||||
|
): PrincipalCredentialsAuthenticationToken {
|
||||||
|
return PrincipalCredentialsAuthenticationToken(principal, credentials, authorities)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The credentials that prove the principal is correct. This is usually a password,
|
||||||
|
* but could be anything relevant to the `AuthenticationManager`. Callers
|
||||||
|
* are expected to populate the credentials.
|
||||||
|
*
|
||||||
|
* @return the credentials that prove the identity of the `Principal`
|
||||||
|
*/
|
||||||
|
override fun getCredentials(): Any? {
|
||||||
|
return if (isAuthenticated) null else this.credentials
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identity of the principal being authenticated. In the case of an authentication
|
||||||
|
* request with username and password, this would be the username. Callers are
|
||||||
|
* expected to populate the principal for an authentication request.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The <tt>AuthenticationManager</tt> implementation will often return an
|
||||||
|
* <tt>Authentication</tt> containing richer information as the principal for use by
|
||||||
|
* the application. Many of the authentication providers will create a
|
||||||
|
* `UserDetails` object as the principal.
|
||||||
|
*
|
||||||
|
* @return the `Principal` being authenticated or the authenticated
|
||||||
|
* principal after authentication.
|
||||||
|
*/
|
||||||
|
override fun getPrincipal(): Any {
|
||||||
|
return this.principal
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重写方法防止滥用
|
||||||
|
* @param authenticated `true` if the token should be trusted (which may
|
||||||
|
* result in an exception) or `false` if the token should not be trusted
|
||||||
|
*/
|
||||||
|
override fun setAuthenticated(authenticated: Boolean) {
|
||||||
|
Assert.isTrue(
|
||||||
|
!isAuthenticated,
|
||||||
|
"无法将此令牌设置为受信任 - 请使用采用 GrantedAuthority 列表的构造函数"
|
||||||
|
)
|
||||||
|
super.setAuthenticated(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.token
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority
|
||||||
|
|
||||||
|
/**
|
||||||
|
*用户名 密码 认证 Token
|
||||||
|
*
|
||||||
|
* @since 2025-02-16 16:00:18
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class UsernamePasswordAuthenticationToken(
|
||||||
|
principal: Any,
|
||||||
|
credentials: Any?,
|
||||||
|
authorities: Collection<GrantedAuthority>? = null
|
||||||
|
) : PrincipalCredentialsAuthenticationToken(principal, credentials, authorities) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* 创建未认证的令牌
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun unauthenticated(principal: Any, credentials: Any): UsernamePasswordAuthenticationToken {
|
||||||
|
return UsernamePasswordAuthenticationToken(principal, credentials)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建已认证的令牌
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun authenticated(
|
||||||
|
principal: Any,
|
||||||
|
credentials: Any?,
|
||||||
|
authorities: Collection<GrantedAuthority>
|
||||||
|
): UsernamePasswordAuthenticationToken {
|
||||||
|
return UsernamePasswordAuthenticationToken(principal, credentials, authorities)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.wrapper
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.identifier.SecurityFilterChainIdentifier
|
||||||
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全过滤器链包装器
|
||||||
|
*
|
||||||
|
* 该类为 [SecurityFilterChain] 提供了装饰功能,同时实现了 [SecurityFilterChainIdentifier] 接口,
|
||||||
|
* 用于标识当前安全过滤器链的唯一 ID。
|
||||||
|
*
|
||||||
|
* @property delegate 被包装的真实 [SecurityFilterChain] 实例,所有方法调用将委托给它。
|
||||||
|
* @property chainId 当前过滤器链的唯一标识符。
|
||||||
|
*
|
||||||
|
* @see SecurityFilterChain
|
||||||
|
* @see SecurityFilterChainIdentifier
|
||||||
|
*
|
||||||
|
* @since 2025-06-24 16:11:43
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class SecurityFilterChainWrapper(
|
||||||
|
private val delegate: SecurityFilterChain,
|
||||||
|
private val chainId: String,
|
||||||
|
) : SecurityFilterChain by delegate, SecurityFilterChainIdentifier {
|
||||||
|
/**
|
||||||
|
* 获取当前过滤器链的唯一标识符。
|
||||||
|
*
|
||||||
|
* @return 返回当前链的标识符 [chainId]
|
||||||
|
*/
|
||||||
|
override fun getChainId(): String = chainId
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.gewuyou.forgeboot.security.core.common.wrapper
|
||||||
|
|
||||||
|
import com.gewuyou.forgeboot.security.core.common.identifier.SecurityFilterChainIdentifier
|
||||||
|
import org.springframework.security.web.server.SecurityWebFilterChain
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forge Boot Security Web 过滤器链包装器
|
||||||
|
*
|
||||||
|
* 该类是一个 Kotlin 实现的 [SecurityWebFilterChain] 包装器,用于代理原始的 [SecurityWebFilterChain] 实例,
|
||||||
|
* 并通过实现 [SecurityFilterChainIdentifier] 接口提供过滤器链标识符功能。
|
||||||
|
*
|
||||||
|
* @property delegate 被包装的 [SecurityWebFilterChain] 实例,用于执行实际的安全过滤逻辑。
|
||||||
|
* @property chainId 当前过滤器链的唯一标识符,用于区分不同的安全配置链。
|
||||||
|
*
|
||||||
|
* @see SecurityWebFilterChain
|
||||||
|
* @see SecurityFilterChainIdentifier
|
||||||
|
*/
|
||||||
|
class SecurityWebFilterChainWrapper(
|
||||||
|
private val delegate: SecurityWebFilterChain,
|
||||||
|
private val chainId: String
|
||||||
|
) : SecurityWebFilterChain by delegate, SecurityFilterChainIdentifier {
|
||||||
|
/**
|
||||||
|
* 获取当前过滤器链的唯一标识符。
|
||||||
|
*
|
||||||
|
* @return 返回当前链的标识符 [chainId]
|
||||||
|
*/
|
||||||
|
override fun getChainId(): String = chainId
|
||||||
|
}
|
||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This settings.gradle.kts file configures the Gradle build for the forgeboot project.
|
* This settings.gradle.kts file configures the Gradle build for the forgeboot project.
|
||||||
* It sets up dependency resolution, plugins, and includes all relevant subprojects.
|
* It sets up dependency resolution, plugins, and includes all relevant subprojects.
|
||||||
@ -95,10 +94,12 @@ project(":forgeboot-webmvc:spec").name = "forgeboot-webmvc-spec"
|
|||||||
*/
|
*/
|
||||||
include(
|
include(
|
||||||
"forgeboot-core",
|
"forgeboot-core",
|
||||||
":forgeboot-core:forgeboot-core-extension"
|
":forgeboot-core:forgeboot-core-extension",
|
||||||
|
":forgeboot-core:forgeboot-core-serialization",
|
||||||
)
|
)
|
||||||
project(":forgeboot-core").name = "forgeboot-core"
|
project(":forgeboot-core").name = "forgeboot-core"
|
||||||
project(":forgeboot-core:forgeboot-core-extension").name = "forgeboot-core-extension"
|
project(":forgeboot-core:forgeboot-core-extension").name = "forgeboot-core-extension"
|
||||||
|
project(":forgeboot-core:forgeboot-core-serialization").name = "forgeboot-core-serialization"
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region module i18n
|
//region module i18n
|
||||||
@ -118,7 +119,6 @@ project(":forgeboot-i18n:forgeboot-i18n-impl").name = "forgeboot-i18n-impl"
|
|||||||
project(":forgeboot-i18n:forgeboot-i18n-autoconfigure").name = "forgeboot-i18n-autoconfigure"
|
project(":forgeboot-i18n:forgeboot-i18n-autoconfigure").name = "forgeboot-i18n-autoconfigure"
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
//region module trace
|
//region module trace
|
||||||
/**
|
/**
|
||||||
* Includes and configures projects related to 'forgeboot-trace'
|
* Includes and configures projects related to 'forgeboot-trace'
|
||||||
@ -134,4 +134,54 @@ project(":forgeboot-trace").name = "forgeboot-trace-spring-boot-starter"
|
|||||||
project(":forgeboot-trace:forgeboot-trace-api").name = "forgeboot-trace-api"
|
project(":forgeboot-trace:forgeboot-trace-api").name = "forgeboot-trace-api"
|
||||||
project(":forgeboot-trace:forgeboot-trace-impl").name = "forgeboot-trace-impl"
|
project(":forgeboot-trace:forgeboot-trace-impl").name = "forgeboot-trace-impl"
|
||||||
project(":forgeboot-trace:forgeboot-trace-autoconfigure").name = "forgeboot-trace-autoconfigure"
|
project(":forgeboot-trace:forgeboot-trace-autoconfigure").name = "forgeboot-trace-autoconfigure"
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
//region module security
|
||||||
|
/**
|
||||||
|
* Includes and configures projects related to 'forgeboot-security'
|
||||||
|
* This module handles security-related functionality.
|
||||||
|
*/
|
||||||
|
include(
|
||||||
|
"forgeboot-security",
|
||||||
|
":forgeboot-security:forgeboot-security-core",
|
||||||
|
|
||||||
|
":forgeboot-security:forgeboot-security-authenticate",
|
||||||
|
":forgeboot-security:forgeboot-security-authenticate:api",
|
||||||
|
":forgeboot-security:forgeboot-security-authenticate:impl",
|
||||||
|
":forgeboot-security:forgeboot-security-authenticate:autoconfigure",
|
||||||
|
|
||||||
|
":forgeboot-security:forgeboot-security-authorize",
|
||||||
|
":forgeboot-security:forgeboot-security-authorize:api",
|
||||||
|
":forgeboot-security:forgeboot-security-authorize:impl",
|
||||||
|
":forgeboot-security:forgeboot-security-authorize:autoconfigure"
|
||||||
|
)
|
||||||
|
project(":forgeboot-security").name = "forgeboot-security-spring-boot-starter"
|
||||||
|
project(":forgeboot-security:forgeboot-security-core").name = "forgeboot-security-core"
|
||||||
|
|
||||||
|
project(":forgeboot-security:forgeboot-security-authenticate").name =
|
||||||
|
"forgeboot-security-authenticate-spring-boot-starter"
|
||||||
|
project(":forgeboot-security:forgeboot-security-authenticate:api").name = "forgeboot-security-authenticate-api"
|
||||||
|
project(":forgeboot-security:forgeboot-security-authenticate:impl").name = "forgeboot-security-authenticate-impl"
|
||||||
|
project(":forgeboot-security:forgeboot-security-authenticate:autoconfigure").name =
|
||||||
|
"forgeboot-security-authenticate-autoconfigure"
|
||||||
|
|
||||||
|
project(":forgeboot-security:forgeboot-security-authorize").name = "forgeboot-security-authorize-spring-boot-starter"
|
||||||
|
project(":forgeboot-security:forgeboot-security-authorize:api").name = "forgeboot-security-authorize-api"
|
||||||
|
project(":forgeboot-security:forgeboot-security-authorize:impl").name = "forgeboot-security-authorize-impl"
|
||||||
|
project(":forgeboot-security:forgeboot-security-authorize:autoconfigure").name =
|
||||||
|
"forgeboot-security-authorize-autoconfigure"
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region module cache
|
||||||
|
include(
|
||||||
|
"forgeboot-cache",
|
||||||
|
":forgeboot-cache:forgeboot-cache-api",
|
||||||
|
":forgeboot-cache:forgeboot-cache-impl",
|
||||||
|
":forgeboot-cache:forgeboot-cache-autoconfigure"
|
||||||
|
)
|
||||||
|
project(":forgeboot-cache").name = "forgeboot-cache-spring-boot-starter"
|
||||||
|
project(":forgeboot-cache:forgeboot-cache-api").name = "forgeboot-cache-api"
|
||||||
|
project(":forgeboot-cache:forgeboot-cache-impl").name = "forgeboot-cache-impl"
|
||||||
|
project(":forgeboot-cache:forgeboot-cache-autoconfigure").name = "forgeboot-cache-autoconfigure"
|
||||||
|
//endregion
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user