Spring Boot JWT
陈宝子 人气:01、概述
JWT 简称 JSON Web Token,也就是通过JSON形式作为Web应用中的令牌,用于各方之间安全地将信息作为JSON对象传输,在数据传输的过程中还可以完成数据加密、签名等相关处理。
2、优势所在
在JavaWeb阶段,经常使用session来存储,以方便用来判断用户是否操作等等。但这又恰巧暴露了一个问题,用session存储是需要占用服务器内存的。当用户只有一两个的时候没什么问题,但是当用户成千上万的话,服务器就很难招架得住了。并且session是基于cookie来实现的,因此数据很容易被截获,遭遇CSRF跨域伪造攻击,因此不太安全。这时JWT的优势就突显出来的:
- 简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
- 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
- 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
- 不需要在服务端保存会话信息,特别适用于分布式微服务。
3、结构组成
JWT 中的 token 组成主要有三部分:
- 标头(Header)
- 有效载荷(Payload)
- 签名(Signature)
3.1、标头(Header)
标头通常由两部分组成:令牌的类型 ( 即JWT ) 和所使用的签名算法,例如HMAC SHA256(默认)或RSA。它会使用Base64编码组成JWT结构的第一部分。值得注意的是,Base64是一种编码,也就是说,它是可以被翻译回原来的样子的,它并不是一种加密过程。
{
"alg": "HS256",
"typ": "JWT"
}
3.2、有效负载(Payload)
有效负荷是包含声明,通常将需要传递的数据存放在Payload中,同样的是使用Base64编码,但这同时说明这一块是可以被反编译的,这是什么意思呢?就是太敏感的信息存放在这里的话存在被捕获的可能,因此官方推荐在Payload中不要存放敏感的信息,例如女生的年龄。
3.3、签名(Signature)
签名是由编码后的标头和有效负荷以及我们提供的一个密钥,然后使用标头规定的签名算法进行签名。因此,签名的主要作用是保证JWT没有被篡改过,保证 token 的合法性。
这里特别注意的是,密钥 secret 是验证 token 是否合法的重要凭证,如果用于验证的密钥被外界所获取到的话,我们所建立的验证防线将如同虚设!
4、Spring boot整合JWT 导入依赖
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.19.0</version> </dependency>
编写JwtUtils,提高代码的重用率
public class JwtUtils { /** 过期时间,单位:秒,默认半小时过期 **/ private static final long EXPIRATION = 60 * 30L; /** 密钥,一般长度较长,内容较复杂 **/ private static final String SECRET = "my_secret"; /** * @description 创建token * @author xBaozi * @date 20:49 2022/3/31 **/ public static String createToken(Map<String, String> claimMap) { // 当前时间戳加上设定的毫秒数(1秒 == 1000毫秒) Date expiration = new Date(System.currentTimeMillis() + EXPIRATION * 1000); // 设置JWT头部 Map<String, Object> map = new HashMap<>(); map.put("alg", "HS256"); map.put("typ", "JWT"); // 创建token JWTCreator.Builder builder = JWT.create(); //使用Lambda创建payload claimMap.forEach((k,v)->{ builder.withClaim(k,v); }); // 添加头部,可省略保持默认,默认即map中的键值对 return builder.withHeader(map) // 设置过期时间 .withExpiresAt(expiration) // 设置签名解码算法 .sign(Algorithm.HMAC256(SECRET)); } /** * @description 验证token * @author xBaozi * @date 23:36 2022/3/31 **/ public static DecodedJWT verifyToken(String token) { return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token); } }
编写过滤器,这是为了使得不用在每一个controller前端控制器都写一遍获取token,将token放在请求体中请求,从而简化开发操作
public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Map<String, Object> map = new HashMap<>(); //获取请求头中令牌 String token = request.getHeader("token"); try { //验证令牌 JwtUtils.verifyToken(token); //验证成功,放行请求 return true; } catch (SignatureVerificationException e) { e.printStackTrace(); map.put("msg", "无效签名!"); } catch (TokenExpiredException e) { e.printStackTrace(); map.put("msg", "token过期!"); } catch (AlgorithmMismatchException e) { e.printStackTrace(); map.put("msg", "token算法不一致!"); } catch (Exception e) { e.printStackTrace(); map.put("msg", "token无效!!"); } //设置状态 map.put("state", false); //将map转为json String json = new ObjectMapper().writeValueAsString(map); // 相应json数据 response.setContentType("application/json;charset=UTF-8"); response.getWriter().println(json); return false; } }
配置过滤器
@Configuration public class JwtConfig implements WebMvcConfigurer{ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JwtInterceptor()) //添加拦截路径 .addPathPatterns("/**") //添加放行路径 .excludePathPatterns("/user/login"); } }
编写前端控制器,这里以两个为例
@RestController @RequestMapping("/user") @Slf4j public class UserController { // 这里只做演示,就不写Service层了 // @Autowired // private UserService userService; /** * @description 登录功能 * @author xBaozi * @date 0:02 2022/4/1 * @param user user对象,默认有值 **/ @GetMapping("/login") public Map<String,Object> login(User user){ log.info("用户名: [{}]",user.getName()); log.info("密码: [{}]",user.getPassword()); Map<String, Object> map = new HashMap<>(); try{ Map<String,String> payload = new HashMap<>(); payload.put("id",user.getId()); payload.put("name",user.getName()); // 生成JWT的令牌 String token = JwtUtils.createToken(payload); map.put("state",true); map.put("msg","认证成功"); // 响应token map.put("token",token); }catch (Exception e){ map.put("state",false); map.put("msg",e.getMessage()); } return map; } @PostMapping("/other") public Map<String,Object> test(HttpServletRequest request){ Map<String, Object> map = new HashMap<>(); //处理自己业务逻辑 String token = request.getHeader("token"); DecodedJWT verify = JwtUtils.verifyToken(token); log.info("用户id: [{}]",verify.getClaim("id").asString()); log.info("用户name: [{}]",verify.getClaim("name").asString()); map.put("state",true); map.put("msg","请求成功!"); return map; } }
postman测试
IDEA控制台查看信息
搞完收工,这里的token交由前端进行管理保存!!!溜了~
加载全部内容