go-jwt 权限验证 | 青训营笔记

132 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天

前言

在大项目编写过程中,涉及到了安全验证,我学习并采用了jwt这一解决方案

一.概念

jwt,全名(json web token),是一种跨域的认证的解决方案,属于一个开放的标准。使用其规定了一种token的实现方式。

二.为什么使用

传统的的web项目,使用的都是session来认证用户的信息,具体的流程如下:

1.用户通过浏览器将账号跟密码传输给后台服务。

2.服务端对用户跟密码校验后会生成一份保存当前用户信息的session和一个对应的session_id。

3.如果返回响应的时候会将生成的session_id一并返回,浏览器会将该值写入cookie中。

4.如果用户再次通过该浏览器访问服务时,都会携带含有session_id的cookie信息至服务端。

5.服务端接收到请求后,利用cookie中session_id查找对应的账号相关的session信息,从而再次验证当前请求。

6.对于非浏览器的客户端、手机移动端等不适用,因为session依赖于cookie,而移动端经常没有cookie因为session认证本质基于cookie,所以如果cookie被截获,用户很容易收到跨站请求伪造攻击。并且如果浏览器禁用了cookie,这种方式也会失效,前后端分离系统中更加不适用,后端部署复杂,前端发送的请求往往经过多个中间件到达后端,cookie中关于session的信息会转发多次。cookie无法跨域,不适用于单点登录。

存在的缺陷:

1.极度依赖浏览器保存cookie机制。并且再服务端,需要为每一个账号都创建一个session来保存相关信息。(存储消耗较大,如果用户量大,分布式存储也会持续的增大并且维护力度增加)。

2.用户登录方式多样化(不可能总是同一个浏览器,限制了自由度),例如用户使用第三方或者app来访问。如果再次使用cookie模式,就会很是有1中的问题,并且也不灵活。

三.jwt的使用

2.结构:

jwt-token由三部分组成,header + payload + signature组成。公式如下:

JWTString=Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

header部分:

是一个json对象:

{
 
  "typ": "JWT",
 
   "alg": 加密方法,例如(HS256)
 
}

payload:

有效载荷部分,jwt主题部分,

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

除此之外,可以可以根据需要自定义相关的信息

{
 "user_id": 0
 "username":"",
}

完整结构例如

cla := MyClaims{
		username,
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
			Issuer:    "lx-jwt",                                   // 签发人
		},
	}
 
type StandardClaims struct {
	Audience  string `json:"aud,omitempty"`
	ExpiresAt int64  `json:"exp,omitempty"`
	Id        string `json:"jti,omitempty"`
	IssuedAt  int64  `json:"iat,omitempty"`
	Issuer    string `json:"iss,omitempty"`
	NotBefore int64  `json:"nbf,omitempty"`
	Subject   string `json:"sub,omitempty"`
}

Signature:

签名是对上面两部分的数据进行签名,过程是使用base64编码后的数据,通过制定的算哈生成哈希(不可逆),这样用于确保数据不会被篡改。另外,还需要一个密钥secret,该secret仅仅保存在服务端,并且不可公开,secret不能对外泄漏,保存于服务端。

4.使用go的go-jwt实现的一个模拟获取token以及请求认证过程

package jwt_use
 
import (
	"errors"
	"fmt"
	"time"
 
	"github.com/dgrijalva/jwt-go"
)
 
const TokenExpireDuration = time.Hour * 2
// const TokenExpireDuration = time.Second * 60
 
var Secret = []byte("人生路漫漫")
 
type MyClaims struct {
	UserName string
	jwt.StandardClaims
}
 
// get token
func GetToken(username string) (string, error) {
	cla := MyClaims{
		username,
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
			Issuer:    "lx-jwt",                                   // 签发人
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, cla)
	fmt.Println("Token = ",token)
	return token.SignedString(Secret) // 进行签名生成对应的token
}
 
// parse token
func ParseToken(tokenString string) (*MyClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
		return Secret, nil
	})
	if err != nil {
		return nil, err
	}
	if claims,ok := token.Claims.(*MyClaims);ok && token.Valid {
		return claims,nil
	}
	return nil,errors.New("invalid token")
}

总结

通过jwt生成token,并且解析token,结合gin框架的中间件,可以在项目中实现对登录权限的验证,甚至实现免登录功能。