补充令牌颁发

This commit is contained in:
yumaojun03 2024-12-08 11:59:36 +08:00
parent 614450a5e6
commit 0f1c51f1b2
6 changed files with 185 additions and 10 deletions

View File

@ -2,20 +2,53 @@ package impl
import ( import (
"context" "context"
"fmt"
"github.com/infraboard/mcube/v2/exception"
"github.com/infraboard/mcube/v2/ioc/config/datasource"
"gitlab.com/go-course-project/go17/vblog/apps/token" "gitlab.com/go-course-project/go17/vblog/apps/token"
"gitlab.com/go-course-project/go17/vblog/apps/user"
"gitlab.com/go-course-project/go17/vblog/apps/user/impl"
) )
var TokenService token.Service = &TokenServiceImpl{} var TokenService token.Service = &TokenServiceImpl{
user: impl.UserService,
}
// 定义一个struct, 用于实现 UserService就是刚才定义的接口 // 定义一个struct, 用于实现 UserService就是刚才定义的接口
// 怎么才能判断这个结构体没有实现这个接口 // 怎么才能判断这个结构体没有实现这个接口
type TokenServiceImpl struct { type TokenServiceImpl struct {
// user service
user user.AdminService
} }
// IssueToken implements token.Service. // IssueToken implements token.Service.
func (t *TokenServiceImpl) IssueToken(context.Context, *token.IssueTokenRequest) (*token.Token, error) { func (t *TokenServiceImpl) IssueToken(ctx context.Context, in *token.IssueTokenRequest) (*token.Token, error) {
panic("unimplemented") if err := in.Validate(); err != nil {
return nil, exception.NewBadRequest("参数校验异常: %s", err)
}
// 1. 查询用户
u, err := t.user.DescribeUser(ctx, &user.DescribeUserRequest{
DescribeBy: user.DESCRIBE_BY_USERNAME,
Value: in.Username,
})
if err != nil {
return nil, err
}
// 2. 比对密码
if err := u.CheckPassword(in.Password); err != nil {
return nil, err
}
// 3. 颁发Token
tk := token.NewToken(fmt.Sprintf("%d", u.Id)).SetRefUserName(u.Username)
if err := datasource.DBFromCtx(ctx).Create(tk).Error; err != nil {
return nil, err
}
return tk, nil
} }
// RevolkToken implements token.Service. // RevolkToken implements token.Service.
@ -24,6 +57,17 @@ func (t *TokenServiceImpl) RevolkToken(context.Context, *token.RevolkTokenReques
} }
// ValidateToken implements token.Service. // ValidateToken implements token.Service.
func (t *TokenServiceImpl) ValidateToken(context.Context, *token.ValidateTokenRequest) (*token.Token, error) { // 1. 这个令牌是不是我们发
panic("unimplemented") // 2. 这个令牌有没有过期
func (t *TokenServiceImpl) ValidateToken(ctx context.Context, in *token.ValidateTokenRequest) (*token.Token, error) {
tk := &token.Token{}
if err := datasource.DBFromCtx(ctx).Where("access_token = ?", in.AccessToken).Take(tk).Error; err != nil {
return nil, err
}
if err := tk.IsAccessTokenExpired(); err != nil {
return nil, err
}
return tk, nil
} }

View File

@ -0,0 +1,37 @@
package token_test
import (
"context"
"testing"
"gitlab.com/go-course-project/go17/vblog/apps/token"
"gitlab.com/go-course-project/go17/vblog/apps/token/impl"
)
var (
ctx = context.Background()
)
// 我要测试的对象是什么?, 这个服务的具体实现
// Service的具体实现现在还没实现
// $2a$10$yHVSVuyIpTrQxwiuZUwSMuaJFsnd4YBd6hgA.31xNzuyTu4voD/QW
// $2a$10$fe0lsMhM15i4cjHmWudroOOIIBR27Nb7vwrigwK.9PhWdFld44Yze
// $2a$10$RoR0qK37vfc7pddPV0mpU.nN15Lv8745A40MkCJLe47Q00Ag83Qru
// https://gitee.com/infraboard/go-course/blob/master/day09/go-hash.md#bcrypt
func TestIssueToken(t *testing.T) {
req := token.NewIssueTokenRequest("admin", "123456")
ins, err := impl.TokenService.IssueToken(ctx, req)
if err != nil {
t.Fatal(err)
}
t.Log(ins)
}
func TestValidateToken(t *testing.T) {
req := token.NewValidateTokenRequest("51bf49f5-12a2-406a-baf8-3f99d985b41a")
ins, err := impl.TokenService.ValidateToken(ctx, req)
if err != nil {
t.Fatal(err)
}
t.Log(ins)
}

View File

@ -1,6 +1,10 @@
package token package token
import "context" import (
"context"
"github.com/infraboard/mcube/v2/ioc/config/validator"
)
// 业务域 // 业务域
type Service interface { type Service interface {
@ -23,19 +27,36 @@ type RevolkTokenRequest struct {
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
} }
func NewIssueTokenRequest(username, password string) *IssueTokenRequest {
return &IssueTokenRequest{
Username: username,
Password: password,
}
}
type IssueTokenRequest struct { type IssueTokenRequest struct {
Username string `json:"username"` Username string `json:"username" validate:"required"`
Password string `json:"password"` Password string `json:"password" validate:"required"`
// 记住我, Token可能1天过期, 过去时间调整为7天 // 记住我, Token可能1天过期, 过去时间调整为7天
RememberMe bool `json:"remember_me"` RememberMe bool `json:"remember_me"`
} }
func (r *IssueTokenRequest) Validate() error {
return validator.Validate(r)
}
// 内部 // 内部
type InnterService interface { type InnterService interface {
// 令牌校验 // 令牌校验
ValidateToken(context.Context, *ValidateTokenRequest) (*Token, error) ValidateToken(context.Context, *ValidateTokenRequest) (*Token, error)
} }
func NewValidateTokenRequest(at string) *ValidateTokenRequest {
return &ValidateTokenRequest{
AccessToken: at,
}
}
type ValidateTokenRequest struct { type ValidateTokenRequest struct {
// 访问令牌 // 访问令牌
AccessToken string `json:"access_token"` AccessToken string `json:"access_token"`

View File

@ -1,6 +1,25 @@
package token package token
import "time" import (
"time"
"github.com/google/uuid"
"github.com/infraboard/mcube/v2/exception"
"github.com/infraboard/mcube/v2/tools/pretty"
)
func NewToken(refUserId string) *Token {
aExpiredAt := time.Now().AddDate(0, 0, 1)
rExpiredAt := time.Now().AddDate(0, 0, 7)
return &Token{
RefUserId: refUserId,
AccessToken: uuid.NewString(),
AccessTokenExpireAt: &aExpiredAt,
IssueAt: time.Now(),
RefreshToken: uuid.NewString(),
RefreshTokenExpireAt: &rExpiredAt,
}
}
// 用户的身份令牌 // 用户的身份令牌
type Token struct { type Token struct {
@ -24,6 +43,35 @@ type Token struct {
RefUserName string `json:"ref_user_name" gorm:"-"` RefUserName string `json:"ref_user_name" gorm:"-"`
} }
func (r *Token) String() string {
return pretty.ToJSON(r)
}
func (t *Token) TableName() string { func (t *Token) TableName() string {
return "tokens" return "tokens"
} }
func (r *Token) IsAccessTokenExpired() error {
if r.AccessTokenExpireAt == nil {
return nil
}
if time.Now().After(*r.AccessTokenExpireAt) {
return exception.NewAccessTokenExpired("%s 已过期", r.AccessToken)
}
return nil
}
func (r *Token) IsRefreshTokenExpired() error {
if r.RefreshTokenExpireAt == nil {
return nil
}
if time.Now().After(*r.RefreshTokenExpireAt) {
return exception.NewAccessTokenExpired("%s 已过期", r.RefreshTokenExpireAt)
}
return nil
}
func (t *Token) SetRefUserName(refUserName string) *Token {
t.RefUserName = refUserName
return t
}

View File

@ -0,0 +1,22 @@
<mxfile host="65bd71144e">
<diagram id="9AKkvTdDAUoQfhV7wM2S" name="第 1 页">
<mxGraphModel dx="884" dy="260" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="" style="shape=flexArrow;endArrow=classic;html=1;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="130" y="150" as="sourcePoint"/>
<mxPoint x="620" y="150" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="3" value="Now()" style="rhombus;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="430" y="190" width="80" height="80" as="geometry"/>
</mxCell>
<mxCell id="4" value="ExpiredAt" style="rhombus;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="280" y="190" width="80" height="80" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -1,6 +1,6 @@
<mxfile host="65bd71144e"> <mxfile host="65bd71144e">
<diagram id="u9_ob_htgBlG7QGFx-zW" name="第 1 页"> <diagram id="u9_ob_htgBlG7QGFx-zW" name="第 1 页">
<mxGraphModel dx="1147" dy="647" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> <mxGraphModel dx="884" dy="647" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root> <root>
<mxCell id="0"/> <mxCell id="0"/>
<mxCell id="1" parent="0"/> <mxCell id="1" parent="0"/>
@ -31,6 +31,9 @@
<mxCell id="9" value="C" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1"> <mxCell id="9" value="C" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="430" y="250" width="30" height="30" as="geometry"/> <mxGeometry x="430" y="250" width="30" height="30" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="12" value="请求之间传递的东西: 请求的上下文 ctx(tx)" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="80" y="585" width="220" height="30" as="geometry"/>
</mxCell>
</root> </root>
</mxGraphModel> </mxGraphModel>
</diagram> </diagram>