登录认证jwt
这几天处理身份逻辑的时候,又用到了一些认证授权知识。
Cookie
和 Session
都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。
维基百科是这样定义 Cookie
的:
Cookies
是某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密)。
简单来说:Cookie
存放在客户端,一般用来保存用户信息。
Cookie 和 Session 有什么区别?
Session
的主要作用就是通过服务端记录用户的状态。 典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session
之后就可以标识这个用户并且跟踪这个用户了。
Cookie
数据保存在客户端(浏览器端),Session
数据保存在服务器端。相对来说 Session
安全性更高。如果使用 Cookie
的一些敏感信息不要写入 Cookie
中,最好能将 Cookie
信息加密然后使用到的时候再去服务器端解密。
用户认证以般流程是下面这样。
- 用户向服务器发送用户名、密码、验证码用于登陆系统。
- 服务器验证通过后,服务器为用户创建一个
Session
,并将Session
信息存储起来。 - 服务器向用户返回一个
SessionID
,写入用户的Cookie
。 - 当用户保持登录状态时,
Cookie
将与每个后续请求一起被发送出去。 - 服务器可以将存储在
Cookie
上的SessionID
与存储在内存中或者数据库中的Session
信息进行比较,以验证用户的身份,返回给用户客户端响应信息的时候会附带用户当前的状态。
使用 Session
的时候需要注意下面几个点:
- 依赖
Session
的关键业务一定要确保客户端开启了Cookie
。 - 注意
Session
的过期时间。
一般是通过 Cookie
来保存 SessionID
,假如你使用了 Cookie
保存 SessionID
的方案的话, 如果客户端禁用了 Cookie
,那么 Session
就无法正常工作。
但是,并不是没有 Cookie
之后就不能用 Session
了,比如你可以将 SessionID
放在请求的 url
里面https://javaguide.cn/?Session_id=xxx
。这种方案的话可行,但是安全性和用户体验感降低。当然,为了安全你也可以对 SessionID
进行一次加密之后再传入后端。
为什么Cookie无法防止CSRF攻击,而Token可以
chatgpt的回答如下
-
存储位置:
- Cookie 存储在浏览器中,并在每次请求中自动发送到相关的服务器。攻击者可以利用用户的浏览器自动发送 Cookie 的特性来执行 CSRF 攻击。
- Token(例如 CSRF Token)通常作为一个额外的参数存储在表单中或者通过 HTTP 头部发送到服务器。攻击者不能在不知情的情况下访问到 Token,因此很难模拟合法用户的请求。
-
跨站点保护:
- Cookie 不具有防止跨站点请求的特性。当用户在一个网站上登录后,在没有适当保护的情况下,攻击者可以在另一个网站上伪造请求,浏览器会自动发送相应的 Cookie。
- Token 可以有效地防止 CSRF 攻击,因为攻击者无法获取到 Token 的值,从而无法伪造带有正确 Token 的请求。
-
可控性:
- Cookie 的发送是由浏览器自动处理的,用户无法控制。因此,即使用户非常谨慎,Cookie 也容易受到 CSRF 攻击。
- Token 则由开发者明确地插入到请求中,攻击者无法自动获取 Token,除非攻击者能够以其他方式获取到该 Token。
来看下重点JWT
JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制。 从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的 JSON 结构的 Token
使用 JWT 认证可以有效避免 CSRF 攻击,因为 JWT 一般是存在在 localStorage 中,使用 JWT 进行身份验证的过程中是不会涉及到 Cookie 的。
下面是 RFC 7519对 JWT 做的较为正式的定义。
JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted. ——JSON Web Token (JWT)
刚刚提到了用户认证的一般流程,这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。
举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?
- 一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。
- 另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。
JWT 由哪些部分组成?
JWT 本质上就是一组字串,通过(.
)切分成三个为 Base64 编码的部分:
- Header : 描述 JWT 的元数据,定义了生成签名的算法以及
Token
的类型。 - Payload : 用来存放实际需要传递的数据
- Signature(签名):服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。
JWT 通常是这样的:xxxxx.yyyyy.zzzzz
Header 和 Payload 都是 JSON 格式的数据,Signature 由 Payload、Header 和 Secret(密钥)通过特定的计算公式和加密算法得到。
Header
Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。
{
"alg": "HS256",
"typ": "JWT"
}
上面代码中,alg
属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ
属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT
。
最后,将上面的 JSON 对象使用 Base64URL 算法(详见后文)转成字符串。
Payload
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。
- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。
这个 JSON 对象也要使用 Base64URL 算法转成字符串。
Signature
Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.
)分隔,就可以返回给用
JWT 的使用方式
客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。
此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization
字段里面。
Authorization: Bearer <token>
另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。
- 建议将 JWT 存放在 localStorage 中,放在 Cookie 中会有 CSRF 风险。
- 请求服务端并携带 JWT 的常见做法是将其放在 HTTP Header 的
Authorization
字段中(Authorization: Bearer Token
)。
JWT的优势
相比于 Session 认证的方式来说,使用 JWT 进行身份认证主要有下面 几个优势。
无状态
JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。
不过,也正是由于 JWT 的无状态,也导致了它最大的缺点:不可控!
就比如说,我们想要在 JWT 有效期内废弃一个 JWT 或者更改它的权限的话,并不会立即生效,通常需要等到有效期过后才可以。再比如说,当用户 Logout 的话,JWT 也还有效。除非,我们在后端增加额外的处理逻辑比如将失效的 JWT 存储起来,后端先验证 JWT 是否有效再进行处理。
有效避免了 CSRF 攻击
CSRF(Cross Site Request Forgery) 一般被翻译为 跨站请求伪造;CSRF 攻击需要依赖 Cookie ,Session 认证中 Cookie 中的 SessionID
是由浏览器发送到服务端的,只要发出请求,Cookie 就会被携带。借助这个特性,即使黑客无法获取你的 SessionID
,只要让你误点攻击链接,就可以达到攻击效果。
使用 JWT 进行身份验证不需要依赖 Cookie ,因此可以避免 CSRF 攻击。不过,这样也会存在 XSS 攻击的风险。为了避免 XSS 攻击,你可以选择将 JWT 存储在标记为httpOnly
的 Cookie 中
单点登录友好
使用 Session 进行身份认证的话,实现单点登录,需要我们把用户的 Session 信息保存在一台电脑上,并且还会遇到常见的 Cookie 跨域的问题。但是,使用 JWT 进行认证的话, JWT 被保存在客户端,不会存在这些问题。
注销登录等场景下 JWT 还有效
与之类似的具体相关场景有:
- 退出登录;
- 修改密码;
- 服务端修改了某个用户具有的权限或者角色;
- 用户的帐户被封禁/删除;
- 用户被服务端强制注销;
- 用户被踢下线
这个问题不存在于 Session 认证方式中,因为在 Session 认证方式中,遇到这种情况的话服务端删除对应的 Session 记录即可。但是,使用 JWT 认证的方式就不好解决了。我们也说过了,JWT 一旦派发出去,如果后端不增加其他逻辑的话,它在失效之前都是有效的。
目前在后端无非就是
JWT 的几个特点
(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。
(3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
(6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
2023-03-23 curl使用记录
2021-03-23 debug open files
2020-03-23 性能工具-io工具
2020-03-23 linux后台开发常用调试工具
2020-03-23 GDB的原理&& 使用
2020-03-23 可变参数以及stdcall