feat(authorization): 添加身份验证中间件和角色条件验证功能- 新增认证中间件组件,支持路径前缀匹配和请求处理管道

- 实现基于正则表达式的URL回退跳转机制
- 添加角色权限验证条件,支持多角色匹配验证
- 集成用户服务注册功能,支持从会话中获取当前用户
- 实现未认证和未授权状态的HTTP响应处理
- 支持配置认证失败时的重定向URL
This commit is contained in:
2025-11-07 21:53:59 +08:00
parent 17ba33933a
commit 84c61efeef
3 changed files with 120 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
package authorization
import (
"net/http"
"platform/authorization/identity"
"platform/config"
"platform/http/handing"
"platform/pipeline"
"regexp"
"strings"
)
func NewAuthComponent(prefix string, condition identity.AuthorizationCondition, requestHandlers ...interface{}) *AuthMiddlewareComponent {
var entries []handing.HandlerEntry
for _, handler := range requestHandlers {
entries = append(entries, handing.HandlerEntry{Prefix: prefix, Handler: handler})
}
router := handing.NewRouter(entries...)
return &AuthMiddlewareComponent{
prefix: "/" + prefix,
condition: condition,
RequestPipeline: pipeline.CreatePipeline(router),
fallbacks: map[*regexp.Regexp]string{},
}
}
type AuthMiddlewareComponent struct {
prefix string
condition identity.AuthorizationCondition
pipeline.RequestPipeline
config.Configuration
authFailUrl string
fallbacks map[*regexp.Regexp]string
}
func (c *AuthMiddlewareComponent) Init() {
c.authFailUrl, _ = c.Configuration.GetString("authorization:failUrl")
}
func (c *AuthMiddlewareComponent) ImplementsProcessRequestWithServices() {}
func (c *AuthMiddlewareComponent) ProcessRequestWithService(context *pipeline.ComponentContext, next func(*pipeline.ComponentContext), user identity.User) {
// 匹配前缀
if strings.HasPrefix(context.Request.URL.Path, c.prefix) {
for expr, target := range c.fallbacks {
// 正则表达式匹配则跳转URL
if expr.MatchString(context.Request.URL.Path) {
http.Redirect(context.ResponseWriter, context.Request, target, http.StatusSeeOther)
return
}
}
if c.condition.Validate(user) {
// 验证通过,则继续处理
err := c.RequestPipeline.ProcessRequest(context.Request, context.ResponseWriter)
if err != nil {
context.Error(err)
return
}
} else if c.authFailUrl != "" {
http.Redirect(context.ResponseWriter, context.Request, c.authFailUrl, http.StatusSeeOther)
} else if user.IsAuthenticated() {
context.ResponseWriter.WriteHeader(http.StatusForbidden)
} else {
context.ResponseWriter.WriteHeader(http.StatusUnauthorized)
}
} else {
next(context)
}
}
func (c *AuthMiddlewareComponent) AddFallback(target string, patterns ...string) *AuthMiddlewareComponent {
for _, p := range patterns {
c.fallbacks[regexp.MustCompile(p)] = target
}
return c
}

View File

@@ -0,0 +1,20 @@
package authorization
import "platform/authorization/identity"
func NewRoleCondition(roles ...string) identity.AuthorizationCondition {
return &roleCondition{allowedRoles: roles}
}
type roleCondition struct {
allowedRoles []string
}
func (c *roleCondition) Validate(user identity.User) bool {
for _, role := range c.allowedRoles {
if user.InRole(role) {
return true
}
}
return false
}

View File

@@ -0,0 +1,23 @@
package authorization
import (
"platform/authorization/identity"
"platform/services"
"platform/sessions"
)
func RegisterDefaultUserService() {
err := services.AddScoped(func(session sessions.Session, store identity.UserStore) identity.User {
userId, found := session.GetValue(UserSessionKey).(int)
if found {
user, userFound := store.GetUserById(userId)
if userFound {
return user
}
}
return identity.UnauthenticatedUser
})
if err != nil {
panic(err)
}
}