补充令牌颁发
This commit is contained in:
parent
614450a5e6
commit
0f1c51f1b2
@ -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
|
||||||
}
|
}
|
||||||
|
37
vblog/apps/token/impl_test.go
Normal file
37
vblog/apps/token/impl_test.go
Normal 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)
|
||||||
|
}
|
@ -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"`
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
22
vblog/docs/token_expired.drawio
Normal file
22
vblog/docs/token_expired.drawio
Normal 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>
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user