biblio/auth/jwt/manager.go

186 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}