局外人1
2022/07/21阅读:26主题:默认主题
Token
Token
在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。一般作为邀请、登录系统使用。
Token出现的背景
-
在早前的Web应用中,Web 基本上就是文档的浏览而已, 既然是浏览,作为服务器, 不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的
HTTP
协议, 就是请求加响应, 尤其是我不用记住是谁刚刚发了HTTP
请求,每个请求对我来说都是全新的; -
但是随着交互式Web应用的兴起,像在线购物网站,需要登录的网站等等,马上就面临一个问题,那就是要管理会话,必须记住哪些人登录系统, 哪些人往自己的购物车中放商品, 也就是说我必须把每个人区分开,这就是一个不小的挑战,因为HTTP请求是无状态的,所以想出的办法就是给大家发一个会话标识(
session id
), 说白了就是一个随机的字串,每个人收到的都不一样, 每次大家向我发起HTTP请求的时候,把这个字符串给一并捎过来, 这样我就能区分开谁是谁了; -
但是客户端只需要保存自身的
session id
,而服务器端则要保存所有客户端的session id
,这对服务器说是一个巨大的开销 , 严重的限制了服务器扩展能力;Token
的出现解决了这个问题,因为服务端不需要存储Token
的信息,而是通过CPU的计算 + 数据的加密解密再核对Token
的方式来验证用户是否合法(即HTTP
请求信息有没有被篡改),让服务器内存得到释放; -
session id
可以被伪造,没有采取加密的方法!!!,一旦攻击者通过session id
伪造攻击,就会给服务器带来压力甚至击垮服务器。 -
Token
是通过加密算法(如:HMAC-SHA256
算法)来实现session
对象验证的,这样使得攻击者无法伪造token
来达到攻击或者其他对服务器不利的行为。
Token的作用
节省服务器内存
数据签名防伪造攻击
完全由应用管理,所以它可以避开同源策略,即可以跨域访问
应用场景:单点登录(SSO)单点登录的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统
可以避免 CSRF 攻击
可以是无状态的,可以在多个服务间共享
Token的优势
1. 无状态、可扩展
在客户端存储的Token
是无状态的,并且能够被扩展。基于这种无状态和不存储Session
信息,负载负载均衡器能够将用户信息从一个服务传到其他服务器上。如果我们将已验证的用户的信息保存在Session
中,则每次请求都需要用户向已验证的服务器发送验证信息(称为Session
亲和性)。用户量大时,可能会造成 一些拥堵。但是不要着急。使用token
之后这些问题都迎刃而解,因为token
自己hold住了用户的验证信息。
2. 安全性
请求中发送token
而不再是发送cookie
能够防止CSRF(跨站请求伪造)
。即使在客户端使用cookie
存储token,cookie也仅仅是一个存储机制而不是用于认证。不将信息存储在Session
中,让我们少了对session
操作。
token
是有时效的,一段时间之后用户需要重新验证。我们也不一定需要等到token
自动失效,token
有撤回的操作,通过token revocataion
可以使一个特定的token
或是一组有相同认证的token
无效。
3. 可扩展性
Token
能够创建与其它程序共享权限的程序。例如,能将一个随便的社交帐号和自己的大号(Fackbook或是Twitter)联系起来。当通过服务登录Twitter(我们将这个过程Buffer)时,我们可以将这些Buffer附到Twitter的数据流上(we are allowing Buffer to post to our Twitter stream)。 使用token
时,可以提供可选的权限给第三方应用程序。当用户想让另一个应用程序访问它们的数据,我们可以通过建立自己的API,得出特殊权限的tokens。
4. 多平台跨域
CORS(跨域资源共享),对应用程序和服务进行扩展的时候,需要介入各种各种的设备和应用程序。
怎么使用Token
在web程序中,Token
应用很广泛,这里只介绍用于前后端分离技术。
Token
经常作为一种校验的标识,自身也能携带一些信息,用于网络通信之间。
Token
是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token
证明自己的合法地位。如果这个 Token
在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌。 简单token
的组成;uid
(用户唯一的身份标识)、time
(当前时间的时间戳)、sign
(签名,token
的前几位以哈希算法压缩成的一定长度的十六进制字符串。为防止token
泄露)。 使用token
机制的身份验证方法,在服务器端不需要存储用户的登录记录。
大概的流程:
客户端使用用户名和密码请求登录。
服务端收到请求,验证用户名和密码。验证成功后,服务端会生成一个token
,然后把这个token
发送给客户端。客户端收到token
后把它存储起来,可以放在cookie
或者Local Storage(本地存储)
里。
客户端每次向服务端发送请求的时候都需要带上服务端发给的 Token
。
服务端收到请求,然后去验证客户端请求里面带着Token
,如果验证成功,就向客户端返回请求的数据。
一般情况我们在使用token
作为验证机制时都会使用一个叫JWT技术(json web token
)下面我们就来讲讲JWT
JWT是什么
Json Web Token(JWT)
它目前是最流行的跨域身份验证解决方案 。是一个标准,借助JSON
格式数据作为WEB应用请求中的令牌,进行数据的自包含设计,实现各方安全的信息传输,在数据传输过程中还可以对数据进行加密,签名等相关处理。同时JWT
也是目前最流行的跨域身份验证解决方案(其官方网址为:https://jwt.io/
)。可以非常方便的在分布式系统中实现用户身份认证。
JWT组成

JWT Header(JWT 头部)
JWT Payload(JWT 有效载荷)
JWT signature(JWT 签名)
JWT 组成部分
一个JWT实际上就是一个字符串,它由三部分组成:头部(Header)、载荷(Payload)与签名(signature)
Header
{"typ":"Bearer","alg":"HS256"}
在rest_framework_simplejwt
默认的请求头为Bearer
使用的加密算法为默认HS256 这个json
中的typ
属性,用来标识整个token
字符串是一个JWT字符串;它的alg
属性,用来说明这个JWT签发的时候所使用的签名和摘要算法
typ
跟alg
属性的全称其实是type
跟algorithm
,分别是类型跟算法的意思。之所以都用三个字母来表示,也是基于JWT最终字符串大小的考虑同时也是跟JWT这个名称保持一致,这样就都是三个字符了…typ
跟alg
是JWT中标准中规定的属性名称
Payload(负荷)
{"username": "Testng","role": "tester","expire": "2022-07-15 10:00:00"}
payload
用来承载要传递的数据,它的json结构实际上是对JWT要传递的数据的一组声明,这些声明被JWT标准称为claims
,它的一个“属性值对”其实就是一个claim
(要求),每一个claim
的都代表特定的含义和作用。
一般用于记录些用户基本信息如username,id等
signature
签名是把header
和payload
对应的json
结构进行base64url
编码之后得到的两个串用英文句点号拼接起来,然后根据header
里面alg
指定的签名算法生成出来的 算法不同,签名结果不同。 以alg: HS256
为例来说明前面的签名如何来得到 按照前面alg
可用值的说明,HS256
其实包含的是两种算法:HMAC
算法和SHA256
算法,前者用于生成摘要,后者用于对摘要进行数字签名。这两个算法也可以用HMACSHA256
来统称
JWT的工作原理
是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户
示例如下:
第一步:前端用户进行登录了,输入了用户名及密码后端通过校验用户名密码是否正确如果正确将记录用户的基本信息如:
{"username": "Testng","role": "tester","expire": "2022-07-15 10:00:00"}
这个expire
时间可以自行设置用于token
的过期时间,不过一般这个时间必不需要自行去添加,如Python
的simplejwt
库可以结合django进行做jwt验证仅仅需要在settings.py中配置JWT相关的配置即可
# 修改默认鉴权用户模型
AUTH_USER_MODEL = "user.User"
# 设置认证器
AUTHENTICATION_BACKENDS = ('common.authenticationclass.UserTokenAuth',)
# JWT配置 里面具体配置可以参考文档
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=1), # 配置过期时间
'REFRESH_TOKEN_LIFETIME': timedelta(days=2),
# 修改认证的请求头类型
'AUTH_HEADER_TYPES': ('Jnote',),
# 修改header请求参数名称
'AUTH_HEADER_NAME': 'HTTP_TOKEN',
}
第二步:为了防止用户篡改数据,通过对Token Header+Token Payload + Token Signature进行加密签名并生成一串加密过由三个部分组成的字符串,一个完整的token是下面这样的。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjU3OTM2NTc5LCJqdGkiOiI2ZDU1MTU3NjZkN2U0ODA5ODY4NzAxNWJjYmYyY2M3OSIsInVzZXJfaWQiOiIxMiJ9.A6LVGbmFGDDO1zVNNfl9gjwg6DABxX1rXjiPdoqLafg
第三步:生成完成token
后通过登录接口返回json
数据如下,token
字段即是用户验证成功后后端生成的用户(凭证):
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjU3OTM2NTc5LCJqdGkiOiI2ZDU1MTU3NjZkN2U0ODA5ODY4NzAxNWJjYmYyY2M3OSIsInVzZXJfaWQiOiIxMiJ9.A6LVGbmFGDDO1zVNNfl9gjwg6DABxX1rXjiPdoqLafg",
"id": "12",
"userInfo": {
"username": "admin",
"nickname": "25252",
"phone": "",
"email": "123456@163.com",
"isSuperuser": true,
"isStaff": true
}
}
第四步:前端为了后续在请求接口时都携带token
进行身份验证都会将其token
保存到浏览器的Local Storage
中以便后续能直接使用
第五步:如果用户还需请求其他接口时每次请求接口都会自动携带(前端需要做相应的处理才能自动携带)Token
给到后端进行验证,验证通过后则向客户端返回对应的接口数据
PS:如果需要断开会话操作需要二步操作即可结果会话(浏览器直接清空Local Storage
中的token
)清空后请求接口时不会携带token
值,token
会找不到。或者等待token
失效则无法再次通过原本的token
进行验证了。

JWT 解决什么问题(优点,特点)
解决了对服务器的资源开销问题,如session
每次请求都需要访问服务端数据库大大提升了服务器资源消耗,而token
的存在则可对该问题进行解决,JWT的精髓在于:“去中心化”,数据是保存在客户端的,不用访问数据库
解决了对跨域请求的限制目前大部分都采用了前后端分离架构,前端界面不由服务器端进行渲染因此可能产生跨域及跨平台(移动设备等)请求服务器等问题
信息泄露及信息篡改,cookie
及session
都容易被任何不法分子进行劫持并篡改,JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证,不过也不能说有了token就能绝对安全仅仅是目前针对信息篡改增加了难度而已。
作者介绍