前端认证方式
认证:Cookie、Session、Token、JWT(JSON Web Token)
1. Cookie
- 弥补HTTP没有状态的不足(无法判断两次HTTP请求是否来源于同一个用户)
- 存储在客户端中
- 不可跨域(依靠域名区分)
1 |
|
2. Session
- 弥补HTTP没有状态的不足(无法判断两次HTTP请求是否来源于同一个用户)
- 存储在服务端中
- 基于Cookie实现(SessionId)
请求流程
- 用户第一次请求服务器的时候,服务端会根据用户输入的信息生成对应的Session
- 服务端在本次请求返回Session对应的SessionId给客户端(Set-Cookie)
- 浏览器会将SessionId存入Cookie中,同时Cookie会记录SessionId对应的Domain
- 用户第二次访问服务器的时候,请求将自动将Cookie中的SessionId发送到服务端上,服务端根据该SessionId查找对应的用户信息,若找不到,说明Session不存在或者已过期,要求用户重新输入信息。
Session的一个使用场景
- Nginx负载均衡的时候:
- 登录在服务器1,请求个人信息在服务器2,那么登录后请求个人信息又需要重新登录。
- 用户将商品a加入购物车,而这个请求发送到服务器1,又将商品b加入购物车,这个请求发送到服务器2,最后订单结算,请求发送到服务器1,那么付款的时候,能看到的就只剩下商品a。
使用Session的时候需要考虑的问题
- Session存储在服务器中的时候,如果用户使用量较大,需要定期清理过期的
Session
- 网站使用集群部署的时候,会遇到多台服务器共享
Session
的问题(即上面所述)- 多个服务器共享Session,还需要考虑
Cookie
跨域的问题SessionId
一般存储在Cookie
中,但是如果浏览器禁止Cookie
或不支持Cookie
,一般把SessionId跟在URL参数后面重写URL(Session
不一定需要Cookie
)- 移动端对
Cookie
的支持不是很好,Session
一般依赖于Cookie
,所以一般移动端应用Token
关闭浏览器后Session就永远消失了吗
不正确的。是因为大部分
Session
依赖于Cookie
实现SessionId
的保存,而关闭浏览器这个Cookie
可能消失,这样再次链接服务器就会找不到原来的Session
。如果服务器设置的Cookie
通过硬盘或者其他手段改写HTTP
请求头,把原来的SessionId
发送给服务器,仍然能打开原来的Session
。 因为关闭浏览器并不会导致
Session
被删除,所以服务器才会给Session
设置一个过期的时间,当Session
到期后,服务器才会删除Session
节省空间。Session与Cookie
- 安全性:Session存在服务端更加安全,Cookie设置HttpOnly可以一定程度避免XSS攻击
- 存取值类型:Cookie只支持字符串类型,Session可以为任意类型
- 有效期:Cookie可以设置为长时间保存,Session一般在客户端关闭或者设定的较短期限到期
- 存储大小与个数限制:Cookie一般单个限制为4KB,限定个数与浏览器有关;而Session一般不存在限制,但是如果访问较多的话会给服务端带来性能影响
3. Token
一种简单的生成Token:
uuid(用户唯一标识)+时间戳+sign(hash处理token前几位生成的十六进制字符串)
特点:
- 服务端无状态化,可扩展性良好
- 支持移动端设备
- 安全
- token完全由应用管理,可以避开同源策略
3.1 Access Token
- 客户端发送用户名密码等验证信息
- 服务端收到请求后进行验证,验证通过后,根据登录凭证加密为Access Token后与数据一起返回给客户端
- 客户端收到Token后,一般存储到localStorage中
- 客户端再次发起请求时,把Token放在请求头中
- 服务端收到请求后,查询数据库信息验证Token,验证成功后返回数据
3.2 Refresh Token
AccessToken存在过期时间(服务端存在数据库内),当Access Token过期后,用户需要重新输入验证信息。为了方便用户,还有一个Refresh Token。
- 客户端发送用户名密码等验证信息
- 服务端收到请求后进行验证,验证通过后,根据登录凭证加密为AccessToken与RefreshToken(RefreshToken过期时限>AccessToken)后与数据一起返回给客户端
- 客户端收到Token后存放在本地(一般为localStorage)
- 当客户端发送请求的时候将Access Token传输到服务端,服务端进行校验与过期检查,如果没有过期则返回数据
- 如果AccessToken过期,服务端向客户端发送过期错误,此时客户端将RefreshToken发送到服务端进行检验
- 如果RefreshToken未过期,则发送新的Access Token与 Refresh Token给客户端;否则,客户端需要重新输入登录验证信息
Token与Session
- Session用于保持会话,使服务端状态化;而Token是访问资源(API)的一个凭证令牌,会使服务端无状态化
- Token的安全性比Session要好,每个请求都会对签名解密,能够防止监听以及重复攻击,而Session必须依赖链路层保障通讯安全
- 二者不冲突,可以同时存在
为什么从Cookie转到Token
原因在于之前,
JQuery
前端时代是前后端不分离的,后端在模板渲染前会判断路由是否有权限来决定是否跳转。登录的时候,后端只需要设置SetCookie
这个响应头,Cookie
就能够存储起来并且自动发送。 但是现在的前后端分离,通常需要进行跨域操作,此时
Cookie
就有更多的限制了,因此我们更愿意使用手动管理权限的Token方式。
4. JWT
JWT原理是在服务器认证后,生成一个JSON对象,发送给用户,这个JSON对象包含用户的信息,之后用户每次发送请求的时候,都需要携带上JWT,为了防止数据被篡改,服务器在生成对象的时候,会加上签名。
4.1 JWT的数据结构
Header.Payload.Signature
三部分用.链接
Header
1
2
3
4{
"alg": "HS256",
"typ": "JWT"
}alg
表示签名的算法HMAC SHA256
,typ
表示该token的类型,JWT
统一为JWT
。Payload
1
2
3
4
5
6
7
8
9{
"iss (issuer)":"签发人"
"exp (expiration time)":"过期时间"
"sub (subject)":"主题"
"aud (audience)":"受众"
"nbf (Not Before)":"生效时间"
"iat (Issued At)":"签发时间"
"jti (JWT ID)":"编号"
}除了上面7个官方字段外,还可以定义一些私有字段。
Signature
指定密钥(secret)后,按下面的公式生成。
signature = HMACSHA256(base64UrlEncode(Header)+"."+(base64UrlEncode(Payload), secret)
BASE64URL算法
由于
JWT
作为令牌可能需要放在URL中,因为+
、/
、=
在URL中有特殊的含义,所以要被替换掉,具体算法:=
省略、+
换位-
,/
替换为_
。
4.2 JWT的使用
当客户端收到服务端返回的
JWT
的时候,可以存储在Cookie
或localStorage
中,在之后的每一次请求都需要带上JWT
。当JWT
在Cookie
中的时候将自动发送(不允许跨域),所以一般会放在HTTP的请求头Authorization
中。一种使用方法:
Authorization: Bearer + 'JWT'
(调用OpenAI API的时候就是将api_key放在请求头中)另一种:跨域的时候,
JWT
放在POST
请求的BODY中。
4.3 JWT的特点
JWT
默认不加密,但是可以生成原始Token
后,使用密钥进行加密。JWT
不加密的时候,由于里面的数据是可以被解码的,可以被直接获取,所以不应该在JWT
内写入秘密数据,并且永远使用强密码哈希算法处理密码。JWT
除了用于认证外,也可以用于信息的交换,可以减少数据库的查询次数。JWT
的最大缺点在于,服务端并不会保存Session
信息,因此无法在过程中废除Token
或者更改JWT
的权限。就是说,JWT
一旦签发,在到期前都始终有效,除非服务器部署额外的逻辑。JWT
本身包含了认证信息,如果泄露,那么任何人都可以获取该令牌的权限。为了减少盗用,JWT
的有效期一般被设置的很短,对于一些重要的信息,使用的时候应该再次对用户认证。- 为了减少盗用,
JWT
不应该使用HTTP
传输,而是应该使用HTTPS
。
TOKEN与JWT的区别
- 相同:
- 都是访问资源的令牌
- 都可以记录用户的信息
- 都使服务器无状态化
- 都是只有验证成功后,客户端才能访问服务端上的受保护的资源
- 不同:
- Token:服务端验证客户端发送的Token的时候,还需要查询数据库获取用户的信息,并且验证Token是否有效。
- JWT:Payload与Token加密后存储在客户端中,服务端只需要使用密钥解密并检验(JWT自己实现)即可,不需要或较少查询数据库,因为JWT包含了用户信息与加密的数据。