JWT认证与golang实现JWT的demo

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认证与golang实现JWT的demo】JWT认证与golang实现JWT的demo
文章图片

JWT的数据结构:
  • Header:描述JWT的元数据,如签名算法;
  • Payload:存放实际传递的数据,除了官方定义的签发人、过期时间等四段,还可以存放私有字段,如userId/userName等信息;
  • Signature:对前两部分的签名,防止数据篡改;
JWT认证与golang实现JWT的demo
文章图片

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的时候,由服务端分配:
JWT认证与golang实现JWT的demo
文章图片

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后,才会得到处理:
JWT认证与golang实现JWT的demo
文章图片

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...

    推荐阅读