1.概念
JWK=Json web token
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密
token 保存在浏览器的strong里只有js可以调取
2. 起源
session的认证
3.token与session的区别
session的原理
我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。
简单说 因为http是无状态的协议,第一次浏览器发送请求将用户信息发送到服务器,服务器验证完返回一个sessionid给浏览器保存在cookie中并且服务器将sessionid 保存在redis中,浏览器发送第二次请求携带cookie服务器进行验证(通过sessionid从redis中获取用户信息)
token的原理
基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利
简单说 第一次浏览器发送请求将用户信息发送到服务器,服务器返回一个加密的token给浏览器,浏览器保存在strong中,浏览器发送第二次请求并在header(请求头中携带token值) 服务器从请求头中获取token值,验证并返回数据
session缺点
session的扩展性差高,服务器认证后在缓存中做保存,这样在分布式的应用上,相应的限制了负载均衡器的能力
session的效率差:随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来.
session的安全性差:因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击
token优点
因为json的通用性,所以JWT是可以进行跨语言支持的
因为有了payload[载荷]部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
它不需要在服务端保存会话信息, 所以它易于应用的扩展
JWT的构成
第一部分我们称它为头部(header),
{
'typ': 'JWT',
'alg': 'HS256'加密格式(第三部分对一、二部分的加密方式)
}
第二部分我们称其为载荷(payload, 挂载一些用户信息[非敏感信息])
ss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
{
"sub": "1234567890",
"username": "xxxx",
"exp": 过期时间
}
第三部分是签证(signature).
secret 秘钥
encodedString=第一部分base64加密结果+"."+第二部分base64加密结果
加密
var signature = HMACSHA256(encodedString, secret)
浏览器的请求头数据格式
headers: {
'Authorization': 'Bearer ' + token
}
go语言的实现
自定义负载信息
type MyClaims struct {
UserId int64 `json:"user_id"`
Username string `json:"username"`
jwt.StandardClaims
}
//设置过期时间
const TokenExpireDuration = time.Hour * 2
//秘钥
var MySecret = []byte("Secret")
//生成加密
func GenToken(username string, useid int64) (string, error) {
// 创建一个我们自己的声明
c := MyClaims{
useid, // 自定义字段
username, // 自定义字段
jwt.StandardClaims{
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
Issuer: viper.GetString(settings.Conf.AppinfoConfig.Name), // 签发人
},
}
// 使用指定的签名方法创建签名对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
// 使用指定的secret签名并获得完整的编码后的字符串token
return token.SignedString(MySecret)
}
//解析token
func ParseToken(tokenString string) (*MyClaims, error) {
// 注意点必须重新定义
mc := new(MyClaims)
// 解析token
token, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {
return MySecret, nil
})
if err != nil {
return nil, err
}
if token.Valid { // 校验token
return mc, nil
}
return nil, errors.New("invalid toke")
}