biblio/auth/jwt/manager.go

186 lines
4.4 KiB
Go
Raw Normal View History

2022-10-04 21:09:31 +08:00
package jwt
import (
"errors"
"fmt"
"strconv"
"time"
"biblio.bing89.com/auth"
"biblio.bing89.com/auth/jwt/store"
"biblio.bing89.com/code"
"biblio.bing89.com/utils/ginhelper"
"github.com/gin-gonic/gin"
)
type manager struct {
store store.ClaimStore
term time.Duration
signedSecret string
}
func NewManager(store store.ClaimStore, term time.Duration, secret string) auth.AuthManager {
return &manager{
store: store,
term: term,
signedSecret: secret,
}
}
func (m *manager) Login(identifyFunc auth.IdentifyFunc, data *auth.AuthenticationRequest) (*auth.AuthenticationResponse, error) {
// if _, err := m.IsLogined(data.Username); err == nil {
// return nil, fmt.Errorf("user is already logined")
// }
resp, err := identifyFunc(data)
if err != nil {
return nil, err
}
tokenClaim, err := m.IsLogined(resp.UID)
if err == nil && tokenClaim != nil { //已登录
//刷新过期时间
err = m.Expire(tokenClaim.UserID, m.term)
tokenString, err1 := tokenClaim.String(m.signedSecret)
if err != nil || tokenString == "" {
return nil, fmt.Errorf("get token string failed:%v", err1)
}
resp.Token = tokenString
if err != nil {
return resp, fmt.Errorf("update token expiration failed:%v", err)
}
return resp, nil
}
//未登录
tokenClaim, err = NewTokenClaim(data.Username, m.term, resp.UID)
if err != nil {
return nil, fmt.Errorf("generate token failed:%v", err)
}
tokenString, err := tokenClaim.String(m.signedSecret)
if err != nil {
return nil, fmt.Errorf("get token string failed:%v", err)
}
//存储登录状态
err = m.StoreLoginState(resp.UID, tokenClaim)
if err != nil {
return nil, fmt.Errorf("StoreLoginState failed:%v", err)
}
resp.Token = tokenString
return resp, nil
}
func (m *manager) Logout(data *auth.AuthenticationRequest) error {
//解析token确认有logout权限
claim, err := ParseTokenClaim(data.Token, m.signedSecret)
if err != nil {
return err
}
if c, err := m.IsLogined(claim.UserID); err == nil {
err = m.DeleteToken(c)
return err
}
return fmt.Errorf("user not login or token invalid")
}
func (m *manager) Refresh(data *auth.AuthenticationRequest) (string, error) {
cliam, err := ParseTokenClaim(data.Token, m.signedSecret)
if err != nil {
return "", err
}
//已过期
if cliam.Valid() != nil {
return "", fmt.Errorf("cannot use an invalid token to refresh: %v", err)
}
//找不到登陆状态
if _, err := m.IsLogined(cliam.UserID); err != nil {
return "", fmt.Errorf("token invalid, user maybe not login")
}
//重置有效期
cliam.ExpiresAt = time.Now().Add(m.term).Unix()
err = m.StoreLoginState(cliam.UserID, cliam)
if err != nil {
return "", err
}
tokenStr, err := cliam.String(m.signedSecret)
return tokenStr, err
}
//中间件
//ERR_NOT_AUTH
func (m *manager) MiddleWare() gin.HandlerFunc {
return func(c *gin.Context) {
tokenString := c.GetHeader("Token")
if tokenString == "" {
ginhelper.Error(c, code.CodeNoAuth, errors.New("token is empty"))
return
}
tokenClaim, err := ParseTokenClaim(tokenString, m.signedSecret)
if err != nil {
ginhelper.Error(c, code.CodeNoAuth,err)
return
}
if err := m.TokenValid(tokenClaim.UserID, tokenString); err != nil {
ginhelper.Error(c, code.CodeNoAuth, err)
return
}
c.Set("uid", strconv.FormatUint(tokenClaim.UserID, 10))
c.Next()
}
}
//存储登录状态
func (m *manager) StoreLoginState(user uint64, claim *TokenClaim) error {
//储存登录状态
str, err := claim.String(m.signedSecret)
if err != nil {
return err
}
return m.store.Set(user, str, m.term)
}
//获取token
//是否已登录
func (m *manager) IsLogined(user uint64) (*TokenClaim, error) {
token, err := m.store.Get(user)
if err != nil {
return nil, err
}
return ParseTokenClaim(token, m.signedSecret)
}
//删除token
func (m *manager) DeleteToken(claim *TokenClaim) error {
return m.store.Delete(claim.UserID)
}
func (m *manager) Close() error {
return m.store.Close()
}
func (m *manager) TokenValid(uid uint64, token string) error {
t, err := m.store.Get(uid)
if err != nil {
return fmt.Errorf("get token failed: %v", err)
}
if t != token {
return fmt.Errorf("token is invalid")
}
return nil
}
func (m *manager) ExpireAt(uid uint64, t time.Time) error {
err := m.store.ExpireAt(uid, t)
return err
}
func (m *manager) Expire(uid uint64, t time.Duration) error {
err := m.store.Expire(uid, t)
return err
}
func (m *manager) TTL(uid uint64) (time.Duration, error) {
return m.store.TTL(uid)
}