JWT 是什么?
JWT -> JSON Web Token
JWT 是一种开放标准(RFC 7519),用于在网络应用中传递声明信息。
JWT 到一大优势是它的可扩展性和自包含性。它可以在各个系统之间进行安全传输和验证,因为它包含了所有必要的信息,并且经过签名保证了数据的完整性。JWT 通常用于身份验证和授权机制,比如 Web 应用中通过 JWT 来验证用于的身份,下面我们就来实践一下。
本文的实践案例,基于本人之前的文章 Spring Boot 整合 Swagger 接口文档工具项目。
安装依赖
安装 JWT 依赖:
<!-- 引入jwt--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.8.2</version> </dependency>
添加 JWT 处理方法
我们这里使用用户字段 id,name 和 password 来创建 token。
1. 生成 token
// 生成 token public static String createToken(User user) { Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000); Map<String, Object> map = new HashMap<>(); map.put("alg", "HS256"); map.put("typ", "JWT"); System.out.println(user+"user"); String token = JWT.create() .withClaim("id", user.getId()) .withClaim("name", user.getName()) .withClaim("password", user.getPassword()) .withExpiresAt(expireDate) .withIssuedAt(new Date()) .sign(Algorithm.HMAC256(SECRET)); return token; }
2. 验证 token
// 校验 token public static Map<String, Claim> verifyToken(String token) { DecodedJWT jwt = null; try { JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build(); jwt = verifier.verify(token); } catch (Exception e) { return null; } return jwt.getClaims(); }
完整的代码如下。我们在包 com.launch.util 下创建 JwtTokenUtils.java 文件:
package com.launch.util; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; import com.launch.model.User; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JwtTokenUtils { private static final String SECRET = "jwtSECRET"; // 密钥 private static final long EXPIRATION = 3600L; // 3600 秒 // 生成 token public static String createToken(User user) { Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000); Map<String, Object> map = new HashMap<>(); map.put("alg", "HS256"); map.put("typ", "JWT"); System.out.println(user+"user"); String token = JWT.create() .withClaim("id", user.getId()) .withClaim("name", user.getName()) .withClaim("password", user.getPassword()) .withExpiresAt(expireDate) .withIssuedAt(new Date()) .sign(Algorithm.HMAC256(SECRET)); return token; } // 校验 token public static Map<String, Claim> verifyToken(String token) { DecodedJWT jwt = null; try { JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build(); jwt = verifier.verify(token); } catch (Exception e) { return null; } return jwt.getClaims(); } }
创建过滤文件
我们紧接着创建拦截的过滤文件。我们在包 com.launch.config.authenticationhandler.jwt 下创建文件 JwtFilter.java:
package com.launch.config.authenticationhandler.jwt; import com.auth0.jwt.interfaces.Claim; import com.launch.util.JwtTokenUtils; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Map; // /api/user/secure/* 的文件起作用 @WebFilter(filterName = "JwtFilter", urlPatterns = "/api/user/secure/*") public class JwtFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) req; final HttpServletResponse response = (HttpServletResponse) res; response.setCharacterEncoding("UTF-8"); final String token = request.getHeader("Auth"); if("OPTIONS".equals(request.getMethod())) { // 是否是 OPTIONS 请求 response.setStatus(HttpServletResponse.SC_OK); chain.doFilter(request, response); } else { if(token == null) { response.getWriter().write("no token"); return; } Map<String, Claim> userData = JwtTokenUtils.verifyToken(token); // 检验 token if(userData == null) { response.getWriter().write("token illegal"); return; } Integer id = userData.get("id").asInt(); // 获取 id String userName = userData.get("name").asString(); // 获取用户名 String password = userData.get("password").asString(); // 获取密码 request.setAttribute("id", id); // 设置 id request.setAttribute("username", userName); // 设置用户名 request.setAttribute("password", password); // 设置密码 chain.doFilter(req, res); // 过滤成功 } } @Override public void destroy() {} }
这个文件做了什么事情呢?
urlPatterns = "/api/user/secure/*" 表示接口 /api/user/secure/* 下的接口需要经过验证才可以访问。
request.getHeader("Auth") 我们通过 request 请求的 header 中获取提前存在字段 Auth 上的 token 值。在真实登陆的时候就会存储进去 Auth 内。
request.setAttribute("id", id); 将获取的 id 值存在 request 请求上。username,password 同理。
为了过滤器能够生效,我们需要在入口类添加注释 @ServletComponentScan(basePackages="com.launch.config.authenticationhandler.jwt")。
验证
最后,我们验证下 token 是否集成生效。该操作在包 com.lauch.controller 下 UserController.java 文件中操作。
我们模拟登陆,生成 token:
// 用户登陆 @GetMapping("/login") String login(User user) { // jimmy simulation // { // "id": 2, // "name": "Jimmy", // "age": 18, // "password": "123456" // } String token = JwtTokenUtils.createToken(user); return token; }
下面我们来获取当前登陆的用户信息。
@GetMapping("/secure/current_registrant") public String currentRegistrant(HttpServletRequest request) { Integer id = (Integer) request.getAttribute("id"); String username = request.getAttribute("username").toString(); String password = request.getAttribute("password").toString(); return "当前用户信息: id="+id+" ,username="+username+" ,password="+password; }
上面也提及到了,我们是要获取 request 中 header 上 Auth 的值。这里就有 Auth = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsIm5hbWUiOiJKaW1teSIsImlkIjoyLCJleHAiOjE2OTExNjA0NjMsImlhdCI6MTY5MTE1Njg2M30.nIP23eI-vgIZC-Fw2FGDQw1GrXlA4mBVBb3vkSFySgc。我们在 postman 上设置,并请求该接口: