一、什么是token
Token是一种用户身份的验证方式。当用户第一次登录后,服务器生成一个Token并将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
相对于session/cookie,基于Token的用户验证方式有如下优势:
- 支持跨域访问:由于在设置cookie时是设置了domain为自身或父域,所以无法实现跨域访问,而在请求时将token放入请求头,跨域后不会使信息丢失。
- 无状态:由于token无需存储在服务器端,而是每次服务器从token中获取用户信息。
- 去耦:token可以在API被调用的任何地方生成,不需要绑定到一个特定的身份验证方案。
- 更适用于移动应用:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多
- 更安全:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御
- 基于标准化:API可以采用标准化的 JSON Web Token (JWT). JSON Web Tokens - jwt.io
也有如下劣势:
- 占带宽:由于正常情况下token要比 sessionId 更大,需要消耗更多流量,挤占更多带宽。实际上,开发过程中会在 JWT 中存储很多信息。
- 无法在服务端注销,很难解决劫持问题。
- 更多的CPU开销,省下了内存容量的同时,增加了CPU的验证开销。
二、Token基本流程
使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
- 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
- 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
- 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
三、JWT Token的组成
JWT Token分为三部分,分别是表头、有效载荷和签名。
3.1 表头Header
表头Header是一个json对象,存储元数据。
{
"alg":"HS256",
"typ":"JWT"
}
alg:签名的算法,默认为HMAC SHA。
typ:token的类型,JWT令牌统一写为JWT。
3.2 有效载荷Payload
Payload 里面是 Token 的具体内容,也是一个json字符串,这些内容里面有一些是标准字段,属于public,也可以添加自己定义的字典,属于private。payload的json结构并不像header那么简单,payload用来承载要传递的数据,它的json结构实际上是对JWT要传递的数据的一组声明,这些声明被JWT标准称为claims , JWT默认提供了一些标准的Claim,具体内容如下。
public:
iss(issuer):签发人
exp(expiration time):过期时间
sub(subject):该JWT所面向的用户
aud(audience):受众,接受该JWT的一方
nbf(not before):生效时间
iat(Issued At):签发时间
jti(JWT ID):编号
private:
{
"role": "admin",
"name": "管理员",
"id": 23455
}
以上数据都是明文传输的,所以最好不要存放敏感数据。前两部分数据都是json对象使用的是base64URL编码,翻译为字符串。
3.3 签名Signature
Signature是对前两部分的签名,以防数据被篡改。
HMACSHA256(
base64UrlEncoder(header)+"."+
base64UrlENcode(paylosd),
secret)
先指定一个secret密匙,把base64URL的header、base64URL的payload和secret密钥使用HNAC SHA256生成签名字符串。最后把三个部分的拼成一个字符串,每个部分之间用点(.)分隔,返回给用户。
四、JWT认证流程
- 前端通过POST请求将用户名和密码发送到后端接口。
- 后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token。
- 后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存,退出登录时删除保存的JWT Token即可。
- 前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中。
- 后端检查前端传过来的JWT Token,验证其有效性。比如检查签名是否正确、是否过期、token的接收方是否是自己等等。
- 验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作,返回结果给前端。