文章目录
- JWT深入浅出
- 1.JWT是什么
- 2.为什么选JWT
- 2.1 传统Session认证
- 2.2 JWT认证
- 3.JWT怎么用
- 4. jwt绝对安全吗?
JWT深入浅出
1.JWT是什么
JWT(JSON Web Token)是一种用于在网络应用间传递信息的开放标准,通常用于身份认证和非敏感数据的传递(签名技术是保证传输的信息不被篡改,并不能保证信息传输的安全)
JWT以点分隔的三部分组成:Header头部,Payload载荷,Signature签名
JWT举例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
我们可以直接通过官网(https://jwt.io/#debugger-io)解析出JWT三部分表示的内容
- Header头部:通常包含Token类型(即JWT)和使用的签名加密算法,用JSON格式表示,将JOSN对象用base64编码得到JWT的Header部分(不是加密,是编码!)
- Payload有效载荷:携带数据。是一个包含了声明 (claims) 的 JSON 对象,用于描述关于 JWT 的信息。
- Signature签名:JWT 的签名通常是通过使用密钥和指定的加密算法对头部和负载进行签名生成的。密钥存储在服务器端,JWT的签发生成就是在服务器端完成。注意:服务器JWT 的签名部分用于验证 JWT 的完整性和真实性。
验证 JWT 时,接收方会使用相同的密钥和算法,对接收到的头部和负载进行签名计算,并与 JWT 中的签名部分进行比较。如果两者一致,则说明 JWT 是合法的,且未被篡改;否则,JWT 将被视为不可信任或无效。
签名部分的存在使得 JWT 在传输过程中具有完整性和真实性保护,但是需要确保密钥的安全性,因为持有正确密钥的任何人都能够验证 JWT 的真实性。
下面扩展对称加密和非对称加密:
- 对称加密:对称加密使用相同的密钥来加密和解密数据。这意味着发送方和接收方都必须共享同一个密钥
- 非对称加密:非对称加密使用一对密钥,分为公钥和私钥。公钥用于加密数据,私钥用于解密数据
2.为什么选JWT
常用于用户认证
2.1 传统Session认证
用户通过用户名密码进行身份验证,服务器端验证用户提供的凭据是否正确并确定用户是否有权限访问该资源。
如果用户提供凭据有效,服务器会创建并维护一个会话Session
,记录用户相关的身份权限信息,并响应用户一个包含会话标识符session ID
的Cookie
。客户端cookie存储在本地,并在后续每次请求携带cookie
(包含session ID
)。服务器会通过session ID来识别用户
暴露的问题:
- 随着用户激增,服务器端在内存中维护的session开销也会很大,服务器增加负担(JWT就只需要根据签名加密算法和密钥验证JWT即可,无需繁琐操作)
- 由于
session
是存在与服务器的物理内存中。在分布式的应用上,存在相同资源的多个服务器,难道每个服务器都登录授权?这大大限制了负载均衡器的能力。以此限制了分布式应用的扩展能力,不方便集群应用。(JWT就只需要根据签名加密算法和密钥验证JWT即可,无需繁琐操作)虽然可以将session
统一保存到Redis中,但是这样做无疑增加了系统的复杂性,对于不需要redis的应用也会白白多引入一个缓存中间件 - 因为
session
认证本质基于cookie
,所以如果cookie
被截获,用户很容易收到跨站请求伪造攻击。并且如果浏览器禁用了cookie
,这种方式也会失效 - 前后端分离系统中更加不适用,后端部署复杂,前端发送的请求往往经过多个中间件到达后端,
cookie
中关于session
的信息会转发多次 - 由于基于Cookie,而cookie无法跨域,所以session的认证也无法跨域,对单点登录不适用(单点登录是一种身份认证和授权机制,它允许用户使用同一套认证信息(如用户名和密码)来访问多个相关的、但又是相互独立的软件应用系统)
2.2 JWT认证
用户通过用户名密码进行身份验证,服务器端核对用户名密码后,生成JWT返回前端,前端本地存储JWT(推出登录删除JWT即可),并在后续的请求中携带JWT,服务器处理请求时只需要验证JWT的合法性即可(即通过密钥和签名加密算法对Header和payload部分进行加密,若与签名部分相等即为合法)
JWT优点:
- 不需要在服务端保存会话信息,特别适用于分布式微服务
- Token是以JSON加密的形式保存在客户端,所以JWT是跨语言的,原则上任何web形式都支持
- 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
- 单点登录友好:使用Session进行身份认证的话,由于cookie无法跨域,难以实现单点登录。但是使用token进行认证及没有这样的问题
3.JWT怎么用
添加依赖
<dependencies>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
添加工具类:
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;
import java.util.Date;
public class JwtHelper {
//指定jwt过期时间
private static long tokenExpiration = 365*24*60*60*1000;
//指定jwt的签名密钥
private static String tokenSignKey = "lpyx";
//根据usesrId和userName创建JWT并返回
public static String createToken(Long userId, String userName) {
String token = Jwts.builder()
.setSubject("lpyx-USER")
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
.claim("userId", userId)
.claim("userName", userName)
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
//通过jwt返回用户id
public static Long getUserId(String token) {
if(StringUtils.isEmpty(token)) return null;
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
Integer userId = (Integer)claims.get("userId");
return userId.longValue();
}
public static String getUserName(String token) {
if(StringUtils.isEmpty(token)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return (String)claims.get("userName");
}
public static void removeToken(String token) {
//jwttoken无需删除,客户端扔掉即可。
}
public static void main(String[] args) {
String token = JwtHelper.createToken(7L, "admin");
System.out.println(token);
System.out.println(JwtHelper.getUserId(token));
System.out.println(JwtHelper.getUserName(token));
}
}
4. jwt绝对安全吗?
为了防止JWT被挟持和恶意攻击,可以采取以下措施:
- 使用HTTPS:确保JWT在传输过程中使用HTTPS协议进行加密传输,以防止中间人攻击。
- 设置合适的过期时间:为JWT设置较短的过期时间,以减少JWT被挟持后的风险。
- 存储敏感信息在服务器端:不要在JWT中存储敏感信息,如密码、密钥等。这些信息应该存储在服务器端的安全存储中。
- 验证JWT的完整性:在服务器端验证JWT的签名和负载内容,确保JWT没有被篡改或伪造。
- 防止CSRF攻击:使用CSRF令牌、验证请求来源等方法来防止CSRF攻击。
- 限制JWT的使用范围:为JWT设置适当的使用范围(audience),确保JWT只能在指定的应用程序或服务中使用。