添加token颁发器
This commit is contained in:
parent
426e493967
commit
47e4772618
7
devcloud/.vscode/settings.json
vendored
Normal file
7
devcloud/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"go.testEnvVars": {
|
||||||
|
"workspaceFolder": "${workspaceFolder}",
|
||||||
|
"CONFIG_PATH": "${workspaceFolder}/etc/application.toml"
|
||||||
|
},
|
||||||
|
"go.testEnvFile": "${workspaceFolder}/etc/unit_test.env"
|
||||||
|
}
|
0
devcloud/etc/unit_test.env
Normal file
0
devcloud/etc/unit_test.env
Normal file
@ -1 +1,12 @@
|
|||||||
package apps
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/user/api"
|
||||||
|
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/user/impl"
|
||||||
|
|
||||||
|
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/api"
|
||||||
|
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/impl"
|
||||||
|
|
||||||
|
// 颁发器
|
||||||
|
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/issuers"
|
||||||
|
)
|
||||||
|
1
devcloud/mcenter/apps/token/api/api.go
Normal file
1
devcloud/mcenter/apps/token/api/api.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package api
|
1
devcloud/mcenter/apps/token/api/token.go
Normal file
1
devcloud/mcenter/apps/token/api/token.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package api
|
21
devcloud/mcenter/apps/token/const.go
Normal file
21
devcloud/mcenter/apps/token/const.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package token
|
||||||
|
|
||||||
|
import "github.com/infraboard/mcube/v2/exception"
|
||||||
|
|
||||||
|
const (
|
||||||
|
ACCESS_TOKEN_HEADER_NAME = "Authorization"
|
||||||
|
ACCESS_TOKEN_COOKIE_NAME = "access_token"
|
||||||
|
ACCESS_TOKEN_RESPONSE_HEADER_NAME = "X-OAUTH-TOKEN"
|
||||||
|
REFRESH_TOKEN_HEADER_NAME = "X-REFRUSH-TOKEN"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 自定义非导出类型,避免外部包直接实例化
|
||||||
|
type tokenContextKey struct{}
|
||||||
|
|
||||||
|
var (
|
||||||
|
CTX_TOKEN_KEY = tokenContextKey{}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
CookieNotFound = exception.NewUnauthorized("cookie %s not found", ACCESS_TOKEN_COOKIE_NAME)
|
||||||
|
)
|
@ -29,3 +29,9 @@ const (
|
|||||||
// 异常Ip登陆
|
// 异常Ip登陆
|
||||||
LOCK_TYPE_OTHER_IP_LOGGED_IN
|
LOCK_TYPE_OTHER_IP_LOGGED_IN
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DESCRIBE_BY int
|
||||||
|
|
||||||
|
const (
|
||||||
|
DESCRIBE_BY_ACCESS_TOKEN DESCRIBE_BY = iota
|
||||||
|
)
|
||||||
|
@ -1 +1,56 @@
|
|||||||
package impl
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/user"
|
||||||
|
"github.com/infraboard/mcube/v2/ioc"
|
||||||
|
"github.com/infraboard/mcube/v2/ioc/config/datasource"
|
||||||
|
"github.com/infraboard/mcube/v2/ioc/config/log"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ioc.Controller().Registry(&TokenServiceImpl{
|
||||||
|
AutoRefresh: true,
|
||||||
|
RereshTTLSecond: 1 * 60 * 60,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ token.Service = (*TokenServiceImpl)(nil)
|
||||||
|
|
||||||
|
type TokenServiceImpl struct {
|
||||||
|
ioc.ObjectImpl
|
||||||
|
user user.Service
|
||||||
|
log *zerolog.Logger
|
||||||
|
// policy policy.PermissionService
|
||||||
|
|
||||||
|
// 自动刷新, 直接刷新Token的过期时间,而不是生成一个新Token
|
||||||
|
AutoRefresh bool `json:"auto_refresh" toml:"auto_refresh" yaml:"auto_refresh" env:"AUTO_REFRESH"`
|
||||||
|
// 刷新TTL
|
||||||
|
RereshTTLSecond uint64 `json:"refresh_ttl" toml:"refresh_ttl" yaml:"refresh_ttl" env:"REFRESH_TTL"`
|
||||||
|
// Api最多多少个, 这种Token往往过期时间比较长, 为了安全不要申请太多
|
||||||
|
MaxActiveApiToken uint8 `json:"max_active_api_token" toml:"max_active_api_token" yaml:"max_active_api_token" env:"MAX_ACTIVE_API_TOKEN"`
|
||||||
|
|
||||||
|
refreshDuration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *TokenServiceImpl) Init() error {
|
||||||
|
i.log = log.Sub(i.Name())
|
||||||
|
i.user = user.GetService()
|
||||||
|
// i.policy = policy.GetService()
|
||||||
|
i.refreshDuration = time.Duration(i.RereshTTLSecond) * time.Second
|
||||||
|
|
||||||
|
if datasource.Get().AutoMigrate {
|
||||||
|
err := datasource.DB().AutoMigrate(&token.Token{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *TokenServiceImpl) Name() string {
|
||||||
|
return token.APP_NAME
|
||||||
|
}
|
||||||
|
18
devcloud/mcenter/apps/token/impl/impl_test.go
Normal file
18
devcloud/mcenter/apps/token/impl/impl_test.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package impl_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
svc token.Service
|
||||||
|
ctx = context.Background()
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
test.DevelopmentSet()
|
||||||
|
svc = token.GetService()
|
||||||
|
}
|
@ -1 +1,201 @@
|
|||||||
package impl
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
||||||
|
"github.com/infraboard/mcube/v2/desense"
|
||||||
|
"github.com/infraboard/mcube/v2/exception"
|
||||||
|
"github.com/infraboard/mcube/v2/ioc/config/datasource"
|
||||||
|
"github.com/infraboard/mcube/v2/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 登录接口(颁发Token)
|
||||||
|
func (i *TokenServiceImpl) IssueToken(ctx context.Context, in *token.IssueTokenRequest) (*token.Token, error) {
|
||||||
|
// 颁发Token
|
||||||
|
// user/password
|
||||||
|
// ldap
|
||||||
|
// 飞书,企业微信 ...
|
||||||
|
issuer := token.GetIssuer(in.Issuer)
|
||||||
|
if issuer == nil {
|
||||||
|
return nil, exception.NewBadRequest("issuer %s no support", in.Issuer)
|
||||||
|
}
|
||||||
|
tk, err := issuer.IssueToken(ctx, in.Parameter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tk.SetIssuer(in.Issuer).SetSource(in.Source)
|
||||||
|
|
||||||
|
// 判断当前数据库有没有已经存在的Token
|
||||||
|
activeTokenQueryReq := token.NewQueryTokenRequest().
|
||||||
|
AddUserId(tk.UserId).
|
||||||
|
SetSource(in.Source).
|
||||||
|
SetActive(true)
|
||||||
|
tks, err := i.QueryToken(ctx, activeTokenQueryReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch in.Source {
|
||||||
|
// 每个端只能有1个活跃登录
|
||||||
|
case token.SOURCE_WEB, token.SOURCE_IOS, token.SOURCE_ANDROID, token.SOURCE_PC:
|
||||||
|
if tks.Len() > 0 {
|
||||||
|
i.log.Debug().Msgf("use exist active token: %s", desense.Default().DeSense(tk.AccessToken, "4", "3"))
|
||||||
|
return tks.Items[0], nil
|
||||||
|
}
|
||||||
|
case token.SOURCE_API:
|
||||||
|
if tks.Len() > int(i.MaxActiveApiToken) {
|
||||||
|
return nil, exception.NewBadRequest("max active api token overflow")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保持Token
|
||||||
|
if err := datasource.DBFromCtx(ctx).
|
||||||
|
Create(tk).
|
||||||
|
Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验Token 是给内部中间层使用 身份校验层
|
||||||
|
func (i *TokenServiceImpl) ValiateToken(ctx context.Context, req *token.ValiateTokenRequest) (*token.Token, error) {
|
||||||
|
// 1. 查询Token (是不是我们这个系统颁发的)
|
||||||
|
tk := token.NewToken()
|
||||||
|
err := datasource.DBFromCtx(ctx).
|
||||||
|
Where("access_token = ?", req.AccessToken).
|
||||||
|
First(tk).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.1 判断Ak是否过期
|
||||||
|
if err := tk.IsAccessTokenExpired(); err != nil {
|
||||||
|
// 判断刷新Token是否过期
|
||||||
|
if err := tk.IsRreshTokenExpired(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果开启了自动刷新
|
||||||
|
if i.AutoRefresh {
|
||||||
|
tk.SetRefreshAt(time.Now())
|
||||||
|
tk.SetExpiredAtByDuration(i.refreshDuration, 4)
|
||||||
|
if err := datasource.DBFromCtx(ctx).Save(tk); err != nil {
|
||||||
|
i.log.Error().Msgf("auto refresh token error, %s", err.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *TokenServiceImpl) DescribeToken(ctx context.Context, in *token.DescribeTokenRequest) (*token.Token, error) {
|
||||||
|
query := datasource.DBFromCtx(ctx)
|
||||||
|
switch in.DescribeBy {
|
||||||
|
case token.DESCRIBE_BY_ACCESS_TOKEN:
|
||||||
|
query = query.Where("access_token = ?", in.DescribeValue)
|
||||||
|
default:
|
||||||
|
return nil, exception.NewBadRequest("unspport describe type %s", in.DescribeValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
tk := token.NewToken()
|
||||||
|
if err := query.First(tk).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退出接口(销毁Token)
|
||||||
|
func (i *TokenServiceImpl) RevolkToken(ctx context.Context, in *token.RevolkTokenRequest) (*token.Token, error) {
|
||||||
|
tk, err := i.DescribeToken(ctx, token.NewDescribeTokenRequest(in.AccessToken))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := tk.CheckRefreshToken(in.RefreshToken); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tk.Lock(token.LOCK_TYPE_REVOLK, "user revolk token")
|
||||||
|
err = datasource.DBFromCtx(ctx).Model(&token.Token{}).
|
||||||
|
Where("access_token = ?", in.AccessToken).
|
||||||
|
Where("refresh_token = ?", in.RefreshToken).
|
||||||
|
Updates(tk.Status.ToMap()).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tk, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询已经颁发出去的Token
|
||||||
|
func (i *TokenServiceImpl) QueryToken(ctx context.Context, in *token.QueryTokenRequest) (*types.Set[*token.Token], error) {
|
||||||
|
set := types.New[*token.Token]()
|
||||||
|
query := datasource.DBFromCtx(ctx).Model(&token.Token{})
|
||||||
|
|
||||||
|
if in.Active != nil {
|
||||||
|
if *in.Active {
|
||||||
|
query = query.
|
||||||
|
Where("lock_at IS NULL AND refresh_token_expired_at > ?", time.Now())
|
||||||
|
} else {
|
||||||
|
query = query.
|
||||||
|
Where("lock_at IS NOT NULL OR refresh_token_expired_at <= ?", time.Now())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Source != nil {
|
||||||
|
query = query.Where("source = ?", *in.Source)
|
||||||
|
}
|
||||||
|
if len(in.UserIds) > 0 {
|
||||||
|
query = query.Where("user_id IN ?", in.UserIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询总量
|
||||||
|
err := query.Count(&set.Total).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = query.
|
||||||
|
Order("issue_at desc").
|
||||||
|
Offset(int(in.ComputeOffset())).
|
||||||
|
Limit(int(in.PageSize)).
|
||||||
|
Find(&set.Items).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return set, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户切换空间
|
||||||
|
// func (i *TokenServiceImpl) ChangeNamespce(ctx context.Context, in *token.ChangeNamespceRequest) (*token.Token, error) {
|
||||||
|
// set, err := i.policy.QueryNamespace(ctx, policy.NewQueryNamespaceRequest().SetUserId(in.UserId).SetNamespaceId(in.NamespaceId))
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ns := set.First()
|
||||||
|
// if ns == nil {
|
||||||
|
// return nil, exception.NewPermissionDeny("你没有该空间访问权限")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 更新Token
|
||||||
|
// tk, err := i.DescribeToken(ctx, token.NewDescribeTokenRequest(in.AccessToken))
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// tk.NamespaceId = ns.Id
|
||||||
|
// tk.NamespaceName = ns.Name
|
||||||
|
|
||||||
|
// // 保存状态
|
||||||
|
// if err := datasource.DBFromCtx(ctx).
|
||||||
|
// Updates(tk).
|
||||||
|
// Error; err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// return tk, nil
|
||||||
|
// }
|
||||||
|
27
devcloud/mcenter/apps/token/impl/token_test.go
Normal file
27
devcloud/mcenter/apps/token/impl/token_test.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package impl_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIssueToken(t *testing.T) {
|
||||||
|
req := token.NewIssueTokenRequest()
|
||||||
|
req.IssueByPassword("admin", "123456")
|
||||||
|
req.Source = token.SOURCE_WEB
|
||||||
|
set, err := svc.IssueToken(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQueryToken(t *testing.T) {
|
||||||
|
req := token.NewQueryTokenRequest()
|
||||||
|
set, err := svc.QueryToken(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(set)
|
||||||
|
}
|
@ -3,18 +3,84 @@ package token
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/infraboard/mcube/v2/http/request"
|
||||||
|
"github.com/infraboard/mcube/v2/ioc"
|
||||||
|
"github.com/infraboard/mcube/v2/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
APP_NAME = "token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetService() Service {
|
||||||
|
return ioc.Controller().Get(APP_NAME).(Service)
|
||||||
|
}
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
// 颁发访问令牌: Login
|
// 颁发访问令牌: Login
|
||||||
IssueToken(context.Context, *IssueTokenRequest) (*Token, error)
|
IssueToken(context.Context, *IssueTokenRequest) (*Token, error)
|
||||||
// 撤销访问令牌: 令牌失效了 Logout
|
// 撤销访问令牌: 令牌失效了 Logout
|
||||||
RevolkToken(context.Context, *RevolkTokenRequest) (*Token, error)
|
RevolkToken(context.Context, *RevolkTokenRequest) (*Token, error)
|
||||||
|
// 查询已经颁发出去的Token
|
||||||
|
QueryToken(context.Context, *QueryTokenRequest) (*types.Set[*Token], error)
|
||||||
|
|
||||||
|
// 查询Token详情
|
||||||
|
DescribeToken(context.Context, *DescribeTokenRequest) (*Token, error)
|
||||||
// 校验访问令牌:检查令牌的合法性, 是不是伪造的
|
// 校验访问令牌:检查令牌的合法性, 是不是伪造的
|
||||||
ValiateToken(context.Context, *ValiateTokenRequest) (*Token, error)
|
ValiateToken(context.Context, *ValiateTokenRequest) (*Token, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewDescribeTokenRequest(accessToken string) *DescribeTokenRequest {
|
||||||
|
return &DescribeTokenRequest{
|
||||||
|
DescribeBy: DESCRIBE_BY_ACCESS_TOKEN,
|
||||||
|
DescribeValue: accessToken,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DescribeTokenRequest struct {
|
||||||
|
DescribeBy DESCRIBE_BY `json:"describe_by"`
|
||||||
|
DescribeValue string `json:"describe_value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQueryTokenRequest() *QueryTokenRequest {
|
||||||
|
return &QueryTokenRequest{
|
||||||
|
PageRequest: request.NewDefaultPageRequest(),
|
||||||
|
UserIds: []uint64{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryTokenRequest struct {
|
||||||
|
*request.PageRequest
|
||||||
|
// 当前可用的没过期的Token
|
||||||
|
Active *bool `json:"active"`
|
||||||
|
// 用户来源
|
||||||
|
Source *SOURCE `json:"source"`
|
||||||
|
// Uids
|
||||||
|
UserIds []uint64 `json:"user_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *QueryTokenRequest) SetActive(v bool) *QueryTokenRequest {
|
||||||
|
r.Active = &v
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *QueryTokenRequest) SetSource(v SOURCE) *QueryTokenRequest {
|
||||||
|
r.Source = &v
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *QueryTokenRequest) AddUserId(uids ...uint64) *QueryTokenRequest {
|
||||||
|
r.UserIds = append(r.UserIds, uids...)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIssueTokenRequest() *IssueTokenRequest {
|
||||||
|
return &IssueTokenRequest{
|
||||||
|
Parameter: make(IssueParameter),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 用户会给我们 用户的身份凭证,用于换取Token
|
// 用户会给我们 用户的身份凭证,用于换取Token
|
||||||
type IssueTokenRequest struct {
|
type IssueTokenRequest struct {
|
||||||
// 端类型
|
// 端类型
|
||||||
@ -25,6 +91,16 @@ type IssueTokenRequest struct {
|
|||||||
Parameter IssueParameter `json:"parameter"`
|
Parameter IssueParameter `json:"parameter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *IssueTokenRequest) IssueByPassword(username, password string) {
|
||||||
|
i.Issuer = ISSUER_PASSWORD
|
||||||
|
i.Parameter.SetUsername(username)
|
||||||
|
i.Parameter.SetPassword(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIssueParameter() IssueParameter {
|
||||||
|
return make(IssueParameter)
|
||||||
|
}
|
||||||
|
|
||||||
type IssueParameter map[string]any
|
type IssueParameter map[string]any
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -39,12 +115,14 @@ func (p IssueParameter) Password() string {
|
|||||||
return GetIssueParameterValue[string](p, "password")
|
return GetIssueParameterValue[string](p, "password")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p IssueParameter) SetUsername(v string) {
|
func (p IssueParameter) SetUsername(v string) IssueParameter {
|
||||||
p["username"] = v
|
p["username"] = v
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p IssueParameter) SetPassword(v string) {
|
func (p IssueParameter) SetPassword(v string) IssueParameter {
|
||||||
p["password"] = v
|
p["password"] = v
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -59,8 +137,14 @@ func (p IssueParameter) ExpireTTL() time.Duration {
|
|||||||
return time.Second * time.Duration(GetIssueParameterValue[int64](p, "expired_ttl"))
|
return time.Second * time.Duration(GetIssueParameterValue[int64](p, "expired_ttl"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p IssueParameter) SetAccessToken(v string) {
|
func (p IssueParameter) SetAccessToken(v string) IssueParameter {
|
||||||
p["access_token"] = v
|
p["access_token"] = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p IssueParameter) SetExpireTTL(v int64) IssueParameter {
|
||||||
|
p["expired_ttl"] = v
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRevolkTokenRequest(at, rk string) *RevolkTokenRequest {
|
func NewRevolkTokenRequest(at, rk string) *RevolkTokenRequest {
|
||||||
|
44
devcloud/mcenter/apps/token/issuer.go
Normal file
44
devcloud/mcenter/apps/token/issuer.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package token
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math/rand/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ISSUER_LDAP = "ldap"
|
||||||
|
ISSUER_FEISHU = "feishu"
|
||||||
|
ISSUER_PASSWORD = "password"
|
||||||
|
ISSUER_PRIVATE_TOKEN = "private_token"
|
||||||
|
)
|
||||||
|
|
||||||
|
var issuers = map[string]Issuer{}
|
||||||
|
|
||||||
|
func RegistryIssuer(name string, p Issuer) {
|
||||||
|
issuers[name] = p
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetIssuer(name string) Issuer {
|
||||||
|
fmt.Println(issuers)
|
||||||
|
return issuers[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
type Issuer interface {
|
||||||
|
IssueToken(context.Context, IssueParameter) (*Token, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
charlist = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MakeBearer https://tools.ietf.org/html/rfc6750#section-2.1
|
||||||
|
// b64token = 1*( ALPHA / DIGIT /"-" / "." / "_" / "~" / "+" / "/" ) *"="
|
||||||
|
func MakeBearer(lenth int) string {
|
||||||
|
t := make([]byte, 0)
|
||||||
|
for range lenth {
|
||||||
|
rn := rand.IntN(len(charlist))
|
||||||
|
t = append(t, charlist[rn])
|
||||||
|
}
|
||||||
|
return string(t)
|
||||||
|
}
|
12
devcloud/mcenter/apps/token/issuer_test.go
Normal file
12
devcloud/mcenter/apps/token/issuer_test.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package token_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMakeBearer(t *testing.T) {
|
||||||
|
t.Log(token.MakeBearer(24))
|
||||||
|
t.Log(token.MakeBearer(24))
|
||||||
|
}
|
67
devcloud/mcenter/apps/token/issuers/password/issuer.go
Normal file
67
devcloud/mcenter/apps/token/issuers/password/issuer.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package password
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/user"
|
||||||
|
"github.com/infraboard/mcube/v2/exception"
|
||||||
|
"github.com/infraboard/mcube/v2/ioc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ioc.Config().Registry(&PasswordTokenIssuer{
|
||||||
|
ExpiredTTLSecond: 1 * 60 * 60,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PasswordTokenIssuer struct {
|
||||||
|
ioc.ObjectImpl
|
||||||
|
// 通过用户模块 来判断用户凭证是否正确
|
||||||
|
user user.Service
|
||||||
|
|
||||||
|
// Password颁发的Token 过去时间由系统配置, 不允许用户自己设置
|
||||||
|
ExpiredTTLSecond int `json:"expired_ttl_second" toml:"expired_ttl_second" yaml:"expired_ttl_second" env:"EXPIRED_TTL_SECOND"`
|
||||||
|
|
||||||
|
expiredDuration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PasswordTokenIssuer) Name() string {
|
||||||
|
return "password_token_issuer"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PasswordTokenIssuer) Init() error {
|
||||||
|
p.user = user.GetService()
|
||||||
|
p.expiredDuration = time.Duration(p.ExpiredTTLSecond) * time.Second
|
||||||
|
|
||||||
|
token.RegistryIssuer(token.ISSUER_PASSWORD, p)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PasswordTokenIssuer) IssueToken(ctx context.Context, parameter token.IssueParameter) (*token.Token, error) {
|
||||||
|
// 1. 查询用户
|
||||||
|
uReq := user.NewDescribeUserRequestByUserName(parameter.Username())
|
||||||
|
u, err := p.user.DescribeUser(ctx, uReq)
|
||||||
|
if err != nil {
|
||||||
|
if exception.IsNotFoundError(err) {
|
||||||
|
return nil, exception.NewUnauthorized("%s", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 比对密码
|
||||||
|
err = u.CheckPassword(parameter.Password())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 颁发token
|
||||||
|
tk := token.NewToken()
|
||||||
|
tk.UserId = u.Id
|
||||||
|
tk.UserName = u.UserName
|
||||||
|
tk.IsAdmin = u.IsAdmin
|
||||||
|
|
||||||
|
tk.SetExpiredAtByDuration(p.expiredDuration, 4)
|
||||||
|
return tk, nil
|
||||||
|
}
|
22
devcloud/mcenter/apps/token/issuers/password/issuer_test.go
Normal file
22
devcloud/mcenter/apps/token/issuers/password/issuer_test.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package password_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPasswordIssuer(t *testing.T) {
|
||||||
|
issuer := token.GetIssuer(token.ISSUER_PASSWORD)
|
||||||
|
tk, err := issuer.IssueToken(context.Background(), token.NewIssueParameter().SetUsername("admin").SetPassword("123456"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(tk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
test.DevelopmentSet()
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package privatetoken_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPasswordIssuer(t *testing.T) {
|
||||||
|
issuer := token.GetIssuer(token.ISSUER_PRIVATE_TOKEN)
|
||||||
|
tk, err := issuer.IssueToken(context.Background(), token.NewIssueParameter().SetAccessToken("LccvuTwISJRheu8PtqAFTJBy").SetExpireTTL(24*60*60))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(tk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
test.DevelopmentSet()
|
||||||
|
}
|
67
devcloud/mcenter/apps/token/issuers/private_token/issuer.go
Normal file
67
devcloud/mcenter/apps/token/issuers/private_token/issuer.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package privatetoken
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/user"
|
||||||
|
"github.com/infraboard/mcube/v2/exception"
|
||||||
|
"github.com/infraboard/mcube/v2/ioc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ioc.Config().Registry(&PrivateTokenIssuer{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrivateTokenIssuer struct {
|
||||||
|
ioc.ObjectImpl
|
||||||
|
|
||||||
|
user user.Service
|
||||||
|
token token.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrivateTokenIssuer) Name() string {
|
||||||
|
return "private_token_issuer"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrivateTokenIssuer) Init() error {
|
||||||
|
p.user = user.GetService()
|
||||||
|
p.token = token.GetService()
|
||||||
|
|
||||||
|
token.RegistryIssuer(token.ISSUER_PRIVATE_TOKEN, p)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrivateTokenIssuer) IssueToken(ctx context.Context, parameter token.IssueParameter) (*token.Token, error) {
|
||||||
|
// 1. 校验Token合法
|
||||||
|
oldTk, err := p.token.ValiateToken(ctx, token.NewValiateTokenRequest(parameter.AccessToken()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 查询用户
|
||||||
|
uReq := user.NewDescribeUserRequestById(oldTk.UserIdString())
|
||||||
|
u, err := p.user.DescribeUser(ctx, uReq)
|
||||||
|
if err != nil {
|
||||||
|
if exception.IsNotFoundError(err) {
|
||||||
|
return nil, exception.NewUnauthorized("%s", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !u.EnabledApi {
|
||||||
|
return nil, exception.NewPermissionDeny("未开启接口登录")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 颁发token
|
||||||
|
tk := token.NewToken()
|
||||||
|
tk.UserId = u.Id
|
||||||
|
tk.UserName = u.UserName
|
||||||
|
tk.IsAdmin = u.IsAdmin
|
||||||
|
|
||||||
|
expiredTTL := parameter.ExpireTTL()
|
||||||
|
if expiredTTL > 0 {
|
||||||
|
tk.SetExpiredAtByDuration(expiredTTL, 4)
|
||||||
|
}
|
||||||
|
return tk, nil
|
||||||
|
}
|
6
devcloud/mcenter/apps/token/issuers/registry.go
Normal file
6
devcloud/mcenter/apps/token/issuers/registry.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package issuers
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/issuers/password"
|
||||||
|
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/issuers/private_token"
|
||||||
|
)
|
@ -1,13 +1,65 @@
|
|||||||
package token
|
package token
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/infraboard/mcube/v2/exception"
|
"github.com/infraboard/mcube/v2/exception"
|
||||||
"github.com/infraboard/mcube/v2/tools/pretty"
|
"github.com/infraboard/mcube/v2/tools/pretty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func GetAccessTokenFromHTTP(r *http.Request) string {
|
||||||
|
// 先从Token中获取
|
||||||
|
tk := r.Header.Get(ACCESS_TOKEN_HEADER_NAME)
|
||||||
|
|
||||||
|
// 1. 获取Token
|
||||||
|
if tk == "" {
|
||||||
|
cookie, err := r.Cookie(ACCESS_TOKEN_COOKIE_NAME)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
tk, _ = url.QueryUnescape(cookie.Value)
|
||||||
|
} else {
|
||||||
|
// 处理 带格式: Bearer <Your API key>
|
||||||
|
ft := strings.Split(tk, " ")
|
||||||
|
if len(ft) > 1 {
|
||||||
|
tk = ft[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTokenFromCtx(ctx context.Context) *Token {
|
||||||
|
if v := ctx.Value(CTX_TOKEN_KEY); v != nil {
|
||||||
|
return v.(*Token)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRefreshTokenFromHTTP(r *http.Request) string {
|
||||||
|
// 先从Token中获取
|
||||||
|
tk := r.Header.Get(REFRESH_TOKEN_HEADER_NAME)
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewToken() *Token {
|
||||||
|
tk := &Token{
|
||||||
|
// 生产一个UUID的字符串
|
||||||
|
AccessToken: MakeBearer(24),
|
||||||
|
RefreshToken: MakeBearer(32),
|
||||||
|
IssueAt: time.Now(),
|
||||||
|
Status: NewStatus(),
|
||||||
|
Extras: map[string]string{},
|
||||||
|
Scope: map[string]string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
|
||||||
// 需要存储到数据库里面的对象(表)
|
// 需要存储到数据库里面的对象(表)
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
|
@ -43,4 +43,25 @@ password + 随机字符串(salt) --> (salt)hash它也是随机
|
|||||||
|
|
||||||
输入: password + 随机字符串(salt: 原来hash中的sal部分) == hash它也是随机(数据库)
|
输入: password + 随机字符串(salt: 原来hash中的sal部分) == hash它也是随机(数据库)
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (req *CreateUserRequest) PasswordHash() {
|
||||||
|
if req.isHashed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||||
|
req.Password = string(b)
|
||||||
|
req.isHashed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断该用户的密码是否正确
|
||||||
|
func (u *User) CheckPassword(password string) error {
|
||||||
|
// u.Password hash过后的只
|
||||||
|
// (password 原始值 + hash值中提区salt)
|
||||||
|
return bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
1
devcloud/mcenter/apps/user/api/api.go
Normal file
1
devcloud/mcenter/apps/user/api/api.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package api
|
@ -1 +1,18 @@
|
|||||||
package impl_test
|
package impl_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/user"
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
impl user.Service
|
||||||
|
ctx = context.Background()
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
test.DevelopmentSet()
|
||||||
|
impl = user.GetService()
|
||||||
|
}
|
||||||
|
@ -1 +1,91 @@
|
|||||||
package impl_test
|
package impl_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestQueryUser(t *testing.T) {
|
||||||
|
req := user.NewQueryUserRequest()
|
||||||
|
set, err := impl.QueryUser(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateAdminUser(t *testing.T) {
|
||||||
|
req := user.NewCreateUserRequest()
|
||||||
|
req.UserName = "admin"
|
||||||
|
req.Password = "123456"
|
||||||
|
req.EnabledApi = true
|
||||||
|
req.IsAdmin = true
|
||||||
|
u, err := impl.CreateUser(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateAuthor2(t *testing.T) {
|
||||||
|
req := user.NewCreateUserRequest()
|
||||||
|
req.UserName = "张三"
|
||||||
|
req.Password = "123456"
|
||||||
|
req.EnabledApi = true
|
||||||
|
u, err := impl.CreateUser(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateGuestUser(t *testing.T) {
|
||||||
|
req := user.NewCreateUserRequest()
|
||||||
|
req.UserName = "guest"
|
||||||
|
req.Password = "123456"
|
||||||
|
req.EnabledApi = true
|
||||||
|
u, err := impl.CreateUser(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteUser(t *testing.T) {
|
||||||
|
_, err := impl.DeleteUser(ctx, &user.DeleteUserRequest{
|
||||||
|
Id: "9",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDescribeUserRequestById(t *testing.T) {
|
||||||
|
req := user.NewDescribeUserRequestById("2")
|
||||||
|
ins, err := impl.DescribeUser(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(ins)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SELECT * FROM `users` WHERE username = 'admin' ORDER BY `users`.`id` LIMIT 1
|
||||||
|
func TestDescribeUserRequestByName(t *testing.T) {
|
||||||
|
req := user.NewDescribeUserRequestByUserName("admin")
|
||||||
|
ins, err := impl.DescribeUser(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(ins)
|
||||||
|
|
||||||
|
err = ins.CheckPassword("1234561")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserJson(t *testing.T) {
|
||||||
|
u := user.NewUser(user.NewCreateUserRequest())
|
||||||
|
t.Log(u)
|
||||||
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/infraboard/mcube/v2/exception"
|
||||||
|
"github.com/infraboard/mcube/v2/tools/pretty"
|
||||||
"github.com/infraboard/modules/iam/apps"
|
"github.com/infraboard/modules/iam/apps"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
@ -30,13 +31,18 @@ type User struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) String() string {
|
func (u *User) String() string {
|
||||||
dj, _ := json.Marshal(u)
|
return pretty.ToJSON(u)
|
||||||
return string(dj)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断该用户的密码是否正确
|
// 判断该用户的密码是否正确
|
||||||
func (u *User) CheckPassword(password string) error {
|
func (u *User) CheckPassword(password string) error {
|
||||||
return bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
|
// u.Password hash过后的只
|
||||||
|
// (password 原始值 + hash值中提区salt)
|
||||||
|
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
|
||||||
|
if err != nil {
|
||||||
|
return exception.NewUnauthorized("用户名或者密码对正确")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 声明你这个对象存储在users表里面
|
// 声明你这个对象存储在users表里面
|
||||||
|
17
devcloud/mcenter/test/set_up.go
Normal file
17
devcloud/mcenter/test/set_up.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/infraboard/mcube/v2/ioc"
|
||||||
|
// 要注册哪些对象, Book, Comment
|
||||||
|
|
||||||
|
// 加载的业务对象
|
||||||
|
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DevelopmentSet() {
|
||||||
|
// import 后自动执行的逻辑
|
||||||
|
// 工具对象的初始化, 需要的是绝对路径
|
||||||
|
ioc.DevelopmentSetupWithPath(os.Getenv("CONFIG_PATH"))
|
||||||
|
}
|
4
go.mod
4
go.mod
@ -6,8 +6,11 @@ require (
|
|||||||
github.com/caarlos0/env/v6 v6.10.1
|
github.com/caarlos0/env/v6 v6.10.1
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/infraboard/mcube/v2 v2.0.59
|
github.com/infraboard/mcube/v2 v2.0.59
|
||||||
|
github.com/infraboard/modules v0.0.12
|
||||||
github.com/rs/zerolog v1.34.0
|
github.com/rs/zerolog v1.34.0
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
|
golang.org/x/crypto v0.38.0
|
||||||
|
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
gorm.io/driver/mysql v1.5.7
|
gorm.io/driver/mysql v1.5.7
|
||||||
gorm.io/gorm v1.26.0
|
gorm.io/gorm v1.26.0
|
||||||
@ -83,7 +86,6 @@ require (
|
|||||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||||
golang.org/x/arch v0.15.0 // indirect
|
golang.org/x/arch v0.15.0 // indirect
|
||||||
golang.org/x/crypto v0.38.0 // indirect
|
|
||||||
golang.org/x/net v0.38.0 // indirect
|
golang.org/x/net v0.38.0 // indirect
|
||||||
golang.org/x/sync v0.14.0 // indirect
|
golang.org/x/sync v0.14.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
|
4
go.sum
4
go.sum
@ -75,6 +75,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
|
|||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/infraboard/mcube/v2 v2.0.59 h1:NONiCPjN6xlbGCJx8+e+ZYZfXV58JByEMlzQ6ZZ+pXk=
|
github.com/infraboard/mcube/v2 v2.0.59 h1:NONiCPjN6xlbGCJx8+e+ZYZfXV58JByEMlzQ6ZZ+pXk=
|
||||||
github.com/infraboard/mcube/v2 v2.0.59/go.mod h1:TbYs8cnD8Cg19sTdU0D+vqWAN+LzoxhMYWmAC2pfJkQ=
|
github.com/infraboard/mcube/v2 v2.0.59/go.mod h1:TbYs8cnD8Cg19sTdU0D+vqWAN+LzoxhMYWmAC2pfJkQ=
|
||||||
|
github.com/infraboard/modules v0.0.12 h1:vQqm+JwzmhL+hcD9SV+WVlp9ecInc7NsbGahcTmJ0Wk=
|
||||||
|
github.com/infraboard/modules v0.0.12/go.mod h1:NdgdH/NoeqibJmFPn9th+tisMuR862/crbXeH4FPMaU=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
@ -197,6 +199,8 @@ golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
|
|||||||
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||||
|
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||||
|
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user