美文网首页
micro统一认证 Token

micro统一认证 Token

作者: 路人甲Boger | 来源:发表于2020-07-09 15:19 被阅读0次

    micro 使用 jwt 做统一的token 验证,看代码就好了

    package token_jwt
    
    import (
        "encoding/json"
        "errors"
        "github.com/dgrijalva/jwt-go"
        "github.com/micro/cli"
        "github.com/micro/go-micro/config"
        "github.com/micro/go-micro/config/source/etcd"
        "github.com/micro/go-micro/v2/logger"
        "github.com/micro/micro/plugin"
        "net/http"
        "strings"
    )
    
    var (
        TokenExpired     = errors.New("Token is expired")
        TokenNotValidYet = errors.New("Token not active yet")
        TokenMalformed   = errors.New("That's not even a token")
        TokenInvalid     = errors.New("Couldn't handle this token:")
    )
    
    type Token struct {
        Name       string
        PrivateKey []byte
        conf config.Config
        UnAuthPath []string
    }
    
    type JwtPayloadInfo struct {
        UserName string
        UUID        string
        ID          uint
        NickName    string
        AuthorityId string
        jwt.StandardClaims
    }
    
    //定义一些参数,可以通过启动micro 的时候传参
    func (l *Token) Flags() []cli.Flag {
        return []cli.Flag{cli.StringFlag{
            Name:   "token_path",
            Usage:  "token私钥在etcd中的路径",
            EnvVar: "TOKEN_PATH",
        }}
    }
    func (l *Token) Commands() []cli.Command {
        return nil
    }
    
    //处理程序 会在每次请求的时候调用
    func (l *Token) Handler() plugin.Handler {
        return func(handler http.Handler) http.Handler {
            return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                //处理token 的逻辑
                if  Contains(l.UnAuthPath,r.URL.Path)!=-1  {
                    logger.Info("allow pass url :",r.URL.Path)
                    handler.ServeHTTP(w, r)
                    return
                }
                authStr := r.Header.Get("Authorization")
                logger.Infof("authStr:%s",authStr)
                payload, err := l.Decode(authStr)
                if err != nil {
                    logger.Info("auth fail:",err)
                    w.WriteHeader(http.StatusUnauthorized)
                    return
                }
                bs,_:=json.Marshal(payload)
                logger.Infof("payload info= %s", string(bs))
                r.Header.Add("claims",string(bs))
                handler.ServeHTTP(w, r)
                return
            })
        }
    }
    
    //初始化数据,程序启动的时候会调用这个方法,可以在这里初始化一些参数
    func (l *Token) Init(ctx *cli.Context) error {
        //从配置加载 公钥
        //加载公钥,放行链接等,存放到实例里面
        address:=ctx.String("registry_address")
        token_path:=ctx.String("token_path")
        source := etcd.NewSource(
            etcd.WithAddress(address),
            )
    
        l.conf=config.NewConfig()
        err:=l.conf.Load(source)
        if err!=nil {
            logger.Fatal(err)
        }
        value:=l.conf.Get(strings.Split(token_path,"/")...).Bytes()
        logger.Debug(l.conf.Map())
        if err!=nil {
            logger.Fatal(err)
        }
        l.PrivateKey=value
    
        logger.Info("JWT privateKey:", string(value))
    
        //初始化放行地址
        l.UnAuthPath=make([]string,0)
        l.UnAuthPath=append(l.UnAuthPath,"/admin/base/login")
    
        l.enableAutoUpdate(strings.Split(token_path,"/")...)
        return nil
    }
    
    //配置更新的方法
    func (l *Token) enableAutoUpdate(path ...string) {
        go func() {
            for {
                w, err := l.conf.Watch(path...)
                if err != nil {
                    logger.Error(err)
                }
                v, err := w.Next()
                if err != nil {
                    logger.Error(err)
                }
    
                value := v.Bytes()
                l.PrivateKey=value
    
                logger.Info("New JWT privateKey:", string(l.PrivateKey))
            }
        }()
    }
    
    
    func (l *Token) String() string {
        return l.Name
    }
    
    //调用这个方法 new 插件,也可以在启动的时候这样子写
    func NewPlugin() plugin.Plugin {
        return &Token{
            Name: "token-jwt",
        }
    }
    
    //Decode 解码
    func (l *Token) Decode(tokenStr string) (*JwtPayloadInfo, error) {
        token, err := jwt.ParseWithClaims(tokenStr, &JwtPayloadInfo{}, func(token *jwt.Token) (i interface{}, e error) {
            return l.PrivateKey, nil
        })
        if err != nil {
            if ve, ok := err.(*jwt.ValidationError); ok {
                if ve.Errors&jwt.ValidationErrorMalformed != 0 {
                    return nil, TokenMalformed
                } else if ve.Errors&jwt.ValidationErrorExpired != 0 {
                    // Token is expired
                    return nil, TokenExpired
                } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
                    return nil, TokenNotValidYet
                } else {
                    return nil, TokenInvalid
                }
            }
        }
        if token != nil {
            if claims, ok := token.Claims.(*JwtPayloadInfo); ok && token.Valid {
                return claims, nil
            }
            return nil, TokenInvalid
    
        } else {
            return nil, TokenInvalid
    
        }
    }
    
    // Encode 将 User 用户信息加密为 JWT 字符串
    // expireTime := time.Now().Add(time.Hour * 24 * 3).Unix() 三天后过期
    func (l *Token) CreateToken(claims JwtPayloadInfo) (string, error) {
        logger.Infof("encode token info :%v",claims)
        jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        return jwtToken.SignedString(l.PrivateKey)
    }
    
    //判断数组包含
    func Contains(array []string, val string) (index int) {
        index = -1
        for i,v:=range array  {
            if v==val{
                index=i
                return
            }
        }
        return
    }
    
    
    

    相关文章

      网友评论

          本文标题:micro统一认证 Token

          本文链接:https://www.haomeiwen.com/subject/lrhscktx.html