完善登录与鉴权机制
This commit is contained in:
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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-web,1-native,2-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
5
test/test-api.http
Normal file
@@ -0,0 +1,5 @@
|
||||
GET http://api:123456@110.40.82.248:9990/port/active
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
|
||||
47
web/auth.go
47
web/auth.go
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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-web,1-native,2-browser" json:"spec"` // 安全规范:0-web,1-native,2-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"` // 更新时间
|
||||
|
||||
@@ -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-web,1-native,2-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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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 定义负载类型
|
||||
|
||||
Reference in New Issue
Block a user