JWT学习笔记
JWT介绍
jwt 官网:https://jwt.io/
百度:JWT(JSON WEB Token)的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT:Json Web Token,是基于Json的一个公开规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,他的两大使用场景是:认证和数据交换
-
认证:认证是JWT的最常用场景。只要用户完成登录,其随后的请求都会包含JWT,以允许用户访问经由当前JWT授权的路由、服务或者是资源。由于开销小且能够被简单应用在跨域访问上,JWT在分布式站点上所支持的单点登录(SSO)已经是当前它被广泛应用的一个特性。
-
信息交换:JWT是一种在各参与方之间安全传递信息的良好方法。由于JWT可以被签名(例:使用公钥/秘钥对),因而可用于确认发送者自称的身份。除此之外,由于signature使用header和payload进行计算,也可以验证内容没有被篡改。
Session方案:
是什么要用token?Session 存放在服务器端,Session ID无法实现共享
Token+Redis方案:
解决Session集群无法共享的问题-->将token存到redis中
- Token 类似于 Session ID
- Token 依赖于 Redis 真实token存放value值
使用Token缺点:每次都需要根据token查询真实内容,对服务器端(比如Redis)压力就比较大。
JWT方案:
JWT和token 最大的区别:
token依赖于Redis,token存放value数据比较安全;而JWT不需要依赖于服务器端,将数据信息内容直接存放在客户端(浏览器)
传统的token
传统的Token,例如:用户登录成功生成对应的令牌,key为令牌 value:userid,隐藏了数据真实性 ,同时将该token存放到redis中,返回对应的真实令牌给客户端存放。
客户端每次访问后端请求的时候,会传递该token在请求中,服务器端接收到该token之后,从redis中查询如果存在的情况下,则说明在有效期内,如果在Redis中不存在的情况下,则说明过期或者token错误。
JWT组成的部分
-
Header(头) 作用:记录令牌类型、签名算法等 例如:
-
Payload(有效载荷)作用:携带一些用户信息 例如
-
Signature(签名)作用:防止Token被篡改、确保安全性 例如 计算出来的签名,一个字符串
1.第一部分:header (头部)
{
Typ="jwt" ---类型为jwt
Alg:"HS256" --加密算法为hs256
}
2.第二部分:playload(载荷) 携带存放的数据 用户名称、用户头像之类 注意敏感数据(不要存放未脱敏的信息,如手机号码、密码)
标准中注册的声明 (建议但不强制使用) :
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
3.第三部分:Signature(签名)
验证签名,防止别人去篡改payload中的数据(基于md5)。
JWT优缺点
优点:
- 无需再服务器存放用户的数据,减轻服务器端压力
- 轻量级、json风格比较简单
- 由于json的通用性,JWT天然支持 跨语言
缺点:
jwt一旦生成后期无法修改:
- 无法更新jwt有效期(比如你想要让jwt 90天以后过期,但是你改变主意了,想让jwt提前60天过期,这是不可行的,原因是你的jwt生成好了是需要存放到客户端的,你无法修改客户端的数据。当然,如果用token实现的话就直接把redis的数据清理掉即可)
- 无法销毁一个jwt(原因同上,jwt存放在浏览器客户端)
保护好secret私钥,该私钥对于鉴权非常重要。
不应该在jwt的payload存放敏感信息,因为客户端也可对称解密该部分信息,它相当于是暴露的。
JWT的应用场景
前端分离项目、(移动app、小程序、H5)
Base64
Base64 在线编码解码网站:https://base64.us/
Base64 相关概念:https://baike.baidu.com/item/base64/8545775?fr=aladdin
Base64不是加密和解密 主要是 编码和解码 基于64个可打印字符来表示二进制数据
header 头部(base64):
#将ewoidHlwIjoiand0IiwKImFsZyI6IkhTMjU2Igp9解码后的数据为:
{
"typ":"jwt",
"alg":"HS256"
}
ewoidHlwIjoiand0IiwKImFsZyI6IkhTMjU2Igp9
playload(base64):
作用:存放的数据
{
"userName":"mayikt",
"age":"28"
}
cGxheWxvYWQ=
secret=Base64(header .playload)
相关问题
token返回给客户端之后,服务端还要保存吗?
不需要保存
校验token时,怎么保证数据并没有被黑客拦截并篡改?
signature中有私钥来进行签名(即添加盐值),可以保证安全性
token颁发给客户端之后,要不要有过期时间?
需要设置token过期时间
多次登录生成的token都是一样的吗?都是可用的吗?
可以再payload加上时间戳,来保证每次生成的token都不一样,都是可用的
jwt和token区别?
jwt和token区别主要体现在接收的信息是否需要进入数据库查询信息。服务端验证客户端发来的token信息要进行数据的查询操作;而JWT验证客户端发来的token信息不需要, JWT使用密钥校验不用数据库的查询。
简单手写jwt
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.digest.Md5Crypt;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
public class JWTDemo04 {
public static void main(String[] args) throws UnsupportedEncodingException {
// 手写jwt的案例
/**
* 1.头部
* 2.payload
* 3.验证签名
*/
// jwt jwtHeader
JSONObject jwtHeader = new JSONObject();
jwtHeader.put("alg","HS256");
jwtHeader.put("typ","jwt");
// jwt playload
JSONObject jwtPlayload = new JSONObject();
jwtPlayload.put("userName","yushengjun644");
jwtPlayload.put("age",22);
//base64JwtHeader
String base64JwtHeader= Base64.getEncoder().encodeToString(jwtHeader.toJSONString().getBytes());
//base64JwtPlayload
String base64JwtPlayload= Base64.getEncoder().encodeToString(jwtPlayload.toJSONString().getBytes());
// 使用MD5 生成签名
String jwtSecret="mayikt"; //签名key,即添加盐值,存放在服务端
String signature = DigestUtils.md5Hex(jwtPlayload.toJSONString() + jwtSecret);
String jwt=base64JwtHeader+"."+base64JwtPlayload+"."+signature;
System.out.println(jwt);
// 解密
String jwtPlayloadStr=new String(Base64.getDecoder().decode(jwt.split("\\.")[1].getBytes()), //为什么要加“//”,做转义,不然有可能会报错
"UTF-8");
String jwtsignatureStr=jwt.split("\\.")[2];
System.out.println(DigestUtils.md5Hex(jwtPlayloadStr+jwtSecret).equals(jwtsignatureStr));
}
}
实际项目整合jwt
maven依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
登录接口:
package com.mayikt.api.member;
import com.alibaba.fastjson.JSONObject;
import com.mayikt.api.base.BaseApiService;
import com.mayikt.api.base.BaseResponse;
import com.mayikt.api.member.dto.req.UserLoginDto;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
public interface JWTLoginService {
/**
* jwt登录的方式
*
* @return
*/
@PostMapping("loginJwt")
BaseResponse<JSONObject> loginJwt(@RequestBody UserLoginDto userLoginDto);
/**
* jwt 验证
*
* @return
*/
@GetMapping("jwtVerification")
BaseResponse<JSONObject> jwtVerification(@RequestParam("jwt") String jwt);
}
package com.mayikt.api.impl.member;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mayikt.api.base.BaseApiService;
import com.mayikt.api.base.BaseResponse;
import com.mayikt.api.impl.entity.UserInfoDo;
import com.mayikt.api.impl.mapper.UserInfoMapper;
import com.mayikt.api.impl.utils.MayiktJwtUtils;
import com.mayikt.api.member.JWTLoginService;
import com.mayikt.api.member.dto.req.UserLoginDto;
import com.mayikt.api.utils.MD5Util;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class JWTLoginServiceImpl extends BaseApiService<JSONObject> implements JWTLoginService {
@Autowired
private UserInfoMapper userInfoMapper;
@Override
public BaseResponse<JSONObject> loginJwt(UserLoginDto userLoginDto) {
// 验证参数
String mobile = userLoginDto.getMobile();
if (StringUtils.isEmpty(userLoginDto.getMobile())) {
return setResultError("mobile 不能为空!");
}
String passWord = userLoginDto.getPassWord();
if (StringUtils.isEmpty(userLoginDto.getPassWord())) {
return setResultError("passWord 不能为空!");
}
// md5加密
String newPassWord = MD5Util.MD5(passWord);
QueryWrapper<UserInfoDo> userInfoDoQueryWrapper = new QueryWrapper<>();
userInfoDoQueryWrapper.eq("MOBILE", mobile);
userInfoDoQueryWrapper.eq("PASSWORD", newPassWord);
UserInfoDo userInfoDo = userInfoMapper.selectOne(userInfoDoQueryWrapper);
if (userInfoDo == null) {
return setResultError("手机号码或者密码错误");
}
// 生成jwttoken
String jwt = MayiktJwtUtils.generateJsonWebToken(userInfoDo);
JSONObject data = new JSONObject();
data.put("jwt", jwt);
return setResultSuccess(data);
}
@Override
public BaseResponse<JSONObject> jwtVerification(String jwt) {
if (StringUtils.isEmpty(jwt)) {
return setResultError("jwt is null");
}
Claims claims = MayiktJwtUtils.checkJWT(jwt);
if(claims==null){
return setResultError("jwt error");
}
return setResultSuccess();
}
}
Jwt如何实现注销
-
浏览器cookie清除(但是服务器还是存在)
-
建议将时间设置稍微短一点
- jwt有效期 90天 无法提前过期
- jwt保存在前端,所以当用户退出后无法清除jwt
文章参考自:
学习视频:【10分钟学会什么是jwt【余胜军通俗易懂版本】-哔哩哔哩】 https://b23.tv/HO8uemt
文档链接:
https://blog.csdn.net/u013343616/article/details/124924211
https://blog.csdn.net/Solo95/article/details/123456965
http://www.ujiuye.com/wenda/2021/70163.html
标签:JWT,mayikt,jwt,笔记,学习,token,import,com From: https://www.cnblogs.com/galo/p/17140376.html