完善登录逻辑,登录接口统一到 /token
This commit is contained in:
@@ -22,6 +22,12 @@
|
||||
- [ ] Limiter
|
||||
- [ ] Compress
|
||||
|
||||
使用 fiber 自带 validator 进行参数验证
|
||||
|
||||
增加 domain 层,缓解同包字段过长的问题
|
||||
|
||||
移动端适配
|
||||
|
||||
授权过期跳转登录,成功后返回原链接
|
||||
|
||||
错误处理类型转换失败问题
|
||||
|
||||
@@ -179,20 +179,21 @@ comment on column user_role.deleted_at is '删除时间';
|
||||
|
||||
drop table if exists client cascade;
|
||||
create table client (
|
||||
id serial primary key,
|
||||
client_id varchar(255) not null unique,
|
||||
client_secret varchar(255) not null,
|
||||
redirect_uri varchar(255),
|
||||
grant_code bool not null default false,
|
||||
grant_client bool not null default false,
|
||||
grant_refresh bool not null default false,
|
||||
spec int not null,
|
||||
name varchar(255) not null,
|
||||
icon varchar(255),
|
||||
status int not null default 1,
|
||||
created_at timestamp default current_timestamp,
|
||||
updated_at timestamp default current_timestamp,
|
||||
deleted_at timestamp
|
||||
id serial primary key,
|
||||
client_id varchar(255) not null unique,
|
||||
client_secret varchar(255) not null,
|
||||
redirect_uri varchar(255),
|
||||
grant_code bool not null default false,
|
||||
grant_client bool not null default false,
|
||||
grant_refresh bool not null default false,
|
||||
grant_password bool not null default false,
|
||||
spec int not null,
|
||||
name varchar(255) not null,
|
||||
icon varchar(255),
|
||||
status int not null default 1,
|
||||
created_at timestamp default current_timestamp,
|
||||
updated_at timestamp default current_timestamp,
|
||||
deleted_at timestamp
|
||||
);
|
||||
|
||||
create index client_client_id_index on client (client_id);
|
||||
@@ -209,6 +210,7 @@ comment on column client.redirect_uri is 'OAuth2 重定向URI';
|
||||
comment on column client.grant_code is '允许授权码授予';
|
||||
comment on column client.grant_client is '允许客户端凭证授予';
|
||||
comment on column client.grant_refresh is '允许刷新令牌授予';
|
||||
comment on column client.grant_password is '允许密码授予';
|
||||
comment on column client.spec is '安全规范:0-web,1-native,2-browser';
|
||||
comment on column client.name is '名称';
|
||||
comment on column client.icon is '图标URL';
|
||||
@@ -822,19 +824,19 @@ comment on column bill.deleted_at is '删除时间';
|
||||
-- coupon 优惠券
|
||||
drop table if exists coupon cascade;
|
||||
create table coupon (
|
||||
id serial primary key,
|
||||
user_id int references "user" (id)
|
||||
id serial primary key,
|
||||
user_id int references "user" (id)
|
||||
on update cascade
|
||||
on delete cascade,
|
||||
code varchar(255) not null unique,
|
||||
remark varchar(255),
|
||||
amount decimal(12, 2) not null default 0,
|
||||
code varchar(255) not null unique,
|
||||
remark varchar(255),
|
||||
amount decimal(12, 2) not null default 0,
|
||||
min_amount decimal(12, 2) not null default 0,
|
||||
status int not null default 0,
|
||||
expire_at timestamp,
|
||||
created_at timestamp default current_timestamp,
|
||||
updated_at timestamp default current_timestamp,
|
||||
deleted_at timestamp
|
||||
status int not null default 0,
|
||||
expire_at timestamp,
|
||||
created_at timestamp default current_timestamp,
|
||||
updated_at timestamp default current_timestamp,
|
||||
deleted_at timestamp
|
||||
);
|
||||
create index coupon_user_id_index on coupon (user_id);
|
||||
create index coupon_code_index on coupon (code);
|
||||
|
||||
131
web/auth/auth.go
131
web/auth/auth.go
@@ -76,7 +76,6 @@ func Permit(types []services.PayloadType, permissions ...string) fiber.Handler {
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func PermitAll(permissions ...string) fiber.Handler {
|
||||
@@ -88,14 +87,6 @@ func PermitAll(permissions ...string) fiber.Handler {
|
||||
}, permissions...)
|
||||
}
|
||||
|
||||
// PermitUser 创建针对单个路由的鉴权中间件
|
||||
func PermitUser(permissions ...string) fiber.Handler {
|
||||
return Permit([]services.PayloadType{
|
||||
services.PayloadUser,
|
||||
services.PayloadAdmin,
|
||||
}, permissions...)
|
||||
}
|
||||
|
||||
func PermitDevice(permissions ...string) fiber.Handler {
|
||||
return Permit([]services.PayloadType{
|
||||
services.PayloadClientPublic,
|
||||
@@ -104,74 +95,6 @@ func PermitDevice(permissions ...string) fiber.Handler {
|
||||
}, permissions...)
|
||||
}
|
||||
|
||||
func PermitPublic(permissions ...string) fiber.Handler {
|
||||
return Permit([]services.PayloadType{
|
||||
services.PayloadClientPublic,
|
||||
services.PayloadAdmin,
|
||||
}, permissions...)
|
||||
}
|
||||
|
||||
func PermitConfidential(permissions ...string) fiber.Handler {
|
||||
return Permit([]services.PayloadType{
|
||||
services.PayloadClientConfidential,
|
||||
services.PayloadAdmin,
|
||||
}, permissions...)
|
||||
}
|
||||
|
||||
func authBearer(ctx context.Context, token string) (*services.AuthContext, error) {
|
||||
auth, err := services.Session.Find(ctx, token)
|
||||
if err != nil {
|
||||
slog.Debug(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func authBasic(ctx context.Context, token string) (*services.AuthContext, error) {
|
||||
|
||||
// 解析 Basic 认证信息
|
||||
var base, err = base64.URLEncoding.DecodeString(token)
|
||||
if err != nil {
|
||||
slog.Debug(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var split = strings.Split(string(base), ":")
|
||||
if len(split) != 2 {
|
||||
msg := "无法解析 Basic 认证信息"
|
||||
slog.Debug(msg)
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
|
||||
var clientID = split[0]
|
||||
|
||||
// 获取客户端信息
|
||||
client, err := q.Client.
|
||||
Where(
|
||||
q.Client.ClientID.Eq(clientID),
|
||||
q.Client.Spec.Eq(0),
|
||||
q.Client.GrantClient.Is(true),
|
||||
q.Client.Status.Eq(1)).
|
||||
Take()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// todo 查询客户端关联权限
|
||||
|
||||
// 组织授权信息(一次性请求)
|
||||
return &services.AuthContext{
|
||||
Payload: services.Payload{
|
||||
Id: client.ID,
|
||||
Type: services.PayloadClientConfidential,
|
||||
Name: client.Name,
|
||||
Avatar: client.Icon,
|
||||
},
|
||||
Permissions: nil,
|
||||
Metadata: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Protect(c *fiber.Ctx, types []services.PayloadType, permissions []string) (*services.AuthContext, error) {
|
||||
// 获取令牌
|
||||
var header = c.Get("Authorization")
|
||||
@@ -216,3 +139,57 @@ func Protect(c *fiber.Ctx, types []services.PayloadType, permissions []string) (
|
||||
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func authBearer(ctx context.Context, token string) (*services.AuthContext, error) {
|
||||
auth, err := services.Session.Find(ctx, token)
|
||||
if err != nil {
|
||||
slog.Debug(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func authBasic(_ context.Context, token string) (*services.AuthContext, error) {
|
||||
|
||||
// 解析 Basic 认证信息
|
||||
var base, err = base64.URLEncoding.DecodeString(token)
|
||||
if err != nil {
|
||||
slog.Debug(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var split = strings.Split(string(base), ":")
|
||||
if len(split) != 2 {
|
||||
msg := "无法解析 Basic 认证信息"
|
||||
slog.Debug(msg)
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
|
||||
var clientID = split[0]
|
||||
|
||||
// 获取客户端信息
|
||||
client, err := q.Client.
|
||||
Where(
|
||||
q.Client.ClientID.Eq(clientID),
|
||||
q.Client.Spec.Eq(0),
|
||||
q.Client.GrantClient.Is(true),
|
||||
q.Client.Status.Eq(1)).
|
||||
Take()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// todo 查询客户端关联权限
|
||||
|
||||
// 组织授权信息(一次性请求)
|
||||
return &services.AuthContext{
|
||||
Payload: services.Payload{
|
||||
Id: client.ID,
|
||||
Type: services.PayloadClientConfidential,
|
||||
Name: client.Name,
|
||||
Avatar: client.Icon,
|
||||
},
|
||||
Permissions: nil,
|
||||
Metadata: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func ListBill(c *fiber.Ctx) error {
|
||||
do = do.Where(q.Bill.BillNo.Eq(*req.BillNo))
|
||||
}
|
||||
|
||||
bills, err := do.Debug().
|
||||
bills, err := do.
|
||||
Preload(q.Bill.Resource, q.Bill.Trade, q.Bill.Refund).
|
||||
Preload(q.Bill.Resource.Pss).
|
||||
Order(q.Bill.CreatedAt.Desc()).
|
||||
|
||||
@@ -3,9 +3,10 @@ package handlers
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"platform/web/models"
|
||||
"log/slog"
|
||||
m "platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"platform/web/services"
|
||||
s "platform/web/services"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -17,22 +18,42 @@ import (
|
||||
// region Token
|
||||
|
||||
type TokenReq struct {
|
||||
ClientID string `json:"client_id" form:"client_id"`
|
||||
ClientSecret string `json:"client_secret" form:"client_secret"`
|
||||
GrantType TokenGrantType `json:"grant_type" form:"grant_type"`
|
||||
Code string `json:"code" form:"code"`
|
||||
RedirectURI string `json:"redirect_uri" form:"redirect_uri"`
|
||||
CodeVerifier string `json:"code_verifier" form:"code_verifier"`
|
||||
RefreshToken string `json:"refresh_token" form:"refresh_token"`
|
||||
Scope string `json:"scope" form:"scope"`
|
||||
GrantType s.OauthGrantType `json:"grant_type" form:"grant_type"`
|
||||
ClientID string `json:"client_id" form:"client_id"`
|
||||
ClientSecret string `json:"client_secret" form:"client_secret"`
|
||||
Scope string `json:"scope" form:"scope"`
|
||||
TokenReqCode
|
||||
TokenReqClient
|
||||
TokenReqRefresh
|
||||
TokenReqPassword
|
||||
}
|
||||
|
||||
type TokenReqCode struct {
|
||||
Code string `json:"code" form:"code"`
|
||||
RedirectURI string `json:"redirect_uri" form:"redirect_uri"`
|
||||
CodeVerifier string `json:"code_verifier" form:"code_verifier"`
|
||||
}
|
||||
|
||||
type TokenReqClient struct {
|
||||
}
|
||||
|
||||
type TokenReqRefresh struct {
|
||||
RefreshToken string `json:"refresh_token" form:"refresh_token"`
|
||||
}
|
||||
|
||||
type TokenReqPassword struct {
|
||||
LoginType s.OauthGrantLoginType `json:"login_type" form:"login_type"`
|
||||
Username string `json:"username" form:"username"`
|
||||
Password string `json:"password" form:"password"`
|
||||
Remember bool `json:"remember" form:"remember"`
|
||||
}
|
||||
|
||||
type TokenResp struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
TokenType string `json:"token_type"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
}
|
||||
|
||||
type TokenErrResp struct {
|
||||
@@ -40,57 +61,57 @@ type TokenErrResp struct {
|
||||
Description string `json:"error_description,omitempty"`
|
||||
}
|
||||
|
||||
type TokenGrantType string
|
||||
|
||||
const (
|
||||
AuthorizationCode = TokenGrantType("authorization_code")
|
||||
ClientCredentials = TokenGrantType("client_credentials")
|
||||
RefreshToken = TokenGrantType("refresh_token")
|
||||
)
|
||||
|
||||
// Token 处理 OAuth2.0 授权请求
|
||||
func Token(c *fiber.Ctx) error {
|
||||
|
||||
// 验证请求参数
|
||||
req := new(TokenReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return sendError(c, services.ErrOauthInvalidRequest, "无法解析请求参数")
|
||||
return sendError(c, s.ErrOauthInvalidRequest, "无法解析请求参数")
|
||||
}
|
||||
if req.GrantType == "" {
|
||||
return sendError(c, services.ErrOauthInvalidRequest, "缺少必要参数:grant_type")
|
||||
return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数:grant_type")
|
||||
}
|
||||
|
||||
slog.Debug("oauth token", slog.String("grant_type",
|
||||
string(req.GrantType)),
|
||||
slog.String("client_id", req.ClientID),
|
||||
)
|
||||
|
||||
// 基于授权类型处理请求
|
||||
switch req.GrantType {
|
||||
|
||||
case AuthorizationCode:
|
||||
case s.OauthGrantTypeAuthorizationCode:
|
||||
return authorizationCode(c, req)
|
||||
|
||||
case ClientCredentials:
|
||||
case s.OauthGrantTypeClientCredentials:
|
||||
return clientCredentials(c, req)
|
||||
|
||||
case RefreshToken:
|
||||
case s.OauthGrantTypeRefreshToken:
|
||||
return refreshToken(c, req)
|
||||
|
||||
case s.OauthGrantTypePassword:
|
||||
return password(c, req)
|
||||
|
||||
default:
|
||||
return sendError(c, services.ErrOauthUnsupportedGrantType)
|
||||
return sendError(c, s.ErrOauthUnsupportedGrantType)
|
||||
}
|
||||
}
|
||||
|
||||
// 授权码
|
||||
func authorizationCode(c *fiber.Ctx, req *TokenReq) error {
|
||||
if req.Code == "" {
|
||||
return sendError(c, services.ErrOauthInvalidRequest, "缺少必要参数:code")
|
||||
return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数:code")
|
||||
}
|
||||
|
||||
client, err := protect(c, services.GrantTypeAuthorizationCode, req.ClientID, req.ClientSecret)
|
||||
client, err := protect(c, s.OauthGrantTypeAuthorizationCode, req.ClientID, req.ClientSecret)
|
||||
if err != nil {
|
||||
return sendError(c, err)
|
||||
}
|
||||
|
||||
token, err := services.Auth.OauthAuthorizationCode(c.Context(), client, req.Code, req.RedirectURI, req.CodeVerifier)
|
||||
token, err := s.Auth.OauthAuthorizationCode(c.Context(), client, req.Code, req.RedirectURI, req.CodeVerifier)
|
||||
if err != nil {
|
||||
return sendError(c, err.(services.AuthServiceOauthError))
|
||||
return sendError(c, err.(s.AuthServiceOauthError))
|
||||
}
|
||||
|
||||
return sendSuccess(c, token)
|
||||
@@ -98,15 +119,15 @@ func authorizationCode(c *fiber.Ctx, req *TokenReq) error {
|
||||
|
||||
// 客户端凭证
|
||||
func clientCredentials(c *fiber.Ctx, req *TokenReq) error {
|
||||
client, err := protect(c, services.GrantTypeClientCredentials, req.ClientID, req.ClientSecret)
|
||||
client, err := protect(c, s.OauthGrantTypeClientCredentials, req.ClientID, req.ClientSecret)
|
||||
if err != nil {
|
||||
return sendError(c, err)
|
||||
}
|
||||
|
||||
scope := strings.Split(req.Scope, ",")
|
||||
token, err := services.Auth.OauthClientCredentials(c.Context(), client, scope...)
|
||||
token, err := s.Auth.OauthClientCredentials(c.Context(), client, scope...)
|
||||
if err != nil {
|
||||
return sendError(c, err.(services.AuthServiceOauthError))
|
||||
return sendError(c, err.(s.AuthServiceOauthError))
|
||||
}
|
||||
|
||||
return sendSuccess(c, token)
|
||||
@@ -115,19 +136,19 @@ func clientCredentials(c *fiber.Ctx, req *TokenReq) error {
|
||||
// 刷新令牌
|
||||
func refreshToken(c *fiber.Ctx, req *TokenReq) error {
|
||||
if req.RefreshToken == "" {
|
||||
return sendError(c, services.ErrOauthInvalidRequest, "缺少必要参数:refresh_token")
|
||||
return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数:refresh_token")
|
||||
}
|
||||
|
||||
client, err := protect(c, services.GrantTypeRefreshToken, req.ClientID, req.ClientSecret)
|
||||
client, err := protect(c, s.OauthGrantTypeRefreshToken, req.ClientID, req.ClientSecret)
|
||||
if err != nil {
|
||||
return sendError(c, err)
|
||||
}
|
||||
|
||||
scope := strings.Split(req.Scope, ",")
|
||||
token, err := services.Auth.OauthRefreshToken(c.Context(), client, req.RefreshToken, scope)
|
||||
token, err := s.Auth.OauthRefreshToken(c.Context(), client, req.RefreshToken, scope)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrInvalidToken) {
|
||||
return sendError(c, services.ErrOauthInvalidGrant)
|
||||
if errors.Is(err, s.ErrInvalidToken) {
|
||||
return sendError(c, s.ErrOauthInvalidGrant)
|
||||
}
|
||||
return sendError(c, err)
|
||||
}
|
||||
@@ -135,8 +156,108 @@ func refreshToken(c *fiber.Ctx, req *TokenReq) error {
|
||||
return sendSuccess(c, token)
|
||||
}
|
||||
|
||||
func password(c *fiber.Ctx, req *TokenReq) error {
|
||||
if req.LoginType == "" {
|
||||
return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数:password_type")
|
||||
}
|
||||
if req.Username == "" {
|
||||
return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数:username")
|
||||
}
|
||||
if req.Password == "" {
|
||||
return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数:password")
|
||||
}
|
||||
|
||||
// 验证客户端凭证
|
||||
_, err := protect(c, s.OauthGrantTypePassword, req.ClientID, req.ClientSecret)
|
||||
if err != nil {
|
||||
return sendError(c, err)
|
||||
}
|
||||
|
||||
// 验证验证码
|
||||
err = s.Verifier.VerifySms(c.Context(), req.Username, req.Password)
|
||||
if err != nil {
|
||||
if errors.Is(err, s.ErrVerifierServiceInvalid) {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "验证码错误")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找用户
|
||||
var user *m.User
|
||||
err = q.Q.Transaction(func(tx *q.Query) error {
|
||||
|
||||
switch req.LoginType {
|
||||
case s.OauthGrantPasswordTypePhoneCode:
|
||||
user, err = tx.User.Where(tx.User.Phone.Eq(req.Username)).Take()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
case s.OauthGrantPasswordTypeEmailCode:
|
||||
user, err = tx.User.Where(tx.User.Email.Eq(req.Username)).Take()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
case s.OauthGrantPasswordTypePassword:
|
||||
user, err = tx.User.
|
||||
Where(tx.User.Or(
|
||||
tx.User.Phone.Eq(req.Username),
|
||||
tx.User.Email.Eq(req.Username),
|
||||
tx.User.Username.Eq(req.Username),
|
||||
)).
|
||||
Take()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return sendError(c, s.ErrOauthInvalidRequest, "无效的登录类型")
|
||||
}
|
||||
|
||||
// 如果用户不存在,初始化用户 todo 初始化默认权限信息
|
||||
if user == nil {
|
||||
user = &m.User{
|
||||
Phone: req.Username,
|
||||
Username: req.Username,
|
||||
}
|
||||
}
|
||||
|
||||
// 更新用户的登录时间
|
||||
user.LastLogin = time.Now()
|
||||
user.LastLoginHost = c.IP()
|
||||
user.LastLoginAgent = c.Get("User-Agent")
|
||||
if err := tx.User.Omit(q.User.AdminID).Save(user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存到会话
|
||||
auth := s.AuthContext{
|
||||
Payload: s.Payload{
|
||||
Id: user.ID,
|
||||
Type: s.PayloadUser,
|
||||
Name: user.Name,
|
||||
Avatar: user.Avatar,
|
||||
},
|
||||
}
|
||||
|
||||
duration := s.DefaultSessionConfig
|
||||
if !req.Remember {
|
||||
duration.RefreshTokenDuration = 0
|
||||
}
|
||||
token, err := s.Session.Create(c.Context(), auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sendSuccess(c, token)
|
||||
}
|
||||
|
||||
// 检查客户端凭证
|
||||
func protect(c *fiber.Ctx, grant services.GrantType, clientId, clientSecret string) (*models.Client, error) {
|
||||
func protect(c *fiber.Ctx, grant s.OauthGrantType, clientId, clientSecret string) (*m.Client, error) {
|
||||
header := c.Get("Authorization")
|
||||
if header != "" {
|
||||
basic := strings.TrimPrefix(header, "Basic ")
|
||||
@@ -155,44 +276,48 @@ func protect(c *fiber.Ctx, grant services.GrantType, clientId, clientSecret stri
|
||||
|
||||
// 查找客户端
|
||||
if clientId == "" {
|
||||
return nil, services.ErrOauthInvalidRequest
|
||||
return nil, s.ErrOauthInvalidRequest
|
||||
}
|
||||
client, err := q.Client.Where(q.Client.ClientID.Eq(clientId)).Take()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, services.ErrOauthInvalidClient
|
||||
return nil, s.ErrOauthInvalidClient
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 验证客户端状态
|
||||
if client.Status != 1 {
|
||||
return nil, services.ErrOauthUnauthorizedClient
|
||||
return nil, s.ErrOauthUnauthorizedClient
|
||||
}
|
||||
|
||||
// 验证授权类型
|
||||
switch grant {
|
||||
case services.GrantTypeAuthorizationCode:
|
||||
case s.OauthGrantTypeAuthorizationCode:
|
||||
if !client.GrantCode {
|
||||
return nil, services.ErrOauthUnauthorizedClient
|
||||
return nil, s.ErrOauthUnauthorizedClient
|
||||
}
|
||||
case services.GrantTypeClientCredentials:
|
||||
case s.OauthGrantTypeClientCredentials:
|
||||
if !client.GrantClient || client.Spec != 0 {
|
||||
return nil, services.ErrOauthUnauthorizedClient
|
||||
return nil, s.ErrOauthUnauthorizedClient
|
||||
}
|
||||
case services.GrantTypeRefreshToken:
|
||||
case s.OauthGrantTypeRefreshToken:
|
||||
if !client.GrantRefresh {
|
||||
return nil, services.ErrOauthUnauthorizedClient
|
||||
return nil, s.ErrOauthUnauthorizedClient
|
||||
}
|
||||
case s.OauthGrantTypePassword:
|
||||
if !client.GrantPassword {
|
||||
return nil, s.ErrOauthUnauthorizedClient
|
||||
}
|
||||
}
|
||||
|
||||
// 如果客户端是 confidential,验证 client_secret,失败返回错误
|
||||
if client.Spec == 0 {
|
||||
if clientSecret == "" {
|
||||
return nil, services.ErrOauthInvalidRequest
|
||||
return nil, s.ErrOauthInvalidRequest
|
||||
}
|
||||
if bcrypt.CompareHashAndPassword([]byte(client.ClientSecret), []byte(clientSecret)) != nil {
|
||||
return nil, services.ErrOauthInvalidClient
|
||||
return nil, s.ErrOauthInvalidClient
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +325,7 @@ func protect(c *fiber.Ctx, grant services.GrantType, clientId, clientSecret stri
|
||||
}
|
||||
|
||||
// 发送成功响应
|
||||
func sendSuccess(c *fiber.Ctx, details *services.TokenDetails) error {
|
||||
func sendSuccess(c *fiber.Ctx, details *s.TokenDetails) error {
|
||||
return c.JSON(TokenResp{
|
||||
AccessToken: details.AccessToken,
|
||||
TokenType: "Bearer",
|
||||
@@ -211,23 +336,23 @@ func sendSuccess(c *fiber.Ctx, details *services.TokenDetails) error {
|
||||
|
||||
// 发送错误响应
|
||||
func sendError(c *fiber.Ctx, err error, description ...string) error {
|
||||
var sErr services.AuthServiceOauthError
|
||||
var sErr s.AuthServiceOauthError
|
||||
if errors.As(err, &sErr) {
|
||||
status := fiber.StatusBadRequest
|
||||
var desc string
|
||||
switch {
|
||||
case errors.Is(sErr, services.ErrOauthInvalidRequest):
|
||||
case errors.Is(sErr, s.ErrOauthInvalidRequest):
|
||||
desc = "无效的请求"
|
||||
case errors.Is(sErr, services.ErrOauthInvalidClient):
|
||||
case errors.Is(sErr, s.ErrOauthInvalidClient):
|
||||
status = fiber.StatusUnauthorized
|
||||
desc = "无效的客户端凭证"
|
||||
case errors.Is(sErr, services.ErrOauthInvalidGrant):
|
||||
case errors.Is(sErr, s.ErrOauthInvalidGrant):
|
||||
desc = "无效的授权凭证"
|
||||
case errors.Is(sErr, services.ErrOauthInvalidScope):
|
||||
case errors.Is(sErr, s.ErrOauthInvalidScope):
|
||||
desc = "无效的授权范围"
|
||||
case errors.Is(sErr, services.ErrOauthUnauthorizedClient):
|
||||
case errors.Is(sErr, s.ErrOauthUnauthorizedClient):
|
||||
desc = "未授权的客户端"
|
||||
case errors.Is(sErr, services.ErrOauthUnsupportedGrantType):
|
||||
case errors.Is(sErr, s.ErrOauthUnsupportedGrantType):
|
||||
desc = "不支持的授权类型"
|
||||
}
|
||||
if len(description) > 0 {
|
||||
|
||||
@@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"platform/web/auth"
|
||||
"platform/web/services"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -16,6 +17,13 @@ type VerifierReq struct {
|
||||
|
||||
func SmsCode(c *fiber.Ctx) error {
|
||||
|
||||
_, err := auth.Protect(c, []services.PayloadType{
|
||||
services.PayloadClientConfidential,
|
||||
}, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(VerifierReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
|
||||
@@ -20,12 +20,12 @@ type Bill struct {
|
||||
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
TradeID int32 `gorm:"column:trade_id" json:"trade_id"`
|
||||
ResourceID int32 `gorm:"column:resource_id" json:"resource_id"`
|
||||
Type int32 `gorm:"column:type;not null" json:"type"`
|
||||
BillNo string `gorm:"column:bill_no;not null" json:"bill_no"`
|
||||
RefundID int32 `gorm:"column:refund_id" json:"refund_id"`
|
||||
Amount float64 `gorm:"column:amount;not null" json:"amount"`
|
||||
TradeID int32 `gorm:"column:trade_id;comment:订单ID" json:"trade_id"` // 订单ID
|
||||
ResourceID int32 `gorm:"column:resource_id;comment:套餐ID" json:"resource_id"` // 套餐ID
|
||||
Type int32 `gorm:"column:type;not null;comment:账单类型:0-充值,1-消费,2-退款" json:"type"` // 账单类型:0-充值,1-消费,2-退款
|
||||
BillNo string `gorm:"column:bill_no;not null;comment:易读账单号" json:"bill_no"` // 易读账单号
|
||||
RefundID int32 `gorm:"column:refund_id;comment:退款ID" json:"refund_id"` // 退款ID
|
||||
Amount float64 `gorm:"column:amount;not null;comment:账单金额" json:"amount"` // 账单金额
|
||||
Trade *Trade `gorm:"foreignKey:TradeID" json:"trade"`
|
||||
Refund *Refund `gorm:"foreignKey:RefundID" json:"refund"`
|
||||
Resource *Resource `gorm:"foreignKey:ResourceID" json:"resource"`
|
||||
|
||||
@@ -31,7 +31,7 @@ type Channel struct {
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
ProxyHost string `gorm:"column:proxy_host;not null" json:"proxy_host"`
|
||||
Protocol int32 `gorm:"column:protocol" json:"protocol"`
|
||||
Protocol int32 `gorm:"column:protocol;comment:协议类型:1-http,2-https,3-socks5" json:"protocol"` // 协议类型:1-http,2-https,3-socks5
|
||||
}
|
||||
|
||||
// TableName Channel's table name
|
||||
|
||||
@@ -14,20 +14,21 @@ const TableNameClient = "client"
|
||||
|
||||
// Client mapped from table <client>
|
||||
type Client struct {
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:客户端ID" json:"id"` // 客户端ID
|
||||
ClientID string `gorm:"column:client_id;not null;comment:OAuth2客户端标识符" json:"client_id"` // OAuth2客户端标识符
|
||||
ClientSecret string `gorm:"column:client_secret;not null;comment:OAuth2客户端密钥" json:"client_secret"` // OAuth2客户端密钥
|
||||
RedirectURI string `gorm:"column:redirect_uri;comment:OAuth2 重定向URI" json:"redirect_uri"` // OAuth2 重定向URI
|
||||
GrantCode bool `gorm:"column:grant_code;not null;comment:允许授权码授予" json:"grant_code"` // 允许授权码授予
|
||||
GrantClient bool `gorm:"column:grant_client;not null;comment:允许客户端凭证授予" json:"grant_client"` // 允许客户端凭证授予
|
||||
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"` // 名称
|
||||
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 common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:客户端ID" json:"id"` // 客户端ID
|
||||
ClientID string `gorm:"column:client_id;not null;comment:OAuth2客户端标识符" json:"client_id"` // OAuth2客户端标识符
|
||||
ClientSecret string `gorm:"column:client_secret;not null;comment:OAuth2客户端密钥" json:"client_secret"` // OAuth2客户端密钥
|
||||
RedirectURI string `gorm:"column:redirect_uri;comment:OAuth2 重定向URI" json:"redirect_uri"` // OAuth2 重定向URI
|
||||
GrantCode bool `gorm:"column:grant_code;not null;comment:允许授权码授予" json:"grant_code"` // 允许授权码授予
|
||||
GrantClient bool `gorm:"column:grant_client;not null;comment:允许客户端凭证授予" json:"grant_client"` // 允许客户端凭证授予
|
||||
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"` // 名称
|
||||
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 common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
GrantPassword bool `gorm:"column:grant_password;not null" json:"grant_password"`
|
||||
}
|
||||
|
||||
// TableName Client's table name
|
||||
|
||||
@@ -15,17 +15,17 @@ const TableNameCoupon = "coupon"
|
||||
|
||||
// Coupon mapped from table <coupon>
|
||||
type Coupon struct {
|
||||
ExpireAt time.Time `gorm:"column:expire_at" json:"expire_at"`
|
||||
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at" json:"deleted_at"`
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||
UserID int32 `gorm:"column:user_id" json:"user_id"`
|
||||
Status int32 `gorm:"column:status;not null" json:"status"`
|
||||
Code string `gorm:"column:code;not null" json:"code"`
|
||||
Remark string `gorm:"column:remark" json:"remark"`
|
||||
Amount float64 `gorm:"column:amount;not null" json:"amount"`
|
||||
MinAmount float64 `gorm:"column:min_amount;not null" json:"min_amount"`
|
||||
ExpireAt time.Time `gorm:"column:expire_at;comment:过期时间" json:"expire_at"` // 过期时间
|
||||
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:优惠券ID" json:"id"` // 优惠券ID
|
||||
UserID int32 `gorm:"column:user_id;comment:用户ID" json:"user_id"` // 用户ID
|
||||
Status int32 `gorm:"column:status;not null;comment:优惠券状态:0-未使用,1-已使用,2-已过期" json:"status"` // 优惠券状态:0-未使用,1-已使用,2-已过期
|
||||
Code string `gorm:"column:code;not null;comment:优惠券代码" json:"code"` // 优惠券代码
|
||||
Remark string `gorm:"column:remark;comment:优惠券备注" json:"remark"` // 优惠券备注
|
||||
Amount float64 `gorm:"column:amount;not null;comment:优惠券金额" json:"amount"` // 优惠券金额
|
||||
MinAmount float64 `gorm:"column:min_amount;not null;comment:最低消费金额" json:"min_amount"` // 最低消费金额
|
||||
}
|
||||
|
||||
// TableName Coupon's table name
|
||||
|
||||
@@ -22,13 +22,13 @@ type Node struct {
|
||||
City string `gorm:"column:city;not null;comment:城市" json:"city"` // 城市
|
||||
ProxyID int32 `gorm:"column:proxy_id;comment:代理ID" json:"proxy_id"` // 代理ID
|
||||
ProxyPort int32 `gorm:"column:proxy_port;comment:代理端口" json:"proxy_port"` // 代理端口
|
||||
Status int32 `gorm:"column:status;not null;comment:节点状态:1-正常,0-离线" json:"status"` // 节点状态:1-正常,0-离线
|
||||
Status int32 `gorm:"column:status;not null;comment:节点状态:0-离线,1-正常" json:"status"` // 节点状态:0-离线,1-正常
|
||||
Rtt int32 `gorm:"column:rtt;comment:延迟" json:"rtt"` // 延迟
|
||||
Loss int32 `gorm:"column:loss;comment:丢包率" json:"loss"` // 丢包率
|
||||
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
Isp int32 `gorm:"column:isp;not null" json:"isp"`
|
||||
Isp int32 `gorm:"column:isp;not null;comment:运营商:0-其他,1-电信,2-联通,3-移动" json:"isp"` // 运营商:0-其他,1-电信,2-联通,3-移动
|
||||
}
|
||||
|
||||
// TableName Node's table name
|
||||
|
||||
@@ -20,9 +20,9 @@ type Refund struct {
|
||||
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
TradeID int32 `gorm:"column:trade_id;not null" json:"trade_id"`
|
||||
Reason string `gorm:"column:reason" json:"reason"`
|
||||
Status int32 `gorm:"column:status;not null" json:"status"`
|
||||
TradeID int32 `gorm:"column:trade_id;not null;comment:订单ID" json:"trade_id"` // 订单ID
|
||||
Reason string `gorm:"column:reason;comment:退款原因" json:"reason"` // 退款原因
|
||||
Status int32 `gorm:"column:status;not null;comment:退款状态:0-待处理,1-已退款,2-已拒绝" json:"status"` // 退款状态:0-待处理,1-已退款,2-已拒绝
|
||||
}
|
||||
|
||||
// TableName Refund's table name
|
||||
|
||||
@@ -20,8 +20,8 @@ type Resource struct {
|
||||
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
ResourceNo string `gorm:"column:resource_no" json:"resource_no"`
|
||||
Type int32 `gorm:"column:type;not null" json:"type"`
|
||||
ResourceNo string `gorm:"column:resource_no;comment:套餐编号" json:"resource_no"` // 套餐编号
|
||||
Type int32 `gorm:"column:type;not null;comment:套餐类型:1-动态,2-隧道,3-独享" json:"type"` // 套餐类型:1-动态,2-隧道,3-独享
|
||||
Pss *ResourcePss `gorm:"foreignKey:ResourceID;references:ID" json:"pss"`
|
||||
}
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@ type Trade struct {
|
||||
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
Type int32 `gorm:"column:type;not null" json:"type"`
|
||||
CancelAt common.LocalDateTime `gorm:"column:cancel_at" json:"cancel_at"`
|
||||
PaidAt common.LocalDateTime `gorm:"column:paid_at" json:"paid_at"`
|
||||
PayURL string `gorm:"column:pay_url" json:"pay_url"`
|
||||
Type int32 `gorm:"column:type;not null;comment:订单类型:0-充值余额,1-购买产品" json:"type"` // 订单类型:0-充值余额,1-购买产品
|
||||
CancelAt common.LocalDateTime `gorm:"column:cancel_at;comment:取消时间" json:"cancel_at"` // 取消时间
|
||||
PaidAt common.LocalDateTime `gorm:"column:paid_at;comment:支付时间" json:"paid_at"` // 支付时间
|
||||
PayURL string `gorm:"column:pay_url;comment:支付链接" json:"pay_url"` // 支付链接
|
||||
}
|
||||
|
||||
// TableName Trade's table name
|
||||
|
||||
@@ -20,7 +20,7 @@ type Whitelist struct {
|
||||
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
Remark string `gorm:"column:remark" json:"remark"`
|
||||
Remark string `gorm:"column:remark;comment:备注" json:"remark"` // 备注
|
||||
}
|
||||
|
||||
// TableName Whitelist's table name
|
||||
|
||||
@@ -71,18 +71,18 @@ type bill struct {
|
||||
billDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int32 // 账单ID
|
||||
UserID field.Int32 // 用户ID
|
||||
Info field.String // 产品可读信息
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
TradeID field.Int32
|
||||
ResourceID field.Int32
|
||||
Type field.Int32
|
||||
BillNo field.String
|
||||
RefundID field.Int32
|
||||
Amount field.Float64
|
||||
ID field.Int32 // 账单ID
|
||||
UserID field.Int32 // 用户ID
|
||||
Info field.String // 产品可读信息
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
TradeID field.Int32 // 订单ID
|
||||
ResourceID field.Int32 // 套餐ID
|
||||
Type field.Int32 // 账单类型:0-充值,1-消费,2-退款
|
||||
BillNo field.String // 易读账单号
|
||||
RefundID field.Int32 // 退款ID
|
||||
Amount field.Float64 // 账单金额
|
||||
Trade billBelongsToTrade
|
||||
|
||||
Refund billBelongsToRefund
|
||||
|
||||
@@ -70,7 +70,7 @@ type channel struct {
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
ProxyHost field.String
|
||||
Protocol field.Int32
|
||||
Protocol field.Int32 // 协议类型:1-http,2-https,3-socks5
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ func newClient(db *gorm.DB, opts ...gen.DOOption) client {
|
||||
_client.CreatedAt = field.NewField(tableName, "created_at")
|
||||
_client.UpdatedAt = field.NewField(tableName, "updated_at")
|
||||
_client.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
_client.GrantPassword = field.NewBool(tableName, "grant_password")
|
||||
|
||||
_client.fillFieldMap()
|
||||
|
||||
@@ -50,21 +51,22 @@ func newClient(db *gorm.DB, opts ...gen.DOOption) client {
|
||||
type client struct {
|
||||
clientDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int32 // 客户端ID
|
||||
ClientID field.String // OAuth2客户端标识符
|
||||
ClientSecret field.String // OAuth2客户端密钥
|
||||
RedirectURI field.String // OAuth2 重定向URI
|
||||
GrantCode field.Bool // 允许授权码授予
|
||||
GrantClient field.Bool // 允许客户端凭证授予
|
||||
GrantRefresh field.Bool // 允许刷新令牌授予
|
||||
Spec field.Int32 // 安全规范:0-web,1-native,2-browser
|
||||
Name field.String // 名称
|
||||
Icon field.String // 图标URL
|
||||
Status field.Int32 // 状态:1-正常,0-禁用
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
ALL field.Asterisk
|
||||
ID field.Int32 // 客户端ID
|
||||
ClientID field.String // OAuth2客户端标识符
|
||||
ClientSecret field.String // OAuth2客户端密钥
|
||||
RedirectURI field.String // OAuth2 重定向URI
|
||||
GrantCode field.Bool // 允许授权码授予
|
||||
GrantClient field.Bool // 允许客户端凭证授予
|
||||
GrantRefresh field.Bool // 允许刷新令牌授予
|
||||
Spec field.Int32 // 安全规范:0-web,1-native,2-browser
|
||||
Name field.String // 名称
|
||||
Icon field.String // 图标URL
|
||||
Status field.Int32 // 状态:1-正常,0-禁用
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
GrantPassword field.Bool
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
@@ -95,6 +97,7 @@ func (c *client) updateTableName(table string) *client {
|
||||
c.CreatedAt = field.NewField(table, "created_at")
|
||||
c.UpdatedAt = field.NewField(table, "updated_at")
|
||||
c.DeletedAt = field.NewField(table, "deleted_at")
|
||||
c.GrantPassword = field.NewBool(table, "grant_password")
|
||||
|
||||
c.fillFieldMap()
|
||||
|
||||
@@ -111,7 +114,7 @@ func (c *client) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
}
|
||||
|
||||
func (c *client) fillFieldMap() {
|
||||
c.fieldMap = make(map[string]field.Expr, 14)
|
||||
c.fieldMap = make(map[string]field.Expr, 15)
|
||||
c.fieldMap["id"] = c.ID
|
||||
c.fieldMap["client_id"] = c.ClientID
|
||||
c.fieldMap["client_secret"] = c.ClientSecret
|
||||
@@ -126,6 +129,7 @@ func (c *client) fillFieldMap() {
|
||||
c.fieldMap["created_at"] = c.CreatedAt
|
||||
c.fieldMap["updated_at"] = c.UpdatedAt
|
||||
c.fieldMap["deleted_at"] = c.DeletedAt
|
||||
c.fieldMap["grant_password"] = c.GrantPassword
|
||||
}
|
||||
|
||||
func (c client) clone(db *gorm.DB) client {
|
||||
|
||||
@@ -48,17 +48,17 @@ type coupon struct {
|
||||
couponDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ExpireAt field.Time
|
||||
CreatedAt field.Field
|
||||
UpdatedAt field.Field
|
||||
DeletedAt field.Field
|
||||
ID field.Int32
|
||||
UserID field.Int32
|
||||
Status field.Int32
|
||||
Code field.String
|
||||
Remark field.String
|
||||
Amount field.Float64
|
||||
MinAmount field.Float64
|
||||
ExpireAt field.Time // 过期时间
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
ID field.Int32 // 优惠券ID
|
||||
UserID field.Int32 // 用户ID
|
||||
Status field.Int32 // 优惠券状态:0-未使用,1-已使用,2-已过期
|
||||
Code field.String // 优惠券代码
|
||||
Remark field.String // 优惠券备注
|
||||
Amount field.Float64 // 优惠券金额
|
||||
MinAmount field.Float64 // 最低消费金额
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
@@ -60,13 +60,13 @@ type node struct {
|
||||
City field.String // 城市
|
||||
ProxyID field.Int32 // 代理ID
|
||||
ProxyPort field.Int32 // 代理端口
|
||||
Status field.Int32 // 节点状态:1-正常,0-离线
|
||||
Status field.Int32 // 节点状态:0-离线,1-正常
|
||||
Rtt field.Int32 // 延迟
|
||||
Loss field.Int32 // 丢包率
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
Isp field.Int32
|
||||
Isp field.Int32 // 运营商:0-其他,1-电信,2-联通,3-移动
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
@@ -52,9 +52,9 @@ type refund struct {
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
TradeID field.Int32
|
||||
Reason field.String
|
||||
Status field.Int32
|
||||
TradeID field.Int32 // 订单ID
|
||||
Reason field.String // 退款原因
|
||||
Status field.Int32 // 退款状态:0-待处理,1-已退款,2-已拒绝
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
@@ -50,14 +50,14 @@ type resource struct {
|
||||
resourceDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int32 // 套餐ID
|
||||
UserID field.Int32 // 用户ID
|
||||
Active field.Bool // 套餐状态
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
ResourceNo field.String
|
||||
Type field.Int32
|
||||
ID field.Int32 // 套餐ID
|
||||
UserID field.Int32 // 用户ID
|
||||
Active field.Bool // 套餐状态
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
ResourceNo field.String // 套餐编号
|
||||
Type field.Int32 // 套餐类型:1-动态,2-隧道,3-独享
|
||||
Pss resourceHasOnePss
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
|
||||
@@ -67,10 +67,10 @@ type trade struct {
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
Type field.Int32
|
||||
CancelAt field.Field
|
||||
PaidAt field.Field
|
||||
PayURL field.String
|
||||
Type field.Int32 // 订单类型:0-充值余额,1-购买产品
|
||||
CancelAt field.Field // 取消时间
|
||||
PaidAt field.Field // 支付时间
|
||||
PayURL field.String // 支付链接
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ type whitelist struct {
|
||||
CreatedAt field.Field // 创建时间
|
||||
UpdatedAt field.Field // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
Remark field.String
|
||||
Remark field.String // 备注
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ func ApplyRouters(app *fiber.App) {
|
||||
|
||||
// 认证
|
||||
auth := api.Group("/auth")
|
||||
auth.Post("/verify/sms", auth2.PermitDevice(), handlers.SmsCode)
|
||||
auth.Post("/verify/sms", handlers.SmsCode)
|
||||
auth.Post("/login/sms", auth2.PermitDevice(), handlers.Login)
|
||||
auth.Post("/logout", handlers.Logout)
|
||||
auth.Post("/token", handlers.Token)
|
||||
|
||||
@@ -85,10 +85,19 @@ func (s *authService) OauthRefreshToken(ctx context.Context, client *models.Clie
|
||||
return details, nil
|
||||
}
|
||||
|
||||
type GrantType int
|
||||
type OauthGrantType string
|
||||
|
||||
const (
|
||||
GrantTypeAuthorizationCode GrantType = iota
|
||||
GrantTypeClientCredentials
|
||||
GrantTypeRefreshToken
|
||||
OauthGrantTypeAuthorizationCode = OauthGrantType("authorization_code")
|
||||
OauthGrantTypeClientCredentials = OauthGrantType("client_credentials")
|
||||
OauthGrantTypeRefreshToken = OauthGrantType("refresh_token")
|
||||
OauthGrantTypePassword = OauthGrantType("password")
|
||||
)
|
||||
|
||||
type OauthGrantLoginType string
|
||||
|
||||
const (
|
||||
OauthGrantPasswordTypePassword = OauthGrantLoginType("password")
|
||||
OauthGrantPasswordTypePhoneCode = OauthGrantLoginType("phone_code")
|
||||
OauthGrantPasswordTypeEmailCode = OauthGrantLoginType("email_code")
|
||||
)
|
||||
|
||||
@@ -239,6 +239,7 @@ func mergeConfig(defaultCfg SessionConfig, customCfg SessionConfig) SessionConfi
|
||||
// AuthContext 定义认证信息
|
||||
type AuthContext struct {
|
||||
Payload Payload `json:"payload"`
|
||||
Agent Agent `json:"agent,omitempty"`
|
||||
Permissions map[string]struct{} `json:"permissions,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
@@ -265,6 +266,11 @@ const (
|
||||
PayloadClientConfidential
|
||||
)
|
||||
|
||||
type Agent struct {
|
||||
Id int32 `json:"id,omitempty"`
|
||||
Addr string `json:"addr,omitempty"`
|
||||
}
|
||||
|
||||
// AnyPermission 检查认证是否包含指定权限
|
||||
func (a *AuthContext) AnyPermission(requiredPermission ...string) bool {
|
||||
if a == nil || a.Permissions == nil {
|
||||
|
||||
Reference in New Issue
Block a user