JWT详解
题记
本文主要是讲JWT的有关知识。
1.Session
Session的使用过程是怎么样的?
- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中;
- 服务器验证该用户名和密码,如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID;
- 服务器返回的响应报文的 Set-Cookie 首部字段包含了这个 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中;
- 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作。
2.Token
Token的使用过程是怎么样的?
- 客户端使用用户名和密码请求登录。
- 服务端收到请求,验证用户名和密码。
- 验证成功后,服务端会签发一个token,再把这个token返回给客户端。
- 客户端收到token后可以把它存储起来,比如放到cookie中。
- 客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带。
- 服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据。
Token对比session的优点:
- 无状态:Token是无状态的,而Session需要维护服务器端的状态信息。这使得使用Token更容易在分布式系统和负载均衡器中进行管理。
- 扩展性:由于没有状态信息,可以轻松地将Token集成到API、移动应用程序等不同的系统中。
- 安全性:Token可以提供更高的安全性,因为它可以避免CSRF攻击,并且可以通过签名和加密来保护数据的完整性和机密性。
- 性能:由于Session需要在服务器端维护状态信息,因此可能会影响性能。而Token可以在客户端进行验证和授权,从而减少了对服务器的负载。
3.什么是JWT
终于我们的主角JWT,登场了。JWT全称:JSON Web Token
,本质上就是一个字符串(它是Token的一种具体实现方式)。
JWT的认证流程:
- 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探。
- 后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串
- 后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的JWT Token即可。
- 前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)
- 后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等等。
- 验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果。
4.为什么要使用JWT
4.1传统Session的弊端
传统Session认证存在一些弊端,包括以下几个方面:
- 服务器端状态管理:使用Session需要在服务器端维护用户的状态信息,这可能会增加服务器的负担和复杂度。
- 分布式环境下的问题:由于Session依赖于服务器端状态,因此在分布式环境下可能需要使用一些特殊技术来解决会话跨节点的问题。
- 扩展性差:传统Session认证的扩展性较差。如果要将应用程序扩展到多个客户端或不同的系统中,必须维护每个客户端的状态信息。这使得Session认证在一些场景下难以扩展和管理。
- CSRF攻击:Session认证容易受到CSRF攻击,攻击者可以通过伪造请求来获取用户的会话信息。
- 跨域问题:由于Session是基于Cookie实现的,而Cookie在跨域请求时会受到浏览器的同源策略限制,可能会导致某些跨域场景下无法正常使用。
- 占用带宽:每次请求都要携带Session ID等相关信息,增加了请求头的大小,占用了带宽,并且可能会影响网络性能。
相比之下,JWT认证可以更好地满足现代Web应用程序的安全和可扩展性需求。它具有以下优点:
- 无状态:JWT是无状态的,服务器不需要在本地存储会话信息,因此可以更容易地实现分布式、跨域和负载均衡等场景下的身份验证。
- 可扩展性:由于JWT是基于标准化的JSON格式,因此可以轻松地将其集成到API、移动应用程序等不同的系统中。
- 安全性:JWT提供了签名和加密机制,以确保数据的完整性和机密性,避免了CSRF攻击和会话劫持等安全问题。
- 可定制性:JWT可以根据业务需求进行自定义,例如添加特定的声明、过期时间等元数据,使其更适合特定的应用场景。
注意这不是说JWT就没有缺点了,它也是有缺点的:
- 信息泄露:JWT中的信息是经过Base64编码的,如果密钥不安全或者遭到暴力破解,可能会导致信息泄露。
- 不可撤销性:由于JWT是无状态的,一旦JWT签发后,就无法撤销。如果需要撤销访问权限,则必须等待到JWT过期或将其加入黑名单中。
- 大小限制:由于JWT包含了所有相关信息,因此它的大小可能会比较大,这可能会导致网络传输和存储方面的性能损失。
- 安全更新问题:如果需要更新JWT中的某些信息,例如访问权限、过期时间等,需要重新签发一个新的JWT。但是,这可能会导致之前签发的JWT仍然有效,从而导致安全性问题。
综上所述,虽然JWT具有许多优点,但在具体应用时要注意其潜在的安全风险和实施的复杂性。在使用JWT时,需要合理设置过期时间、密钥长度和加密算法等参数,以确保其安全可靠性。
5.JWT结构
JWT(JSON Web Token)的结构通常由三个部分组成,分别是Header、Payload和Signature。每个部分都使用Base64编码,并使用句点(.)将它们连接起来,形成一个完整的JWT字符串。
下面是一个示例JWT的完整结构:
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. |
其中,Header部分为:
1 | { |
Payload部分为:
1 | { |
Signature部分为:
1 | SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c |
需要注意的是,尽管JWT使用Base64进行编码,但它并不是加密算法,因此在使用时仍然需要保证密钥的安全性。
1.Header
JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存
1 | { |
2.Payload
有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择
1 | iss:发行人 |
除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中,如下例:
1 | { |
请注意,默认情况下JWT是未加密的,因为只是采用base64算法,拿到JWT字符串后可以转换回原本的JSON数据,任何人都可以解读其内容,因此不要构建隐私信息字段,比如用户的密码一定不能保存到JWT中,以防止信息泄露。JWT只是适合在网络中传输一些非敏感的信息。
3.Signature
签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据公式生成签名,在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象。
注意JWT每部分的作用,在服务端接收到客户端发送过来的JWT token之后:
- header和payload可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据
- signature由于使用了不可逆的加密算法,无法解码出原文,它的作用是校验token有没有被篡改。服务端获取header中的加密算法之后,利用该算法加上secretKey对header、payload进行加密,比对加密后的数据和客户端发送过来的是否一致。注意secretKey只能保存在服务端,而且对于不同的加密算法其含义有所不同,一般对于MD5类型的摘要加密算法,secretKey实际上代表的是盐值。
6.JWT种类
JWT(JSON Web Token)根据用途和属性的不同,可以分为以下几种:
- JWS:JSON Web Signature,用于验证消息的完整性和来源。JWS包含Header、Payload和Signature三个部分,其中Signature是通过指定算法生成的,用于验证消息是否被篡改。
- JWE:JSON Web Encryption,用于对消息进行加密。JWE包含Header、Payload和Encryption三个部分,其中Encryption使用指定算法对消息进行加密,以保证机密性。
- JWK:JSON Web Key,用于描述用于加密、解密或签名JWT的公钥和私钥信息。JWK通常包括key type、key id、public key等属性。
- JWA:JSON Web Algorithm,用于描述JWT所支持的加密、解密和签名算法,例如HMAC、RSA、AES等。
需要注意的是,这些JWT的种类并不是相互独立的,它们可以结合使用来实现更强大的功能,例如使用JWS和JWE来同时保证消息的完整性、机密性和来源可信度。
7.JWT在Go中的应用
Go中实现JWT需要使用第三方库:jwt-go (官方提供的JWT库,支持生成、解析和验证JWT),一般你需要go get第三库,然后编写相关代码。
1 | type StandardClaims struct { |
这段代码定义了一个名为StandardClaims的结构体类型,它包含了一些常用的声明(claim),这些声明可以被添加到JWT的Payload部分,在身份验证、授权等方面起着重要的作用。具体来说,StandardClaims结构体包括以下字段:
- Audience:表示接收JWT的一方,可以是单个值或多个值的集合。
- ExpiresAt:表示JWT的过期时间,以Unix时间戳为单位。
- Id:表示JWT的唯一标识符,用于避免重放攻击等安全问题。
- IssuedAt:表示JWT的签发时间,以Unix时间戳为单位。
- Issuer:表示JWT的签发者,通常是应用程序的名称或网站的域名。
- NotBefore:表示JWT的生效时间,在此时间之前,JWT无法使用,以Unix时间戳为单位。
- Subject:表示JWT所代表的主题,例如用户ID、用户名等。
这些声明都是可选的,开发人员可以根据需要自由选择使用哪些声明,并将它们填充到StandardClaims结构体中,然后再将该结构体传递给JWT库生成JWT。
jwt.go
1 | var mySignKey = []byte("自己填") //secretKey |
JWTMiddleWare
1 | // JWTMiddleWare 鉴权中间件,鉴权并设置user_id |
- 标题: JWT详解
- 作者: Olivia的小跟班
- 创建于 : 2023-03-29 20:22:29
- 更新于 : 2023-05-27 03:47:42
- 链接: https://www.youandgentleness.cn/2023/03/29/JWT详解/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。