首页 > 其他分享 >JWT 没那么神秘,用它换掉 Session + Cookie 认证

JWT 没那么神秘,用它换掉 Session + Cookie 认证

时间:2024-07-22 14:24:58浏览次数:14  
标签:验证 JWT 认证 Token Session Cookie

本项目代码已开源,具体见fullstack-blog

数据库初始化脚本:关注公众号程序员白彬,回复关键词“博客数据库脚本”,即可获取。

背景引入

在《前端轻松拿捏!最简全栈登录认证和权限设计!》一文中,我们掌握了如何基于 Session + Cookie 实现一个基本的登录认证功能,这是一个经得起时间考验的方案。

基于 Session + Cookie 的认证,在用户登录认证时,简单做法是将 Session ID 作为 Token,接着将这个 Token 放到 Cookie 中,后续客户端发送请求时,服务端就可以从 Cookie 中取出 Token,验证用户身份。

实现上,我们可以将这个 Token 直接作为 user 表中的一个字段,在用户登录后直接写入数据库,后续验证都是根据 Session ID 查 user 表验明身份。

总的来说,Session + Cookie 的方案相当于是对每个 Token 都做了登记,每个 Token 都有对应的内存或存储系统来维护,有时还会考虑将 Token 写入到数据库中,这就是一种有状态的认证机制。当我们验证 Token 时,就需要从 Cookie 中取出口令,再去校验这个 Token 的有效性。

Session + Cookie 实现登录认证的方案,它易于实现、安全性较强,但是由于它的实现机制,也会受到一些制约。

比如 Cookie 的一些问题:

  • Cookie 受同源策略限制,在跨域应用场景下使用会遇到一些困难。
  • 依赖于客户端对 Cookie 的支持,如果用户禁用 Cookie,这套体系将无法正常工作。
  • Cookie 还有有名的 CSRF 钓鱼链接问题,不过现在浏览器对 Cookie 也有了一些安全措施应对。

Session 也会有一些问题存在:

  • 每个用户的 Session 数据都需要存储在服务器上,当用户量大时会占用大量服务器内存,增加服务器负载。当然很多 Session 的库实现也支持外部存储,比如提供 Redis 连接,但是都对内存提出了更高要求。
  • 分布式场景下,要考虑引入较为复杂的分布式 Session 方案,可扩展性一般。

那么有没有备选的认证方案呢?我们不妨来看一看JWT

认识 JWT

image-20240715172203552

JWT 全名是 JSON Web Tokens。

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

JWT 有一个很重要的特点是 self-contained,也就是说它不依赖于外部系统就能正常工作,生成 Token 和验签的过程都基于自身的算法完成。

JWT格式解释

JWT 的格式是这样的,也就是由头部、载荷、签名三部分组成:

Header.Payload.Signature

其中头部包含标准的两个字段:

  • typ: 其实是 type 的简写,代表 Token 的类型,一般固定为JWT
  • alg: 采用的签名算法,通常有HMAC SHA256, RSA
{
  "alg": "HS256",
  "typ": "JWT"
}

HMAC SHA256 采用对称密钥,RSA 则采用非对称加密,这也说明了 JWT 并不强制采用对称加密或者非对称加密。

在实际使用场景中,RSA 适合分布式系统,可以在认证服务中使用私钥签名和下发 JWT,其他各个微服务可以使用公钥验签,这既保证了签发 Token 的安全性,也避免了各个微服务对认证服务的过度调用。

头部 json 进行Base64Url编码后,作为Header.Payload.Signature中的 Header 部分。

而 Payload 则是由三类声明组成,分别是 Registered claims, Public claims, Private claims。

其中 Registered claims 是可选的标准化的,具体参考RFC7519#Registered Claim Names

Public claims 是公开的一些字段,建议大家使用时尽量往这上面凑,满足通用性和 interoperable。

Private claims 就是完全自定义的,意思就是在上面两大类 claims 中实在是找不到适合自己业务的,你就自己起个名字自己用。

整理好 Payload 字段后,也是进行Base64Url编码。

Signature 就很好理解了,签名嘛,假设采用 HMACSHA256 算法,就可以参考这个公式:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

相当于把前面两个 Base64Url 编码的内容用英文点号连接起来,使用密钥进行签名,签名的结果再进行Base64Url编码。

这样一来,我们就得到了三个Base64Url编码的结果,把这三个结果用点号连接起来就是一个标准的 JWT。下方就是一个示例。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9qZWN0IjoiZnVsbHN0YWNrLWJsb2ciLCJhdXRob3IiOiLnmb3lvawiLCJzdWIiOiJkZW1vIiwiaWF0IjoxNzIxMDM0ODMyLCJuYmYiOjE3MjEwMzQ4MzIsImV4cCI6MTcyMTEyMTIzMn0.g1rUb1rWBtV6mzbUtFxk4dBRfLFYQmwHP_eQtKyVfIw

寻找合适的库实现

理解了上面的数据结构后,就可以找具体的语言库来应用了,这个大家可以在 JWT 官方推荐的 Libraries 中找到,并且各个语言的实现都有。

image-20240715172050893

代码实现改造

接下来就是代码的改造了,主要考虑两处,一个是认证下发 Token 的地方,一个是验证 Token 的地方。

下发 Token

在本博客项目中,认证下发 Token 也就是在登录的实现里。

image-20240715173144715

在使用 Session 认证时,我们的思路是:验证了用户名和密码后,就将 Session ID 更新到 user 表中的 token 字段,同时把这个 token 设置到 Cookie 中。

而采用 JWT 时,我们的做法是:验证了用户名和密码后,采用jwt.sign签发 Token,并将 Token 返回给前端。

前端则将登录接口返回的 token 存起来,后续接口请求中在Authorization头部中携带 token。

验证 Token

权限验证我们是放在 BaseController 实现的,并且我们仅会对 authMap 中标识过的接口进行权限验证。

image-20240715174034070

对于需要验证权限的接口请求,在使用 Session 认证时,我们是判断请求 Cookie 中是否携带了 token,如果没有,直接返回“未授权”,如果携带了 token,再去查 user 表验证身份。

改为 JWT 后,我们的做法是:取出请求中的Authorization头部字段,进行 JWT 验签,如果通过,继续验证角色是否符合。

注意事项

以上实现仅供参考,如用于业务项目生产环境使用,应该还做更多考虑。

JWT 不应该存储经常变化的信息,假设你在一个管理系统中使用 JWT,通常来说,用户的权限信息可能随时会变化,我们不应该把权限信息放在 JWT 中。

如果你实在需要在 JWT 中放置一些可能会变化的信息,那么也不能放关键信息,并且尽量让 JWT 的有效期短一点。

JWT 也不应该存储敏感信息,你不应该把一些密码之类的信息放在 JWT 中。

标签:验证,JWT,认证,Token,Session,Cookie
From: https://blog.csdn.net/weixin_41196185/article/details/140599968

相关文章

  • 分布式环境下,如何实现 Session共享
    先了解一下为什么会出现这种session共享的解决方案?随着互联网公司的项目在微服务和分布式的环境下进行的搭建,导致一个项目可能分别部署在几个甚至很多的服务器集群下,此时就会出现一个问题:当用户进行一个session会话的时候,比如一个用户去登录项目,一般的大公司的项目都是有Nginx进......
  • 封禁 NetBIOS Session Service 和 SMB 服务(特别是旧版本的SMB)可能是出于安全性考虑。
    封禁NetBIOSSessionService和SMB服务(特别是旧版本的SMB)可能是出于安全性考虑。这两种服务在过去的实现中存在一些安全漏洞和风险,特别是在现代网络环境中,这些风险可能会被利用来进行攻击或者未经授权的访问。下面是一些常见的安全考虑:中间人攻击:未加密的NetBIOS和旧版本......
  • net core中使用jwt时,提示DenyAnonymousAuthorizationRequirement: Requires an authe
    客户端请求是401,控制台提示info:Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]Authorizationfailed.Theserequirementswerenotmet:DenyAnonymousAuthorizationRequirement:Requiresanauthenticateduser.翻遍了资料,也查不到原因,......
  • Cookie、Session、JWT在koa中的应用及实现原理
    Cookie、Session、JWT在koa中的应用及实现原理  目录Cookie重要属性实现原理cookie签名实现原理注意事项Session实现原理JWT使用方式组成实际应用实现原理前端存储方式cookiesessionlocalStoragesessionStoragetoken区别 CookieHTTP......
  • FastAPI登录实现(JWT)
    JWT(JSON Web Tokens)一、依赖库安装jwtpipinstalljwt==1.2.0python-jose用于生成和检验JWT令牌pipinstallpython-jose==3.2.0passlib用于处理哈希密码的包支持许多安全哈希算法以及配合算法使用的实用程序推荐的算法是 Bcryptpipinstallpasslib[Bcrypt]==1.7......
  • Python爬虫(5-10)-编解码、ajax的get请求、ajax的post请求、URLError/HTTPError、微博
    五、编解码(Unicode编码)(1)GET请求所提方法都在urllib.parse.路径下get请求的quote()方法(适用于只提交一两个参数值)url='http://www.baidu.com/baidu?ie=utf-8&wd='#对汉字进行unicode编码name=urllib.parse.quote('白敬亭')url+=nameget请求的urlencode()方法(适用于......
  • cookie方法封装
    方法封装:/***获取cookie*/exportfunctiongetCookie(sKey:string):string{if(!sKey){returnnull;}returndecodeURIComponent(document.cookie.replace(newRegExp("(?:(?:^|.*;)\\s*"+encodeURIComponent(sKey).replace(/[\-\.\+\*]/g,......
  • [BJDCTF2020]Cookie is so stable
    打开题目是三个页面Hint中有提示flag页面有个输入框抓包观察cookie发现多了一user就是回显内容然后猜测有模板注入漏洞就开始尝试'时代少年团队长乌萨奇的颜值一直被质疑'的文章内容如何判断对方的模板?常见模板有Smarty、Mako、Twig、Jinja2、Eval、Flask、Tornado、Go......
  • ChatGPT:为什么说 JWT 是无状态的,无法实现 Token 的作废,例如用户登出系统、修改密码等
    ChatGPT:为什么说JWT是无状态的,无法实现Token的作废,例如用户登出系统、修改密码等场景JWT(JSONWebToken)被称为无状态(stateless)是因为它本身不存储会话状态或会话数据在服务端。这意味着每个JWT包含了足够的信息来验证用户的身份和权限,而不需要在服务端存储任何关于......
  • JavaScript全解析——本地存储✔(localStorage~sessionStorage~cookie)
    ●就是浏览器给我们提供的可以让我们在浏览器上保存一些数据●常用的本地存储(localStorage~sessionStorage~cookie)1-localStorage=>特点:->长期存储,除非手动删除否则会一直保存在浏览器中清除缓存或者卸载浏览器也就没有了->可以跨页面通讯,也就是说在一个页面写下......