可参考:https://juejin.cn/post/7106702145520402468
流程
组成部分
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和session区别
session是保存在服务端的,而JWT是保存在客户端的
- 基于session的认证流程
用户在浏览器中输入用户名和密码,服务器通过密码校验后生成一个session并保存到数据库
服务器为用户生成一个sessionId,并将具有sesssionId的cookie放置在用户浏览器中,在后续的请求中都将带有这个cookie信息进行访问
服务器获取cookie,通过获取cookie中的sessionId查找数据库判断当前请求是否有效
- 基于JWT的认证流程
用户在浏览器中输入用户名和密码,服务器通过密码校验后生成一个token并保存到数据库
前端获取到token,存储到cookie或者local storage中,在后续的请求中都将带有这个token信息进行访问
服务器获取token值,通过查找数据库判断当前token是否有效
安全性
JWT的payload使用的是base64编码的,因此在JWT中不能存储敏感数据。而session的信息是存在服务端的,相对来说更安全
性能
经过编码之后JWT将非常长,cookie的限制大小一般是4k,cookie很可能放不下,所以JWT一般放在local storage里面。并且用户在系统中的每一次http请求都会把JWT携带在Header里面,HTTP请求的Header可能比Body还要大。而sessionId只是很短的一个字符串,因此使用JWT的HTTP请求比使用session的开销大得多
一次性
无状态是JWT的特点,但也导致了这个问题,JWT是一次性的。想修改里面的内容,就必须签发一个新的JWT
无法废弃
一旦签发一个JWT,在到期之前就会始终有效,无法中途废弃。若想废弃,一种常用的处理手段是结合redis
续签
如果使用JWT做会话管理,传统的cookie续签方案一般都是框架自带的,session有效期30分钟,30分钟内如果有访问,有效期被刷新至30分钟。一样的道理,要改变JWT的有效时间,就要签发新的JWT。
最简单的一种方式是每次请求刷新JWT,即每个HTTP请求都返回一个新的JWT。这个方法不仅暴力不优雅,而且每次请求都要做JWT的加密解密,会带来性能问题。另一种方法是在redis中单独为每个JWT设置过期时间,每次访问时刷新JWT的过期时间
选择JWT或session
JWT有很多缺点,但是在分布式环境下不需要像session一样额外实现多机数据共享,虽然seesion的多机数据共享可以通过粘性session、session共享、session复制、持久化session、terracoa实现seesion复制等多种成熟的方案来解决这个问题。但是JWT不需要额外的工作,使用JWT不香吗?并且JWT一次性的缺点可以结合redis进行弥补
实现
- java
添加依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
代码编写
//controller层
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@RequestMapping(value = "/login", method = RequestMethod.POST)
public JwtResponse login(@RequestParam(name = "userName") String userName,
@RequestParam(name = "passWord") String passWord){
String jwt = "";
try {
jwt = userService.login(userName, passWord);
return JwtResponse.success(jwt);
} catch (Exception e) {
e.printStackTrace();
return JwtResponse.fail(jwt);
}
}
@Resource
private JwtUtils jwtUtil;
@RequestMapping("/test")
public Map<String, Object> test(@RequestParam("jwt") String jwt) {
//这个步骤可以使用自定义注解+AOP编程做解析jwt的逻辑,这里为了简便就直接写在controller里
Claims claims = null;
HashMap<String, Object> map = new HashMap<>();
try {
claims = jwtUtil.parseJWT(jwt);
} catch (Exception e) {
map.put("code", "1");
map.put("msg", "已过期,请重新登录");
return map;
}
String name = claims.get("name", String.class);
String age = claims.get("age", String.class);
map.put("name", name);
map.put("age", age);
map.put("code", "0");
map.put("msg", "请求成功");
return map;
}
//service层
public interface UserService {
String login(String username, String password) throws Exception;
}
@Service
public class UserServiceImpl implements UserService {
@Resource
private JwtUtils jwtUtil;
@Resource
private UserMapper userMapper;
@Override
public String login(String userName, String passWord) throws Exception {
//登录验证
User user = userMapper.findByUserNameAndPassword(userName, passWord);
if (user == null) {
return null;
}
//如果能查出,则表示账号密码正确,生成jwt返回
String uuid = UUID.randomUUID().toString().replace("-", "");
HashMap<String, Object> map = new HashMap<>();
map.put("name", user.getName());
map.put("age", user.getAge());
//ttl设置 下面是一分钟示例(一分钟后过期)
return jwtUtil.createJWT(uuid, "login subject", 60 * 1000L, map);
}
//核心
@Component
public class JwtUtils {
@Value("${jwt.secretKey}")
private String secretKey;
public String createJWT(String id, String subject, long ttlMillis, Map<String, Object> map) throws Exception {
//头部信息,不设置也是默认这个值
Map<String, Object> header = new HashMap<>(2);
header.put("Type", "Jwt");
header.put("alg", "HS256");
JwtBuilder builder = Jwts.builder()
.setHeader(header)
.setId(id)
.setSubject(subject) // 发行者
.setIssuedAt(new Date()) // 发行时间
.signWith(SignatureAlgorithm.HS256, secretKey) // 签名类型 与 密钥
.compressWith(CompressionCodecs.DEFLATE);// 对载荷进行压缩
if (!CollectionUtils.isEmpty(map)) {
builder.setClaims(map);
}
if (ttlMillis > 0) {
builder.setExpiration(new Date(System.currentTimeMillis() + ttlMillis));
}
return builder.compact();// 拼接header + payload
}
public Claims parseJWT(String jwtString) {
return Jwts.parser().setSigningKey(secretKey)
.parseClaimsJws(jwtString)
.getBody();
}
public static void main(String[] args) {
Date date1 = new Date(System.currentTimeMillis());
Date date = new Date(System.currentTimeMillis() + 60 * 1000L);
System.out.println(date1);
System.out.println(date);
}
}
}
//mapper
@Component
public class UserMapper {
private static final Map<String, User> map = new HashMap<>();
static {
User value = new User();
value.setUsername("zs");
value.setPassword("123");
value.setName("林大大");
value.setAge("999");
map.put("zs", value);
}
public User findByUserNameAndPassword(String userName, String passWord) {
User user = map.get(userName);
if (!Objects.isNull(user)) {
if (passWord.equals(user.getPassword())) {
return user;
}
}
return null;
}
}
//common
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BaseResponse {
private String code;
private String msg;
public static BaseResponse success() {
return new BaseResponse("0", "成功");
}
public static BaseResponse fail() {
return new BaseResponse("1", "失败");
}
//构造器、getter、setter方法
}
@EqualsAndHashCode(callSuper = true)
@Data
public
class JwtResponse extends BaseResponse {
private String jwtData;
private String code;
private String msg;
public JwtResponse(String code, String msg, String jwtData) {
this.code = code;
this.msg = msg;
this.jwtData = jwtData;
}
public static JwtResponse success(String jwtData) {
BaseResponse success = BaseResponse.success();
return new JwtResponse(success.getCode(), success.getMsg(), jwtData);
}
public static JwtResponse fail(String jwtData) {
BaseResponse fail = BaseResponse.fail();
return new JwtResponse(fail.getCode(), fail.getMsg(), jwtData);
}
//构造器、getter、setter方法
}
application.yml
## 用户生成jwt字符串的secretKey
jwt:
secretKey: ak47
标签:map,return,String,登录,JWT,oss,jwt,public
From: https://www.cnblogs.com/lin5923/p/17446933.html