jwt和token区别
一、安全性比较
-
验证方式:
- Token:服务端验证客户端发送的Token时,通常需要查询数据库获取用户信息,以验证Token的有效性。
- JWT(JSON Web Token):服务端验证JWT时,只需使用密钥解密进行校验,无需查询或减少查询数据库,因为JWT自包含了用户信息和加密的数据。
- Token:服务端验证客户端发送的Token时,通常需要查询数据库获取用户信息,以验证Token的有效性。
-
信息安全性:
- Token:一般的Token没有签名机制,只能通过验证Token的合法性来确保安全性,可能存在被伪造的风险。
- JWT:使用签名来验证Token的有效性,确保Token在传输过程中没有被篡改,因此更安全。
- Token:一般的Token没有签名机制,只能通过验证Token的合法性来确保安全性,可能存在被伪造的风险。
二、实际应用中的区别
-
存储位置与信息包含:
- Token:通常只是一个标识符,需要在服务器上存储相关的用户信息。
- JWT:包含了所有必要的信息,如用户信息、加密信息和过期时间,服务端不需要保存任何用户信息。
-
格式与结构:
- Token:结构多样,可以是简单的字符串、Base64编码的字符串等,没有固定的格式要求。1
- JWT:遵循RFC 7519标准,由头部(Header)、载体(Payload)和签名(Signature)三部分组成,使用JSON对象来存储信息,并使用Base64编码进行传输。
-
无状态性:
- Token:需要在服务端存储相关信息,增加了服务端的负担。5
- JWT:是无状态的,服务端不需要保存任何用户信息,只需验证JWT的签名即可。
-
扩展性:
- Token:通常只能存储有限的信息。
- JWT:可以存储更多的信息,如用户的角色、权限等。
JWT 格式
使用逗号分隔的三部分 :
- Header
- Payload
- Signature
token 格式:xxxxx.yyyyy.zzzzz
1、Header
Header 通常由 token 类型和签名算法名两部分组成.是token的第1部分
例如:
{ |
|
"alg": "HS256", // 签名算法 |
|
"typ": "JWT" // token类型 |
|
} |
2、Payload
Payload 是token的第2部分.包含了一些声明(claims).声明的名字必须是唯一的.
payload 示例
{ |
|
"sub": "1234567890", |
|
"name": "John Doe", |
|
"admin": true |
|
} |
JSON
然后, 这个JSON 使用 Base64Url 编码后放到 JWT 的第2部分.
3、Signature
拿到编码后的 header 和 编码后的 payload 使用 密码进行签名.
使用 HMAC SHA256 加签示例:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
Signature需要使用编码后的header和payload以及我们提供的一个秘钥,然后使用header中指定的签名算法进行签名,签名的作用是保证JWT没有被篡改过
HMACSHA256(base64UrlEncode(header)+“.”+base64UrlEncode(payload),secret)
secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。【secret 是签名秘钥】
4、 JWT实现:
1、依赖引入
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt-jsonwebtoken.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>RELEASE</version>
</dependency>
2、生成Token
// 签名密钥
private static final String SECRET = "!Doker$";
public String createToken(Map<String, Object> claims, String subject) {
final Date createdDate = clock.now();
final Date expirationDate = calculateExpirationDate(createdDate);
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(createdDate)
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, Algorithm.HMAC256(SECRET))
.compact();
}
private Date calculateExpirationDate(Date createdDate) {
return new Date(createdDate.getTime() + expiration * 1000);
}
3、刷新Token
public String RefreshToken(String token) {
final Date createdDate = clock.now();
final Date expirationDate = calculateExpirationDate(createdDate);
final Claims claims = getAllClaimsFromToken(token);
claims.setIssuedAt(createdDate);
claims.setExpiration(expirationDate);
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, Secret)
.compact();
}
4、token发送给前端
传入当前用户的功能与用户信息,登录名生成token,写入response的返回头中,前端获取后保存在前端的本地缓存中,后续前端请求要把token放在头header里。
//登录成功之后
List<Object> functs=(List<Object>) authResult.getAuthorities();
//当前功能列表
String loginName=authResult.getName();//登录名
Users obj=(Users)authResult.getPrincipal();//用户信息
String token=JwtUtil.createToken(loginName,functs,obj);
//生成token TOKEN_HEADER= Authorization TOKEN_PREFIX=Bearer token值
response.setHeader(JwtUtil.TOKEN_HEADER,JwtUtil.TOKEN_PREFIX+token);
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK); //个人编写的视图对象
DTO dto=new DTO<>();
dto.setCode("000000");
dto.setMessage("认证通过");
PrintWriter pw=response.getWriter();
pw.write(JsonUtil.set(dto));//写入json
pw.flush();//强制刷新
pw.close();//关闭流
5、验证用户请求携带token
String header = request.getHeader(JwtUtil.TOKEN_HEADER);
if (null == header || !header.toLowerCase().startsWith(JwtUtil.TOKEN_PREFIX)) {
// 如果头部 Authorization 未设置或者不是 basic 认证头部,则当前
// 请求不是该过滤器关注的对象,直接放行,继续filter chain 的执行
chain.doFilter(request, response);
return;
}
try {
String token = header.replace(JwtUtil.TOKEN_PREFIX, "");
// 验证token是否过期
if (JwtUtil.isExpiration(token)) {
throw new javax.security.sasl.AuthenticationException("token 验证不通过");
}
//檢查token是否能解析
Users user = (Users) JwtUtil.getUser(token);
if (null == user) {
throw new javax.security.sasl.AuthenticationException("token 验证不通过");
}
//验证成功