后端用户认证系统构建详解
后端用户认证系统构建详解
一年没更新博客,发现学习到的东西还是要写下来梳理一下,要不很难理解透彻,有遗忘重新学效率也很低
常用的两种用户认证方式
Ⅰ 基于 Session 的身份验证
Session 本身是一个抽象的概念,有多种实现,这里就不讲复杂的多节点服务器下 session 身份验证的实现了,讲最普遍的 Session+Cookie
基于 Session 的身份验证,是最传统的验证方式,工作流程如下:
- 用户首次登陆成功时,后端生成一个 sessionId,储存在缓存中(还储存一些用户信息,以 sessionId 为 key)(比如用 Redis),设置一个过期时间,然后再把这个 sessionId 返回给用户的浏览器,浏览器再把 sessionId 储存在 cookie 中。
-
再次访问时,服务器会取出 HTTP 请求头内 cookie 中的 sessionId ,然后在缓存中寻找该字串,如果存在,那么该用户已登录(A);如果不存在,那么该用户的登陆已经过期(B)。如果根本没收到 session,说明还没登陆过(C)。
对情况A:验证该用户权限,决定是否放行
对情况B、C:均跳转到登陆界面
- 客户端调用后端 logout API时,清除缓存中 sessionId key下所有数据
优点
提供了用户认证功能,使无状态的 HTTP 请求有状态化
缺点
本地储存 cookie 不安全,session 储存占用服务器资源
跨域请求不便/不安全,记个 TODO,下次写写 CORS/CSRF
Ⅱ 基于 Token 的身份验证
基于 Token 的身份验证,近几年越来越常见,特点是服务器不用储存登陆状态信息
基于 Token 的身份验证,工作流程如下:
- 用户首次登陆成功时,后端生成一个 token,不在后端储存,把 token 发送给用户,用户拿到 token 后将 token 储存在 localStorage(不储存在 cookie,因为不安全)
- 再次访问时,服务器会取出 HTTP 请求头内或是 POST 请求体内的 token,对 token 进行解码、校验,判断 token 是否有效,如果有效,那么该用户已登陆;如果无效,那么该用户的登陆已经过期,或者该 token 是伪造的(B)。如果根本没收到 token,说明还没登陆过(C)。
对情况A:验证该用户权限,决定是否放行
对情况B、C:均跳转到登录界面
Token 是什么?
上述验证的可行性依赖于 token 的结构,token 结构分为数据体和签名两个部分,数据体往往是经 Base64 等方式编码生成的字串,其中包含用户名、token 过期时间等信息;签名往往是通过 RS256 等非对称加密算法对数据体信息进行加密得到的。
签名的特点是,只有拥有私钥才可以生成签名,有公钥才可以对签名进行解密(从数学上是严格的,暴力破解的时间花费极大),而私钥会被严密保管在签名者处,公钥则由签名者公开,人人都可以拿到。
上述特点导致一个情况,就是签名无法伪造,但是人人都可以获得签名中的数据,所以有了签名,就可以确认数据体是否被人篡改了
也有使用 SHA256 等 Hash 算法生成签名的,再次对数据体信息进行 Hash 生成的新签名,然后与传递来的签名进行比对,相同则说明没有被篡改,但是这样只有后端可以校验签名,因为只有后端牢牢保管 Hash 的生成密钥,才能确保签名是后端生成而非伪造的。
优点
节省服务器内存,不需要 Session 模式下的大量内存
无 Cookie 不会因此受到 CSRF 攻击
缺点
大量的签名验证 CPU 开销略大
无法主动销毁 token,需要等 token 过期
token 易被劫持攻击
JSON Web Token⭐
JWT的官方简介:
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
一个 JWT 标准 token 包含三个部分——header、payload、signature,其中 header 和 payload 使用 Base64 编码,signature 使用 header 中注明的算法生成,未编码的 header 和 payload 格式如下:
- header
header 只有 alg 和 typ 两个 key
{
'alg': "RS256",
'typ': "JWT"
}
alg 注明了 signature 的生成算法,typ 注明了该 token 的类型(JWT)
- payload
payload 的 key 分为三类,分别是 Registered claims、Public claims、Private claims
Registered claims 是 JWT 标准保留的 key,用于 JWT 的验证,这些 key 是可选项
Public claims 和 Private claims 是签发者自定义的,Public claims 的特点是 value 应当都是 唯一标识符 类(Collision resistant),而 Private claims 的特点是允许重复(Not collision resistant)
以上只是个分类,简单记住:payload 除了 Registered claims 外,都是自定义的
{
//以下是 Registered claims
"iss": "https://jnn.icu/",
"aud": "https://pc.jnn.icu/",
"sub": "1035522103",
"exp": "1620108643",
//上面四个是最常见的 Registered claims
"nbf": "1610108643",
"iat": "1600108643",
"jti": "dfd1aa97-6d8d-4575-a0fe-34b96de2bfad",
//以下是 Public claims
"uuid": "37729e92-0bf7-426a-97e4-e4558a1c848b",
//以下是 Private claims
"name": "jnn",
"admin": true
}
Registered claims :
iss(Issuser):代表这个JWT的签发主体;
sub(Subject):代表这个JWT的主体,即它的所有人;
aud(Audience):代表这个JWT的接收对象;
exp(Expiration time):是一个时间戳,代表这个JWT的过期时间;
nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的;
iat(Issued at):是一个时间戳,代表这个JWT的签发时间;
jti(JWT ID):是JWT的唯一标识。
Session 身份验证的实现
Django 提供了一套拓展性很强的用户认证系统,很香
待补充😅
JWT 身份验证的实现
待补充😅