232 lines
6.1 KiB
Go
232 lines
6.1 KiB
Go
package jwt
|
||
|
||
import (
|
||
"fmt"
|
||
"net/http"
|
||
"time"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
gojwt "github.com/golang-jwt/jwt"
|
||
"github.com/ycyxuehan/zelda/apiserver/auth/api"
|
||
)
|
||
|
||
type TokenClaim struct {
|
||
Username string `json:"username"`
|
||
Group string `json:"group"`
|
||
gojwt.StandardClaims
|
||
}
|
||
|
||
func (t *TokenClaim) Expired() bool {
|
||
return t.ExpiresAt <= time.Now().Unix()
|
||
}
|
||
|
||
type tokenCache struct {
|
||
kubernetesToken string
|
||
claim *TokenClaim
|
||
cert string
|
||
}
|
||
|
||
type manager struct {
|
||
tokenStore map[string]tokenCache
|
||
secret string
|
||
term time.Duration
|
||
}
|
||
|
||
func NewManager(secret string, term time.Duration) api.AuthManager {
|
||
m := manager{
|
||
tokenStore: make(map[string]tokenCache),
|
||
secret: secret,
|
||
term: term,
|
||
}
|
||
return &m
|
||
}
|
||
|
||
//登录
|
||
func (m *manager) Login(f api.IdentifyFunc, data *api.AuthentitionRequest) *api.AuthentitionResponse {
|
||
if _, ok := m.IsLogined(data.Username); ok {
|
||
return &api.AuthentitionResponse{Error: fmt.Errorf("user is already logined"), Code: 1000}
|
||
}
|
||
res, err := f(data)
|
||
if err != nil {
|
||
return &api.AuthentitionResponse{Error: err, Code: 1001}
|
||
}
|
||
tokenClaim, err := m.GenerateTokenClaim(data)
|
||
if err != nil {
|
||
return &api.AuthentitionResponse{Error: err, Code: 1002}
|
||
}
|
||
tokenString, err := m.GenerateToken(tokenClaim)
|
||
if err != nil {
|
||
return &api.AuthentitionResponse{Error: err, Code: 1003}
|
||
}
|
||
//存储登录状态
|
||
m.StoreLoginState(data.Username, res, tokenClaim)
|
||
|
||
return &api.AuthentitionResponse{Token: tokenString}
|
||
}
|
||
|
||
func (m *manager) Logout(data *api.AuthentitionRequest) *api.AuthentitionResponse {
|
||
//解析token,确认有logout权限
|
||
claim, err := m.ParseTokenClaim(data.Token)
|
||
if err != nil {
|
||
return &api.AuthentitionResponse{Error: err, Code: 1004}
|
||
|
||
}
|
||
if c, ok := m.IsLogined(claim.Username); ok {
|
||
if claim.ExpiresAt == c.ExpiresAt {
|
||
delete(m.tokenStore, data.Username)
|
||
return &api.AuthentitionResponse{Message: "success"}
|
||
}
|
||
}
|
||
return &api.AuthentitionResponse{Error: fmt.Errorf("user not login or token invalid"), Code: 1005}
|
||
}
|
||
|
||
func (m *manager) Refresh(data *api.AuthentitionRequest) *api.AuthentitionResponse {
|
||
cliam, err := m.ParseTokenClaim(data.Token)
|
||
if err != nil {
|
||
return &api.AuthentitionResponse{Error: err, Code: 1006}
|
||
}
|
||
//已过期
|
||
if cliam.Valid() != nil {
|
||
return &api.AuthentitionResponse{Error: fmt.Errorf("cannot use an invalid token to refresh: %v", err), Code: 1007}
|
||
|
||
}
|
||
//找不到登陆状态
|
||
if _, ok := m.IsLogined(cliam.Username); !ok {
|
||
return &api.AuthentitionResponse{Error: fmt.Errorf("token invalid, user maybe not login"), Code: 1005}
|
||
|
||
}
|
||
//重置有效期
|
||
cliam.ExpiresAt = time.Now().Add(m.term).Unix()
|
||
m.ReSetTokenClaim(cliam)
|
||
|
||
tokenStr, err := m.GenerateToken(cliam)
|
||
|
||
if err != nil {
|
||
return &api.AuthentitionResponse{Error: err, Code: 1003}
|
||
}
|
||
|
||
return &api.AuthentitionResponse{Token: tokenStr}
|
||
}
|
||
|
||
func (m *manager) KubernetesToken(data *api.AuthentitionRequest) (string, error) {
|
||
if t, ok := m.tokenStore[data.Username]; ok {
|
||
return t.kubernetesToken, nil
|
||
}
|
||
return "", fmt.Errorf("not found")
|
||
}
|
||
|
||
func (m *manager) KubernetesCert(data *api.AuthentitionRequest) (string, error) {
|
||
if t, ok := m.tokenStore[data.Username]; ok {
|
||
return t.cert, nil
|
||
}
|
||
return "", fmt.Errorf("not found")
|
||
}
|
||
|
||
//生成token claim对象
|
||
func (m *manager) GenerateTokenClaim(data *api.AuthentitionRequest) (*TokenClaim, error) {
|
||
now := time.Now()
|
||
claim := TokenClaim{
|
||
Username: data.Username,
|
||
Group: data.Group,
|
||
StandardClaims: gojwt.StandardClaims{
|
||
ExpiresAt: now.Add(m.term).Unix(),
|
||
},
|
||
}
|
||
return &claim, nil
|
||
}
|
||
|
||
//从string解析token claim
|
||
func (m *manager) ParseTokenClaim(token string) (*TokenClaim, error) {
|
||
tokenClaims, err := gojwt.ParseWithClaims(token, &TokenClaim{}, func(token *gojwt.Token) (interface{}, error) {
|
||
return m.secret, nil
|
||
})
|
||
|
||
if tokenClaims != nil {
|
||
if claims, ok := tokenClaims.Claims.(*TokenClaim); ok && tokenClaims.Valid {
|
||
return claims, nil
|
||
}
|
||
}
|
||
//
|
||
return nil, err
|
||
}
|
||
|
||
//从token claim 生成 string token
|
||
func (m *manager) GenerateToken(claim *TokenClaim) (string, error) {
|
||
tokenClaims := gojwt.NewWithClaims(gojwt.SigningMethodHS256, claim)
|
||
token, err := tokenClaims.SignedString(m.secret)
|
||
return token, err
|
||
}
|
||
|
||
//存储登录状态
|
||
func (m *manager) StoreLoginState(user string, result api.IdentifyResult, claim *TokenClaim) {
|
||
//储存登录状态
|
||
t := tokenCache{
|
||
kubernetesToken: result.KubernetesToken,
|
||
claim: claim,
|
||
cert: result.Cert,
|
||
}
|
||
m.tokenStore[user] = t
|
||
}
|
||
|
||
//是否已登录
|
||
func (m *manager) IsLogined(user string) (*TokenClaim, bool) {
|
||
t, ok := m.tokenStore[user]
|
||
if ok {
|
||
return t.claim, true
|
||
}
|
||
return nil, false
|
||
}
|
||
|
||
//重置token状态
|
||
func (m *manager) ReSetTokenClaim(claim *TokenClaim) {
|
||
if t, ok := m.tokenStore[claim.Username]; ok {
|
||
t.claim = claim
|
||
m.tokenStore[claim.Username] = t
|
||
}
|
||
}
|
||
|
||
//token是否有效
|
||
func (m *manager) TokenValid(claim *TokenClaim) bool {
|
||
if t, ok := m.tokenStore[claim.Username]; ok {
|
||
if t.claim.ExpiresAt == claim.ExpiresAt && claim.Valid() == nil {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
//中间件
|
||
func (m *manager) MiddleWare() gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
tokenString := c.GetHeader("AccessToken")
|
||
if tokenString == "" {
|
||
c.AbortWithStatus(http.StatusUnauthorized)
|
||
return
|
||
}
|
||
tokenClaim, err := m.ParseTokenClaim(tokenString)
|
||
if err != nil {
|
||
c.AbortWithError(http.StatusUnauthorized, err)
|
||
return
|
||
}
|
||
if m.TokenValid(tokenClaim) {
|
||
c.AbortWithError(http.StatusUnauthorized, fmt.Errorf("token invalid"))
|
||
return
|
||
}
|
||
t, err := m.KubernetesToken(&api.AuthentitionRequest{Username: tokenClaim.Username})
|
||
if err != nil || t == "" {
|
||
c.AbortWithError(http.StatusUnauthorized, fmt.Errorf("k8s token not found"))
|
||
}
|
||
c.Request.Header.Set("Authorization", fmt.Sprintf("bearer %s", t))
|
||
cert, _ := m.KubernetesCert(&api.AuthentitionRequest{Username: tokenClaim.Username})
|
||
c.Request.Header.Set("cert", cert)
|
||
c.Next()
|
||
}
|
||
}
|
||
|
||
//初始化auth接口
|
||
func (m *manager) InitAuthRoute(identifyFunc api.IdentifyFunc, authGroup *gin.RouterGroup) {
|
||
authGroup.POST("/login", m.HandlerLogin(identifyFunc))
|
||
authGroup.POST("/logout", m.HandlerLogout())
|
||
authGroup.POST("/refresh", m.HandlerRefreshToken())
|
||
}
|