完善登录与鉴权机制

This commit is contained in:
2025-03-28 15:01:30 +08:00
parent e61f0bef32
commit edec734b71
11 changed files with 132 additions and 108 deletions

View File

@@ -7,6 +7,8 @@ import (
"platform/pkg/orm"
"platform/web/models"
q "platform/web/queries"
"golang.org/x/crypto/bcrypt"
)
func main() {
@@ -14,41 +16,59 @@ func main() {
logs.Init()
orm.Init()
q.User.Select(
q.User.Phone,
).Create(&models.User{
Phone: "12312341234",
})
q.User.
Select(
q.User.Phone).
Create(&models.User{
Phone: "12312341234"})
q.Proxy.Select(
q.Proxy.Version,
q.Proxy.Name,
q.Proxy.Host,
q.Proxy.Type,
).Create(&models.Proxy{
Version: 1,
Name: "7a17e8b4-cdc3-4500-bf16-4a665991a7f6",
Host: "110.40.82.248",
Type: 1,
})
q.Proxy.
Select(
q.Proxy.Version,
q.Proxy.Name,
q.Proxy.Host,
q.Proxy.Type).
Create(&models.Proxy{
Version: 1,
Name: "7a17e8b4-cdc3-4500-bf16-4a665991a7f6",
Host: "110.40.82.248",
Type: 1})
q.Node.Select(
q.Node.Version,
q.Node.Name,
q.Node.Host,
q.Node.Isp,
q.Node.Prov,
q.Node.City,
q.Node.Status,
).Create(&models.Node{
Version: 1,
Name: "test-node",
Host: "123",
Isp: "test-isp",
Prov: "test-prov",
City: "test-city",
Status: 1,
})
q.Node.
Select(
q.Node.Version,
q.Node.Name,
q.Node.Host,
q.Node.Isp,
q.Node.Prov,
q.Node.City,
q.Node.Status).
Create(&models.Node{
Version: 1,
Name: "test-node",
Host: "123",
Isp: "test-isp",
Prov: "test-prov",
City: "test-city",
Status: 1})
var secret, _ = bcrypt.GenerateFromPassword([]byte("test"), bcrypt.DefaultCost)
q.Client.
Select(
q.Client.ClientID,
q.Client.ClientSecret,
q.Client.GrantClient,
q.Client.GrantRefresh,
q.Client.Spec,
q.Client.Name).
Create(&models.Client{
ClientID: "test",
ClientSecret: string(secret),
GrantClient: true,
GrantRefresh: true,
Spec: 0,
Name: "默认客户端",
})
slog.Info("✔ Data inserted successfully")
}

View File

@@ -184,7 +184,7 @@ create table client (
grant_refresh bool not null default false,
spec int not null,
name varchar(255) not null,
version int not null,
icon varchar(255),
status int not null default 1,
created_at timestamp default current_timestamp,
updated_at timestamp default current_timestamp,
@@ -206,7 +206,7 @@ comment on column client.grant_client is '允许客户端凭证授予';
comment on column client.grant_refresh is '允许刷新令牌授予';
comment on column client.spec is '安全规范0-web1-native2-browser';
comment on column client.name is '名称';
comment on column client.version is '版本';
comment on column client.icon is '图标URL';
comment on column client.status is '状态1-正常0-禁用';
comment on column client.created_at is '创建时间';
comment on column client.updated_at is '更新时间';

5
test/test-api.http Normal file
View File

@@ -0,0 +1,5 @@
GET http://api:123456@110.40.82.248:9990/port/active
Accept: application/json
###

View File

@@ -56,3 +56,50 @@ func PermitUser(permissions ...string) fiber.Handler {
return c.Next()
}
}
func PermitDevice(permissions ...string) fiber.Handler {
return func(c *fiber.Ctx) error {
// 获取令牌
var header = c.Get("Authorization")
var token = strings.TrimPrefix(header, "Bearer ")
if token == "" {
return c.Status(fiber.StatusUnauthorized).JSON(common.ErrResp{
Error: true,
Message: "没有权限",
})
}
// 验证令牌
auth, err := services.Session.Find(c.Context(), token)
if err != nil {
return c.Status(fiber.StatusUnauthorized).JSON(common.ErrResp{
Error: true,
Message: "没有权限",
})
}
// 检查权限
switch auth.Payload.Type {
case services.PayloadAdmin:
// 管理员不需要权限检查
case services.PayloadClientPublic, services.PayloadClientConfidential:
if len(permissions) > 0 && !auth.AnyPermission(permissions...) {
return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{
Error: true,
Message: "拒绝访问",
})
}
default:
return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{
Error: true,
Message: "拒绝访问",
})
}
// 将认证信息存储在上下文中
c.Locals("auth", auth)
c.Locals("access_token", token) // 存储原始令牌,便于后续操作
return c.Next()
}
}

View File

@@ -1,52 +0,0 @@
package handlers
import (
"platform/web/models"
q "platform/web/queries"
"time"
"github.com/gofiber/fiber/v2"
"golang.org/x/crypto/bcrypt"
)
type CreateClientReq struct {
ClientID string `query:"client_id"`
ClientSecret string `query:"client_secret"`
}
func CreateClient(c *fiber.Ctx) error {
// 验证请求参数
req := new(CreateClientReq)
if err := c.QueryParser(req); err != nil {
return err
}
if req.ClientID == "" {
return fiber.NewError(fiber.StatusBadRequest, "client_id不能为空")
}
if req.ClientSecret == "" {
return fiber.NewError(fiber.StatusBadRequest, "client_secret不能为空")
}
// 创建客户端
hashedSecret, err := bcrypt.GenerateFromPassword([]byte(req.ClientSecret), bcrypt.DefaultCost)
if err != nil {
return err
}
client := &models.Client{
ClientID: req.ClientID,
ClientSecret: string(hashedSecret),
Name: "默认客户端 - " + time.Now().String(),
Spec: 0,
GrantCode: true,
GrantClient: true,
GrantRefresh: true,
Version: 0,
}
err = q.Client.Create(client)
if err != nil {
return err
}
return c.JSON(client)
}

View File

@@ -18,8 +18,9 @@ type LoginReq struct {
}
type LoginResp struct {
Token string `json:"token"`
Expires int64 `json:"expires"`
Token string `json:"token"`
Expires int64 `json:"expires"`
Auth services.AuthContext `json:"auth"`
}
func Login(c *fiber.Ctx) error {
@@ -65,6 +66,7 @@ func loginByPhone(c *fiber.Ctx, req *LoginReq) error {
if user == nil {
user = &models.User{
Phone: req.Username,
Name: req.Username,
}
}
@@ -87,8 +89,10 @@ func loginByPhone(c *fiber.Ctx, req *LoginReq) error {
"user": {},
},
Payload: services.Payload{
Type: services.PayloadUser,
Id: user.ID,
Id: user.ID,
Type: services.PayloadUser,
Name: user.Name,
Avatar: user.Avatar,
},
}
duration := time.Hour * 24
@@ -103,5 +107,6 @@ func loginByPhone(c *fiber.Ctx, req *LoginReq) error {
return c.JSON(LoginResp{
Token: token.AccessToken,
Expires: token.AccessTokenExpires.Unix(),
Auth: auth,
})
}

View File

@@ -23,7 +23,7 @@ type Client struct {
GrantRefresh bool `gorm:"column:grant_refresh;not null;comment:允许刷新令牌授予" json:"grant_refresh"` // 允许刷新令牌授予
Spec int32 `gorm:"column:spec;not null;comment:安全规范0-web1-native2-browser" json:"spec"` // 安全规范0-web1-native2-browser
Name string `gorm:"column:name;not null;comment:名称" json:"name"` // 名称
Version int32 `gorm:"column:version;not null;comment:版本" json:"version"` // 版本
Icon string `gorm:"column:icon;comment:图标URL" json:"icon"` // 图标URL
Status int32 `gorm:"column:status;not null;default:1;comment:状态1-正常0-禁用" json:"status"` // 状态1-正常0-禁用
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间

View File

@@ -36,7 +36,7 @@ func newClient(db *gorm.DB, opts ...gen.DOOption) client {
_client.GrantRefresh = field.NewBool(tableName, "grant_refresh")
_client.Spec = field.NewInt32(tableName, "spec")
_client.Name = field.NewString(tableName, "name")
_client.Version = field.NewInt32(tableName, "version")
_client.Icon = field.NewString(tableName, "icon")
_client.Status = field.NewInt32(tableName, "status")
_client.CreatedAt = field.NewTime(tableName, "created_at")
_client.UpdatedAt = field.NewTime(tableName, "updated_at")
@@ -60,7 +60,7 @@ type client struct {
GrantRefresh field.Bool // 允许刷新令牌授予
Spec field.Int32 // 安全规范0-web1-native2-browser
Name field.String // 名称
Version field.Int32 // 版本
Icon field.String // 图标URL
Status field.Int32 // 状态1-正常0-禁用
CreatedAt field.Time // 创建时间
UpdatedAt field.Time // 更新时间
@@ -90,7 +90,7 @@ func (c *client) updateTableName(table string) *client {
c.GrantRefresh = field.NewBool(table, "grant_refresh")
c.Spec = field.NewInt32(table, "spec")
c.Name = field.NewString(table, "name")
c.Version = field.NewInt32(table, "version")
c.Icon = field.NewString(table, "icon")
c.Status = field.NewInt32(table, "status")
c.CreatedAt = field.NewTime(table, "created_at")
c.UpdatedAt = field.NewTime(table, "updated_at")
@@ -121,7 +121,7 @@ func (c *client) fillFieldMap() {
c.fieldMap["grant_refresh"] = c.GrantRefresh
c.fieldMap["spec"] = c.Spec
c.fieldMap["name"] = c.Name
c.fieldMap["version"] = c.Version
c.fieldMap["icon"] = c.Icon
c.fieldMap["status"] = c.Status
c.fieldMap["created_at"] = c.CreatedAt
c.fieldMap["updated_at"] = c.UpdatedAt

View File

@@ -11,14 +11,10 @@ func ApplyRouters(app *fiber.App) {
// 认证
auth := api.Group("/auth")
auth.Post("/verify/sms", PermitUser(), handlers.SmsCode)
auth.Post("/login/sms", PermitUser(), handlers.Login)
auth.Post("/verify/sms", PermitDevice(), handlers.SmsCode)
auth.Post("/login/sms", PermitDevice(), handlers.Login)
auth.Post("/token", handlers.Token)
// 客户端
client := api.Group("/client")
client.Get("/test/create", handlers.CreateClient)
// 通道
channel := api.Group("/channel", PermitUser())
channel.Post("/create", handlers.CreateChannel)

View File

@@ -59,8 +59,9 @@ func (s *authService) OauthClientCredentials(ctx context.Context, client *models
auth := AuthContext{
Permissions: permissions,
Payload: Payload{
Type: clientType,
Id: client.ID,
Type: clientType,
Name: client.Name,
},
}

View File

@@ -238,15 +238,17 @@ func mergeConfig(defaultCfg SessionConfig, customCfg SessionConfig) SessionConfi
// AuthContext 定义认证信息
type AuthContext struct {
Payload Payload
Permissions map[string]struct{}
Metadata map[string]interface{}
Payload Payload `json:"payload"`
Permissions map[string]struct{} `json:"permissions,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// Payload 定义负载信息
type Payload struct {
Type PayloadType
Id int32
Id int32 `json:"id,omitempty"`
Type PayloadType `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Avatar string `json:"avatar,omitempty"`
}
// PayloadType 定义负载类型