JWT
全称JSON Web Token
应用流程
- 客户端使用用户名和密码请求登录,服务端收到请求
- 验证用户名和密码正确后,后端通过
JWT
机制,将用户数据作为JWT
的Payload
,同时在前面拼接上一个JWT Header
之后进行Base64
编码,并进行签名,生成一个token
,格式为header.payload.signature
,返回给客户端 - 客户端后续的每次请求都需要携带
token
,携带在HTTP Header Authorization
中 - 后端拿到客户端传递的
token
后,进行解密验证身份,验证其有效性,检查签名是否正确,是否过期,最后解析出JWT Token
中的用户信息
优点
- 无状态,
token
机制在服务端不需要存储session
信息,因为token
自身包含了所有登录用户的信息,所以可以减轻服务端压力 - 分布式友好,由于
session
需要固定保存在一个地方,如果保存在本机,分布式系统中认证会失效,如果采用redis
等统一保存session
,系统复杂性会增加 - 支持跨域访问,跨域后不会存在信息丢失问题
CDN
友好,可以通过内容分发网络请求服务端的所有资料- 移动端友好,当客户端是非浏览器平台时,
cookie
是不被支持的,此时采用token
认证方式会简单很多 无需考虑
CSRF
(Cross Site Request Forgery
跨站点请求伪造),token
是开发者为了防范csrf
而特别设计的令牌,浏览器不会自动添加到headers
里,攻击者也无法访问用户的token
,所以攻击者提交的表单无法通过服务器认证,也就无法形成攻击CSRF
简述- 在一个浏览器中打开了两个标签页,其中一个页面通过窃取另一个页面的
cookie
来发送伪造的请求,因为cookie
是随着浏览器请求自动发送到服务端的,这个是CSRF
攻击成功的核心原因 session
认证本质需要依赖Cookie
,如果cookie
被截获,用户很容易受到跨站请求伪造攻击CSRF
无法直接窃取到用户的Cookie,header
,仅仅是冒用Cookie
- 在一个浏览器中打开了两个标签页,其中一个页面通过窃取另一个页面的
缺点
- 不可控,由于
JWT
无状态,想要在JWT
有效期内废弃一个JWT
或者更改它的权限的话,并不会立即生效,通常需要等到有效期过后才可以,如果要避免这个问题,需要把JWT
存入redis
等缓存,JWT
失效的时候就删除这个JWT
,每次验证的时候查询一下JWT
在不在redis
,但是这样就增加了成本和系统复杂度 token
续签不方便,JWT
本身payload
参数当中携带exp
参数表示过期时间,payload
修改之后签名也需要修改,所以需要重新生成一个JWT
JWT
令牌一般会比较长,如果是性能极度敏感的话需要在意这一点
组成
JWT
生成的Token
由三部分组成:header.payload.signature
,通俗地说,JWT
的本质是一个字符串,将用户信息保存到一个Json
字符串中,然后进行编码后得到一个JWT token
,并且这个JWT token
带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json
对象传输
header
alg
:指定signature
采用的加密算法,默认是HS256(HMAC SHA256)
,对称加密(加密和解密的密钥相同)typ
:固定值,通常是JWT
- 通常值是
{"alg": "HS256", "typ": "JWT"}
, 通过base64Url
算法进行编码之后进行拼接
payload
- 用户
id
和name
- 默认携带
iat
,令牌签发时间(时间戳) exp
设置令牌过期时间参数一般形式如下,通过
base64Url
算法进行编码与header
进行拼接,默认情况下JWT
是未加密的,因为只是采用base64
算法,拿到JWT
字符串后可以转换回原本的JSON
数据,任何人都可以解读其内容,因此不要构建隐私信息字段,比如用户的密码一定不能保存到JWT
中,JWT
只是适合在网络中传输一些非敏感的信息,要传递一些敏感数据的话需要使用一些AES
或者其他类型的算法,尽量加上一些salt
进行加密payload
{ "sub": "1234567890", "name": "Helen", "admin": true }
- 用户
signature
- 设置一个
secretKey
,通过将前两个结果合并后进行HS256
算法,signature = HS256(base64Url(header)+'.'+base64Url(payload),secretKey)
secreKey
一定不能暴露,因为可以颁发token
,也可以解密
- 设置一个
跨域资源共享
是一种基于 HTTP
头的机制,该机制主要是为了避免跨站脚本攻击而存在
实际网站开发过程当中需要从A
网站访问到另外一个网站B
,就需要通过允许服务器标示除了它自己以外的其他源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源,服务器需要返回一个HTTP Header Access-Control-Allow-Origin: *
表明,该资源可以被任意外源访问,这样浏览器才会正常加载返回的数据
但是当携带cookie
进行访问的时候就不能返回一个Header
头表示允许被任意外源访问
- 服务器不能将
Access-Control-Allow-Origin
的值设为通配符“*
”,而应将其设置为特定的域,如:Access-Control-Allow-Origin: https://example.com
,如果值被设置为*
,请求会失败,如歌设置为具体的域请求成功 - 服务器不能将
Access-Control-Allow-Headers
的值设为通配符“*
”,而应将其设置为标头名称的列表,如:Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
- 服务器不能将
Access-Control-Allow-Methods
的值设为通配符“*
”,而应将其设置为特定请求方法名称的列表,如:Access-Control-Allow-Methods: POST, GET
详情参考Mozilla
对跨域资源共享的定义
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
python
实现
依赖项目pyjwt
https://github.com/jpadilla/pyjwt
安装
$ pip install pyjwt
使用
>>> import jwt
>>> encoded = jwt.encode({"some": "payload"}, "secret", algorithm="HS256")
>>> print(encoded)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg
>>> jwt.decode(encoded, "secret", algorithms=["HS256"])
{'some': 'payload'}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。