diff --git a/vblog/apps/token/impl/impl.go b/vblog/apps/token/impl/impl.go index 773ede9..2950dbe 100644 --- a/vblog/apps/token/impl/impl.go +++ b/vblog/apps/token/impl/impl.go @@ -2,20 +2,53 @@ package impl import ( "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/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就是刚才定义的接口 // 怎么才能判断这个结构体没有实现这个接口 type TokenServiceImpl struct { + // user service + user user.AdminService } // IssueToken implements token.Service. -func (t *TokenServiceImpl) IssueToken(context.Context, *token.IssueTokenRequest) (*token.Token, error) { - panic("unimplemented") +func (t *TokenServiceImpl) IssueToken(ctx context.Context, in *token.IssueTokenRequest) (*token.Token, error) { + 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. @@ -24,6 +57,17 @@ func (t *TokenServiceImpl) RevolkToken(context.Context, *token.RevolkTokenReques } // ValidateToken implements token.Service. -func (t *TokenServiceImpl) ValidateToken(context.Context, *token.ValidateTokenRequest) (*token.Token, error) { - panic("unimplemented") +// 1. 这个令牌是不是我们发 +// 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 } diff --git a/vblog/apps/token/impl_test.go b/vblog/apps/token/impl_test.go new file mode 100644 index 0000000..6d1e7db --- /dev/null +++ b/vblog/apps/token/impl_test.go @@ -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) +} diff --git a/vblog/apps/token/interface.go b/vblog/apps/token/interface.go index e7fa75b..b9152d7 100644 --- a/vblog/apps/token/interface.go +++ b/vblog/apps/token/interface.go @@ -1,6 +1,10 @@ package token -import "context" +import ( + "context" + + "github.com/infraboard/mcube/v2/ioc/config/validator" +) // 业务域 type Service interface { @@ -23,19 +27,36 @@ type RevolkTokenRequest struct { RefreshToken string `json:"refresh_token"` } +func NewIssueTokenRequest(username, password string) *IssueTokenRequest { + return &IssueTokenRequest{ + Username: username, + Password: password, + } +} + type IssueTokenRequest struct { - Username string `json:"username"` - Password string `json:"password"` + Username string `json:"username" validate:"required"` + Password string `json:"password" validate:"required"` // 记住我, Token可能1天过期, 过去时间调整为7天 RememberMe bool `json:"remember_me"` } +func (r *IssueTokenRequest) Validate() error { + return validator.Validate(r) +} + // 内部 type InnterService interface { // 令牌校验 ValidateToken(context.Context, *ValidateTokenRequest) (*Token, error) } +func NewValidateTokenRequest(at string) *ValidateTokenRequest { + return &ValidateTokenRequest{ + AccessToken: at, + } +} + type ValidateTokenRequest struct { // 访问令牌 AccessToken string `json:"access_token"` diff --git a/vblog/apps/token/model.go b/vblog/apps/token/model.go index bdc49c2..7463b3d 100644 --- a/vblog/apps/token/model.go +++ b/vblog/apps/token/model.go @@ -1,6 +1,25 @@ 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 { @@ -24,6 +43,35 @@ type Token struct { RefUserName string `json:"ref_user_name" gorm:"-"` } +func (r *Token) String() string { + return pretty.ToJSON(r) +} + func (t *Token) TableName() string { 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 +} diff --git a/vblog/docs/token_expired.drawio b/vblog/docs/token_expired.drawio new file mode 100644 index 0000000..254be50 --- /dev/null +++ b/vblog/docs/token_expired.drawio @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vblog/docs/transation.drawio b/vblog/docs/transation.drawio index d5a4a23..0aa8c70 100644 --- a/vblog/docs/transation.drawio +++ b/vblog/docs/transation.drawio @@ -1,6 +1,6 @@ - + @@ -31,6 +31,9 @@ + + +