package jwts
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/dgrijalva/jwt-go"
|
|
"github.com/kataras/golog"
|
|
"LEIT_PM/conf"
|
|
"LEIT_PM/global"
|
|
"LEIT_PM/web/supports"
|
|
"log"
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
"sync"
|
|
|
|
"github.com/kataras/iris/v12"
|
|
)
|
|
|
|
// iris provides some basic middleware, most for your learning courve.
|
|
// You can use any net/http compatible middleware with iris.FromStd wrapper.
|
|
//
|
|
// JWT net/http video tutorial for golang newcomers: https://www.youtube.com/watch?v=dgJFeqeXVKw
|
|
//
|
|
// Unlike the other middleware, this middleware was cloned from external source: https://github.com/auth0/go-jwt-middleware
|
|
// (because it used "context" to define the user but we don't need that so a simple iris.FromStd wouldn't work as expected.)
|
|
// jwt_test.go also didn't created by me:
|
|
// 28 Jul 2016
|
|
// @heralight heralight add jwts unit test.
|
|
//
|
|
// So if this doesn't works for you just try other net/http compatible middleware and bind it via `iris.FromStd(myHandlerWithNext)`,
|
|
// It's here for your learning curve.
|
|
|
|
type (
|
|
// A function called whenever an error is encountered
|
|
errorHandler func(iris.Context, string)
|
|
|
|
// TokenExtractor is a function that takes a context as input and returns
|
|
// either a token or an error. An error should only be returned if an attempt
|
|
// to specify a token was found, but the information was somehow incorrectly
|
|
// formed. In the case where a token is simply not present, this should not
|
|
// be treated as an error. An empty string should be returned in that case.
|
|
TokenExtractor func(iris.Context) (string, error)
|
|
|
|
// Middleware the middleware for JSON Web tokens authentication method
|
|
Jwts struct {
|
|
Config Config
|
|
}
|
|
)
|
|
|
|
var (
|
|
jwts *Jwts
|
|
lock sync.Mutex
|
|
)
|
|
|
|
// Serve the middleware's action
|
|
func Serve(ctx iris.Context) bool {
|
|
ConfigJWT()
|
|
if err := jwts.CheckJWT(ctx); err != nil {
|
|
//supports.Unauthorized(ctx, supports.Token_failur, nil)
|
|
//ctx.StopExecution()
|
|
golog.Errorf("Check jwt error, %s", err)
|
|
return false
|
|
}
|
|
return true
|
|
// If everything ok then call next.
|
|
//ctx.Next()
|
|
}
|
|
|
|
// 解析token的信息为当前用户
|
|
func ParseToken(ctx iris.Context) (*global.User, bool) {
|
|
mapClaims := (jwts.Get(ctx).Claims).(jwt.MapClaims)
|
|
|
|
plantNr, ok1 := mapClaims["plantNr"].(float64)
|
|
userId, ok2 := mapClaims["userId"].(string)
|
|
role, ok3 := mapClaims["role"].(string)
|
|
roleId, ok4 := mapClaims["roleId"].(float64)
|
|
|
|
if !ok1 || !ok2 || !ok3 || !ok4 {
|
|
supports.Error(ctx, iris.StatusInternalServerError, supports.TokenParseFailur, nil)
|
|
return nil, false
|
|
}
|
|
|
|
user := global.User{
|
|
PlantNr: int(plantNr),
|
|
UserId: userId,
|
|
Role: role,
|
|
RoleId: int(roleId),
|
|
}
|
|
return &user, true
|
|
}
|
|
|
|
// 解析token的信息为当前用户和Token签名
|
|
func ParseTokenAndSignature(ctx iris.Context) (*global.User, string, bool) {
|
|
token := jwts.Get(ctx)
|
|
mapClaims := (token.Claims).(jwt.MapClaims)
|
|
|
|
plantNr, ok1 := mapClaims["plantNr"].(float64)
|
|
userId, ok2 := mapClaims["userId"].(string)
|
|
role, ok3 := mapClaims["role"].(string)
|
|
roleId, ok4 := mapClaims["roleId"].(float64)
|
|
|
|
if !ok1 || !ok2 || !ok3 || !ok4 {
|
|
supports.Error(ctx, iris.StatusInternalServerError, supports.TokenParseFailur, nil)
|
|
return nil, "", false
|
|
}
|
|
|
|
user := global.User{
|
|
PlantNr: int(plantNr),
|
|
UserId: userId,
|
|
Role: role,
|
|
RoleId: int(roleId),
|
|
}
|
|
return &user, token.Signature, true
|
|
}
|
|
|
|
// 解析token的信息为当前用户
|
|
func GetToken(ctx iris.Context) *jwt.Token {
|
|
return jwts.Get(ctx)
|
|
}
|
|
|
|
// below 3 method is get token from url
|
|
// FromAuthHeader is a "TokenExtractor" that takes a give context and extracts
|
|
// the JWT token from the Authorization header.
|
|
func FromAuthHeader(ctx iris.Context) (string, error) {
|
|
authHeader := ctx.GetHeader("Authorization")
|
|
if authHeader == "" {
|
|
return "", nil // No error, just no token
|
|
}
|
|
|
|
// TODO: Make this a bit more robust, parsing-wise
|
|
authHeaderParts := strings.Split(authHeader, " ")
|
|
if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != "bearer" {
|
|
return "", fmt.Errorf("Authorization header format must be Bearer {token}")
|
|
}
|
|
|
|
return authHeaderParts[1], nil
|
|
}
|
|
|
|
// below 3 method is get token from url
|
|
// FromParameter returns a function that extracts the token from the specified
|
|
// query string parameter
|
|
func FromParameter(param string) TokenExtractor {
|
|
return func(ctx iris.Context) (string, error) {
|
|
return ctx.URLParam(param), nil
|
|
}
|
|
}
|
|
|
|
// below 3 method is get token from url
|
|
// FromFirst returns a function that runs multiple token extractors and takes the
|
|
// first token it finds
|
|
func FromFirst(extractors ...TokenExtractor) TokenExtractor {
|
|
return func(ctx iris.Context) (string, error) {
|
|
for _, ex := range extractors {
|
|
token, err := ex(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if token != "" {
|
|
return token, nil
|
|
}
|
|
}
|
|
return "", nil
|
|
}
|
|
}
|
|
|
|
func (m *Jwts) logf(format string, args ...interface{}) {
|
|
if m.Config.Debug {
|
|
log.Printf(format, args...)
|
|
}
|
|
}
|
|
|
|
// Get returns the user (&token) information for this client/request
|
|
func (m *Jwts) Get(ctx iris.Context) *jwt.Token {
|
|
return ctx.Values().Get(m.Config.ContextKey).(*jwt.Token)
|
|
}
|
|
|
|
// CheckJWT the main functionality, checks for token
|
|
func (m *Jwts) CheckJWT(ctx iris.Context) error {
|
|
if !m.Config.EnableAuthOnOptions {
|
|
if ctx.Method() == iris.MethodOptions {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Use the specified token extractor to extract a token from the request
|
|
token, err := m.Config.Extractor(ctx)
|
|
// If an error occurs, call the error handler and return an error
|
|
if err != nil {
|
|
m.logf("Error extracting JWT: %v", err)
|
|
m.Config.ErrorHandler(ctx, supports.TokenExactFailur)
|
|
return fmt.Errorf("Error extracting token: %v", err)
|
|
}
|
|
|
|
// If the token is empty...
|
|
if token == "" {
|
|
// Check if it was required
|
|
if m.Config.CredentialsOptional {
|
|
m.logf(" No credentials found (CredentialsOptional=true)")
|
|
// No error, just no token (and that is ok given that CredentialsOptional is true)
|
|
return nil
|
|
}
|
|
|
|
m.logf(" Error: No credentials found (CredentialsOptional=false)")
|
|
// If we get here, the required token is missing
|
|
m.Config.ErrorHandler(ctx, supports.TokenParseFailurAndEmpty)
|
|
return fmt.Errorf(supports.TokenParseFailurAndEmpty)
|
|
}
|
|
|
|
// Now parse the token
|
|
|
|
parsedToken, err := jwt.Parse(token, m.Config.ValidationKeyGetter)
|
|
|
|
// Check if there was an error in parsing...
|
|
if err != nil {
|
|
m.logf("Error parsing token1: %v", err)
|
|
m.Config.ErrorHandler(ctx, supports.TokenExpire)
|
|
return fmt.Errorf("Error parsing token2: %v", err)
|
|
}
|
|
|
|
if m.Config.SigningMethod != nil && m.Config.SigningMethod.Alg() != parsedToken.Header["alg"] {
|
|
message := fmt.Sprintf("Expected %s signing method but token specified %s",
|
|
m.Config.SigningMethod.Alg(),
|
|
parsedToken.Header["alg"])
|
|
m.logf("Error validating token algorithm: %s", message)
|
|
m.Config.ErrorHandler(ctx, supports.TokenParseFailur) // 算法错误
|
|
return fmt.Errorf("Error validating token algorithm: %s", message)
|
|
}
|
|
|
|
// Check if the parsed token is valid...
|
|
if !parsedToken.Valid {
|
|
m.logf(supports.TokenParseFailurAndInvalid)
|
|
m.Config.ErrorHandler(ctx, supports.TokenParseFailurAndInvalid)
|
|
return fmt.Errorf(supports.TokenParseFailurAndInvalid)
|
|
}
|
|
|
|
if m.Config.Expiration {
|
|
if claims, ok := parsedToken.Claims.(jwt.MapClaims); ok {
|
|
if expired := claims.VerifyExpiresAt(time.Now().Unix(), true); !expired {
|
|
return fmt.Errorf(supports.TokenExpire)
|
|
}
|
|
}
|
|
}
|
|
|
|
//m.logf("JWT: %v", parsedToken)
|
|
|
|
// If we get here, everything worked and we can set the
|
|
// user property in context.
|
|
ctx.Values().Set(m.Config.ContextKey, parsedToken)
|
|
|
|
return nil
|
|
}
|
|
|
|
// jwt中间件配置
|
|
func ConfigJWT() {
|
|
if jwts != nil {
|
|
return
|
|
}
|
|
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
if jwts != nil {
|
|
return
|
|
}
|
|
|
|
c := Config{
|
|
ContextKey: DefaultContextKey,
|
|
//这个方法将验证jwt的token
|
|
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
|
|
//自己加密的秘钥或者说盐值
|
|
return []byte(conf.AppConfig.Other.Secret), nil
|
|
},
|
|
//设置后,中间件会验证令牌是否使用特定的签名算法进行签名
|
|
//如果签名方法不是常量,则可以使用ValidationKeyGetter回调来实现其他检查
|
|
//重要的是要避免此处的安全问题:https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
|
|
//加密的方式
|
|
SigningMethod: jwt.SigningMethodHS256,
|
|
//验证未通过错误处理方式
|
|
ErrorHandler: func(ctx iris.Context, errMsg string) {
|
|
supports.Error(ctx, iris.StatusUnauthorized, errMsg, nil)
|
|
},
|
|
// 指定func用于提取请求中的token
|
|
Extractor: FromAuthHeader,
|
|
// if the token was expired, expiration error will be returned
|
|
Expiration: true,
|
|
Debug: true,
|
|
EnableAuthOnOptions: false,
|
|
}
|
|
jwts = &Jwts{Config: c}
|
|
}
|
|
|
|
type Claims struct {
|
|
PlantNr int `json:"plantNr"`
|
|
Userid string `json:"userId"`
|
|
Role string `json:"role"`
|
|
RoleId int `json:"roleId"`
|
|
jwt.StandardClaims
|
|
}
|
|
|
|
// 在登录成功的时候生成token
|
|
func GenerateToken(user *global.User) (string, error) {
|
|
//expireTime := time.Now().Add(60 * time.Second)
|
|
expireTime := time.Now().Add(time.Duration(conf.AppConfig.Other.JWTTimeout) * time.Second)
|
|
|
|
claims := Claims{
|
|
user.PlantNr,
|
|
user.UserId,
|
|
user.Role,
|
|
user.RoleId,
|
|
jwt.StandardClaims{
|
|
ExpiresAt: expireTime.Unix(),
|
|
Issuer: "iris-casbins-jwt",
|
|
},
|
|
}
|
|
fmt.Println(claims)
|
|
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
token, err := tokenClaims.SignedString([]byte(conf.AppConfig.Other.Secret))
|
|
return token, err
|
|
}
|