JWT=JSON Web Token,是目前较为风行的分布式认证计划。

认证计划

个别罕用传统的Cookie/Session认证计划,认证过程:

  1. 用户向服务器发送username+password;
  2. 服务器验证当前,存储该用户的登录信息,如userId/role/loginTime等;
  3. 服务器向用户返回 session_id,写入用户的cookie;
  4. 用户随后的每一次申请,都通过Cookie,将session_id传回服务器;
  5. 服务器收到session_id,找到后期保留的数据,由此失去用户的身份(userId/role等);

在分布式系统中,常遇到跨域认证的问题,比方A网站和B网站 是同一家公司的关联服务器。现要求,用户只有在其中一个网站登录,再拜访另一个网站就会主动登录,对该问题,罕用的计划有:

  • 传统的Cookie/Session计划:将session集中长久化并做cache,每来一个申请,都要用申请中的sessionId进行认证;
  • JWT计划:服务端不存储session数据,认证信息存储在客户端,服务端仅要session的颁发和校验;

JWT(JSON Web Token)是目前较为风行的跨域认证解决方案。

JWT的认证过程

JWT的认证过程是,客户端将用户名和明码传入服务端,服务端通过认证后,将生成一个JSON对象,发回给用户,JSON对象大略的格局:

{
  "姓名": "张三",
  "角色": "管理员",
  "到期工夫": "2018年7月1日0点0分"
}

当前客户端再与服务端通信的时候,都要带上这个JSON对象,服务端校验JSON对象的内容认证用户。

这样服务端不必保留任何session信息,服务端变成无状态的,扩展性较好。

JWT的数据结构:

  • Header:形容JWT的元数据,如签名算法;
  • Payload:寄存理论传递的数据,除了官网定义的签发人、过期工夫等四段,还能够寄存公有字段,如userId/userName等信息;
  • Signature:对前两局部的签名,避免数据篡改;

JWT的长处:服务端便于扩大,因为服务端不存储认证信息,无状态的,十分利于扩大。

JWT的毛病:JWT一旦签发,在到期之前始终无效,除非服务端部署额定的逻辑。

golang-jwt的demo

http-server应用github.com/gin-gonic/gin。
jwt应用github.com/dgrijalva/jwt-go。

该demo中,client通过POST /login进行登录,而后获取JWT token,而后通过在header中带上token,POST /order进行商品下单。

生成jwt token

JWT token在用户/login的时候,由服务端调配:

type AuthClaim struct {
    UserId uint64 `json:"userId"`
    jwt.StandardClaims
}

var secret = []byte("It is my secret")

const TokenExpireDuration = 2 * time.Hour

// 生成JWT token
func GenToken(userId uint64) (string, error) {
    c := AuthClaim{
        UserId: userId,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(),
            Issuer:    "TEST001",
        },
    }
    //应用指定的签名办法创立签名对象
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
    // 应用指定的secret签名并取得残缺的编码后的字符串token
    return token.SignedString(secret)
}

校验jwt token

login胜利后,用户再申请其它的request时,带上该JWT token,交由服务端的middleware认证,认证OK后,才会失去解决:

func ParseToken(tokenStr string) (*AuthClaim, error) {
    token, err := jwt.ParseWithClaims(tokenStr, &AuthClaim{}, func(tk *jwt.Token) (interface{}, error) {
        return secret, nil
    })
    if err != nil {
        return nil, err
    }
    if claim, ok := token.Claims.(*AuthClaim); ok && token.Valid {
        return claim, nil
    }
    return nil, errors.New("Invalid token ")
}

JWTAuthMiddleware的实现

func jwtAuthMiddleware() func(c *gin.Context) {
    return func(c *gin.Context) {
        token := c.Request.Header.Get("token")
        if token == "" {
            c.JSON(http.StatusForbidden, "empty token")
            c.Abort()
            return
        }
        claim, err := ParseToken(token)
        if err != nil {
            c.JSON(http.StatusForbidden, "Invalid token")
            c.Abort()
            return
        }
        c.Set("userId", claim.UserId)
        c.Next()
    }
}

在gin中应用JWTMiddleware:

r := gin.Default()
r.POST("/login", loginHandler)

api := r.Group("/api")
api.Use(jwtAuthMiddleware())
api.POST("/order", orderHandler)

参考:

1.https://mojotv.cn/go/golang-j…
2.https://ruanyifeng.com/blog/2…
3.https://github.com/lwangrabbi…