FastAPI从入门到实战(8)——一文弄懂Cookie、Session、Token与JWT
看到标题应该也能看出来本文讲的就是前端鉴权相关的内容了,鉴权也就是身份认证,指验证用户是否有系统的访问权限,只要是web开发,这部分内容就是不可能不学的,很多面试也必问,所以本文就针对此主题详细记录一下其常见的几种方式。
HTTP状态
HTTP 是无状态的。也就是说,HTTP 请求方和响应方间无法维护状态,都是一次性的,它不知道前后的请求都发生了什么。但有的场景下,我们需要维护状态。最典型的,一个用户登陆微博,发布、关注、评论,都应是在登录后的用户状态下的。这种情况下,各种鉴权就应运而生了。
在此之前还了解一点基本的概念:
- 认证
认证就是验证当前用户的身份,比如用户名密码登录认证、邮箱发送登录链接、手机号验证码认证。认证当前用户就是本人,不是机器。
- 授权
用户授予第三方应用访问用户某些资源的权限,客户端授予服务器端应用的一些权限,最常见的就是安装手机应用时,APP询问用户是否授予媒体访问权限。
- 凭证
实现认证和授权就需要有东西来标识访问者的身份,这个标识就是凭证,如银行,去大厅逛逛,不需要办业务就不需要认证,也就不需要凭证,逛逛就走了;但是要办业务的话就需要提供身份证明和银行卡信息,身份证和银行卡就对应到了凭证,拿着凭证就可以在银行进行认证办业务了。
Cookie
参考链接:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies
Cookie是服务器发送到用户浏览器并保存在本地的一些数据,浏览器会将cookie进行存储,在下次向浏览器发送请求的时候,会一并将cookie也发送到服务器上,这样服务器就能知道这两个请求是不是来自同一浏览器了。
Cookie主要在以下三个方面发挥的很淋漓尽致:
- 会话状态管理:
比如最最常见的用户登录状态,客户端登录的时候发送一个请求,服务器接收到请求后进行验证,验证成功发送一个请求成功和setcookie的响应,客户端收到响应后完成登录的同时进行cookie的存储,下一次进行页面刷新、跳转等操作的时候,会将cookie一并发送给服务器。服务器接收cookie并进行验证,验证成功便直接发出响应,验证失败跳转到登录页面重新登录。
- 个性化设置:
比如用户自定义设置、主题设置,与上面同样的道理,进行设置和验证即可保证有效期内的用户自定义设置。
- 浏览器行为跟踪:
由于http都是无状态的,所以服务器无法判断当前请求的发送者是不是同一个人,服务器与浏览器进行会话,服务器就能知道浏览器的行为情况,进一步便能进行追踪。
Cookie应用
这一部分的内容参见下一节,直接通过代码的形式进行展示与讲解。
Session
客户端发送请求,服务器会为了这个请求创建一个内存空间,这个对象就是session对象,在创建session的同时,会生成一个sessionid,并通过Set-Cookie:JSESSIONID=XXXXXXX
的命令发送一个cookie的设置,客户端收到响应便在浏览器设置了一个cookie信息,cookie结束的时候,这一次会话也就结束了。
接下来客户端的所有请求,请求头都会带上携带有sessionid的cookie信息
,然后服务器通过读取请求头中的cookie信息回去sessionid,进一步进行session的验证,进行会话的继续。
- session的缺点
session是存储在服务器上的,如果是分布式的架构,那么很可能会面临失效的问题。
Session应用
后面实战项目中应该会涉及,这里不进行试验了。
Cookie和Session的区别
- 安全性:
Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。
- 存取值的类型不同:
Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。
- 有效期不同:
Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
- 存储大小不同:
单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。
Token
Token 的中文意思是"令牌"。主要用来身份验证。
比传统的身份验证方法,Token 有扩展性强,安全性高的特点,非常适合用在 Web 应用或者移动应用上。它是服务器生成的字符串,可以作为客户端的一个凭证。
session的设置对于中小型的项目来讲,是比较合适的,但是一旦流量大起来,服务器要保存的数据就太多了,对服务器的消耗是巨大的。
针对这个问题,就想能不能不在服务器中进行保存了,去客户端保存,比如用户登录了系统,服务器就给客户端发送一个token,里面包含了用户的id,下一次请求的时候,把这个token又带回来,但是这样还是有问题,这个是容易伪造的,所以就需要做一个加密,别人就无法伪造了。
这个token服务器不保存,当用户把这个token 给我发过来的时候,我再用同样的算法和密钥,对数据计算一次签名, 和token 中的签名做个比较, 如果相同, 我服务器就知道用户已经登录过了,并且可以直接取到用户的id , 如果不相同, 数据部分肯定被人篡改过,服务器就返回验证失败的错误。
- 和cookie的不同
最开始我看到这里的时候,就已经迷糊了,生成数据发送到客户端,客户端每次请求都会发送给服务器,这和cookie有什么区别呢?
但是细想一下就知道很不一样了,cookie是一个数据块,可以保存很多键值对数据,token是一个令牌,这个令牌只保存验证需要用的数据。token可以放在url中、header中、请求体中,也可以放在cookie中,本质上就是一条数据。
- 总结一下验证的流程
- 客户端使用用户名和密码请求登录
- 服务端收到请求,验证用户名和密码
- 验证成功后,服务端会签发一个token,再把这个token返回给客户端
- 客户端收到token后可以把它存储起来,比如放到cookie中
- 客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带
- 服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据
Token对比Session
session和token并不矛盾,session是一种存储机制,目的是存储登录信息;token是为了提供认证和授权,加密的数据是用户信息,服务器拿到需要和数据库对比,进行用户认证。
- 支持跨域访问:
cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题
- 无状态:
token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力
- 更适用CDN:
可以通过内容分发网络请求服务端的所有资料
- 更适用于移动端:
当客户端是非浏览器平台时,cookie是不支持的,采用token认证方式会简单很多
- 无需考虑CSRF:
由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御
JWT
JWT是token的一种实现方式,其全称是JSON Web Token
。官网:https://jwt.io/
JWT将用户信息保存在一个Json字符串中
,然后进行编码就得到了一个JWT token,而且JWT带有签名信息,接收后可以进行校验,所以可以用于在各方之间安全地将信息作为Json对象传输。
- JWT的认证流程:
- 前端将
用户信息通过表单发送到后端
- 后端拿到信息和数据库进行比对,核验成功后,将包含
用户信息的数据作为JWT的主要载荷
,然后结合JWT Header进行编码后进行签名,就得到了一个JWT Token- 后端将
JWT Token字符串作为登录成功的结果返回给前端
。前端可以将返回的结果进行存储,退出浏览器的时候删除即可- 前端发送请求的时候
把JWT Token放置到HTTP请求头中的Authorization属性中
(解决XSS和XSRF的问题)- 后端
检查前端传过来的JWT Token后进行验证
- 验证通过后,后端解析JWT Token中包含的信息,进行进一步的处理
JWT结构
JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)
。在传输的时候,会将JWT的3部分分别进行Base64编码后用.
进行连接形成最终传输的字符串:
Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
Header
JWT头
是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存:
{
"alg": "HS256",
"typ": "JWT"
}
Payload
有效载荷
部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择:
iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT
除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中:
{
"sub": "1234567890",
"name": "MinChess",
"admin": true
}
Signature
签名哈希
部分是对上面两部分数据签名,需要使用base64编码后的header和payload
数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象
JWT的种类
JWT(JSON Web Token)
指的是一种规范,这种规范允许使用JWT在两个组织之间传递安全可靠的信息,JWT的具体实现可以分为以下几种:
-
nonsecure JWT:未经过签名,不安全的JWT
header中没有指定签名算法,只有
alg
和typ
,就更没有签名了。 -
JWS:经过签名的JWT
JWS就是在header中指定了算法,最后也有对应签名的token。
-
JWE:payload部分经过加密的JWT
JWE就是载荷数据加密过的JWT,这样不能直接解析出载荷的明文数据
JWT总结
JWT就是Token的一种规范,三个部分,头+载荷+签名,头中声明类型、加密算法等,载荷装载主要数据,签名由算法+头+载荷+密钥组成,token也是三个部分,前面两个部分是明文的,前端可以直接进行解析获取到有效数据,所以不能放敏感数据;
最开始没弄懂的时候,到这里还不知道为什么要加密,加密了也是明文,加密有什么意义,后来弄明白了才后知后觉,加密只是保证前面两部分的数据不会被修改,修改了就返回错误。
验证的过程是,服务器拿到数据,对header和payload进行解码,进一步对解码的结果结合密钥进行一次签名,然后将结果和客户端返回回来的签名对比,对比不同即返回错误。
个人感觉记录的还是相对清晰的,具体的实战都在后面进一步进行操作。
- 感谢你赐予我前进的力量