JSON WEB Token JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
jwt 官网:
传统的token
传统的Token,例如:用户登录成功生成对应的令牌,key为令牌 value:userid,隐藏了数据真实性 ,同时将该token存放到redis中,返回对应的真实令牌给客户端存放。
客户端每次访问后端请求的时候,会传递该token在请求中,服务器端接收到该token之后,从redis中查询如果存在的情况下,则说明在有效期内,如果在Redis中不存在的情况下,则说明过期或者token错误。
JWT组成的部分
1.Header(头) 作用:记录令牌类型、签名算法等 例如:{“alg":"HS256","type","JWT} 2.Payload(有效载荷)作用:携带一些用户信息 例如{"userId":"1","username":"mayikt"} 3.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(签名)
JWT优缺点
优点:
1. 无需再服务器存放用户的数据,减轻服务器端压力
2. 轻量级、json风格比较简单
3. 跨语言
缺点:
jwt一旦生成后期无法修改:
1. 无法更新jwt有效期
2. 无法销毁一个jwt
jwt 90天以后过期 提前60天过期
JWT的应用场景
前端分离项目、(移动app、小程序、H5)
Base64
Base64不是加密和解密 主要是 编码和解码 基于64个可打印字符来表示二进制数据
Base64 在线编码解码 | Base64 加密解密 - Base64.us
header 头部:
{
"typ":"jwt",
"alg":"HS256"
}
ewoidHlwIjoiand0IiwKImFsZyI6IkhTMjU2Igp9
playload 存放的数据
{
"userName":"mayikt",
"age":"28"
}
cGxheWxvYWQ=
secret=Base64(header .playload)
Base64 在线编码解码 | Base64 加密解密 - Base64.us
简单手写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 {
String jwtSecret="mayikt";
// 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());
String base64JwtPlayload= Base64.getEncoder().encodeToString(jwtPlayload.toJSONString().getBytes());
// 使用MD5 生成签名
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>
登录接口:
import com.alibaba.fastjson.JSONObject;
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);
}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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如何实现注销
1. 浏览器cookie清除(但是服务器还是存在)
2. 建议将时间设置稍微短一点