Spring Boot REST 风格 API 接口 JWT Token 认证
需求分析
接口认证需求:
1 能够有选择地过滤没有权限(Token)的请求 2 Token 具有时效性 3 如果用户连续操作,Token 能够自动刷新(自动延长有效期)
核心依赖
- <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
- <dependency>
- <groupId>com.auth0</groupId>
- <artifactId>java-jwt</artifactId>
- <version>3.8.3</version>
- </dependency>
本文使用目前最新版本的依赖
核心代码
JwtUtil 生成和解密Token
- package com.example.util;
- import com.auth0.jwt.JWT;
- import com.auth0.jwt.JWTVerifier;
- import com.auth0.jwt.algorithms.Algorithm;
- import java.util.Date;
- /**
- * @Author: lty
- * @Date: 2019/12/17 09:33
- */
- public class JwtUtil {
- private final static String DEFAULT_SECRET = "TASDASDF9823K4JH29S8D2H349SDFH14";
- //region 加密区
- public static String encode(String k, String v) {
- return encode(k, v, 0);
- }
- private static String encode(String k, String v, long expireTime) {
- return encode(null, k, v, expireTime);
- }
- private static String encode(String secret, String k, String v, long expireTime) {
- if (secret == null || secret.length() < 1) {
- secret = DEFAULT_SECRET;
- }
- Date expDate = null;
- if (expireTime > 1) {
- expDate = new Date(System.currentTimeMillis() + expireTime);
- }
- //创建加密的token
- Algorithm algorithm = Algorithm.HMAC256(secret);
- String token =JWT.create().withIssuer("lty").withClaim(k,v)
- .withExpiresAt(expDate).sign(algorithm);
- return token;
- }
- //endregion
- //region 解密区
- public static String decode(String key, String encryptedToken) {
- return decode(null, key, encryptedToken);
- }
- public static String decode(String secret, String key, String encryptedToken) {
- if ("".equals(secret) || null == secret) {
- secret = DEFAULT_SECRET;
- }
- Algorithm algorithm = Algorithm.HMAC256(secret);
- JWTVerifier verifier = JWT.require(algorithm)
- .withIssuer("lty")
- .build();
- String s = verifier.verify(encryptedToken).getClaim(key).asString();
- return s;
- }
- //endregion
- public static void main(String[] args) {
- String encode = JwtUtil.encode("TokenKey", "843328437@1576551621592");
- System.out.println(encode);
- String lty = JwtUtil.decode("TokenKey", encode);
- System.out.println(lty);
- }
- }
Token 拦截器
拦截器配置
- package com.example.interceptor;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
- import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
- @Configuration
- public class InterceptorConfig extends WebMvcConfigurationSupport {
- /**
- * 多个拦截器组成一个拦截器链
- * addPathPatterns 用于添加拦截规则
- * excludePathPatterns 用户排除拦截
- * @param registry
- */
- @Override
- protected void addInterceptors(InterceptorRegistry registry) {
- //拦截
- registry.addInterceptor(new TokenInterceptor())
- .addPathPatterns("/**")
- .excludePathPatterns("/autologon/authorizationLogin");
- super.addInterceptors(registry);
- }
- /**
- * 配置静态资源映射
- * @param registry
- */
- @Override
- protected void addResourceHandlers(ResourceHandlerRegistry registry) {
- ///将所有/static/** 访问都映射到classpath:/static/ 目录下
- registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
- super.addResourceHandlers(registry);
- }
- }
- package com.example.interceptor;
- import com.example.pojo.Result;
- import com.example.util.JwtUtil;
- import com.example.util.ResultUtil;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.web.servlet.HandlerInterceptor;
- import org.springframework.web.servlet.ModelAndView;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.util.Date;
- import java.util.Objects;
- public class TokenInterceptor implements HandlerInterceptor {
- private final Logger logger = LoggerFactory.getLogger(TokenInterceptor.class);
- private final String TOKEN_HEADERS_FIELD = "authCheckCode";
- private final String TOKEN_KEY = "TokenKey";
- public static final long TOKEN_REFRESH_TIME_MILLIS = 1000 * 60 * 60 * 2L;
- public static final long TOKEN_EXPIRE_TIME_MILLIS = 1000 * 60 * 60 * 24 * 30L;
- /**
- * controller 执行之前调用
- * @param httpRequest
- * @param httpResponse
- * @param o
- * @return
- * @throws IOException
- */
- @Override
- public boolean preHandle(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Object o) throws Exception {
- httpResponse.addHeader("Access-Control-Allow-Origin", "*");
- httpResponse.addHeader("Access-Control-Allow-Headers","*");
- // 允许跨域的Http方法
- httpResponse.addHeader("Access-Control-Allow-Methods", "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS,TRACE");
- // 允许浏览器访问 Token 认证响应头
- httpResponse.addHeader("Access-Control-Expose-Headers", TOKEN_HEADERS_FIELD);
- // 默认返回原 Token
- httpResponse.setHeader(TOKEN_HEADERS_FIELD, httpRequest.getHeader(TOKEN_HEADERS_FIELD));
- // 应对探针模式请求(OPTIONS)
- String methodOptions = "OPTIONS";
- if (httpRequest.getMethod().equals(methodOptions)) {
- httpResponse.setStatus(HttpServletResponse.SC_ACCEPTED);
- return true;
- }
- Result result = checkToken(httpRequest, httpResponse);
- if (!"00".equals(result.getCode())) {
- logger.warn("{}",result.toString());
- httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
- httpResponse.setContentType("application/json; charset=utf-8");
- httpResponse.setCharacterEncoding("utf-8");
- PrintWriter writer = httpResponse.getWriter();
- writer.write(new ObjectMapper().writeValueAsString(result.toString()));
- return false;
- }
- return true;
- }
- private Result checkToken(HttpServletRequest request, HttpServletResponse response) throws Exception {
- try {
- String token = request.getHeader(TOKEN_HEADERS_FIELD);
- if (token == null || token.length() < 1) {
- return ResultUtil.requestFaild("无效请求头");
- }
- String tokenValue = JwtUtil.decode(TOKEN_KEY, token);
- long time = Long.parseLong(tokenValue.substring(tokenValue.indexOf("@") + 1));
- String user = tokenValue.substring(0, tokenValue.indexOf("@"));
- logger.info("{}, date: {}, user: {}", tokenValue, new Date(time), user);
- // 校验 Token 有效性
- long subResult = System.currentTimeMillis() - time;
- if (subResult >= TOKEN_EXPIRE_TIME_MILLIS) {
- return ResultUtil.requestFaild("Token过期");
- }
- if (subResult > TOKEN_REFRESH_TIME_MILLIS) {
- // 刷新 Token
- String newToken = JwtUtil.encode(TOKEN_KEY,user + "@" + System.currentTimeMillis());
- System.out.println("生成新token "+ newToken+ "刷新时间 "+System.currentTimeMillis());
- response.setHeader(TOKEN_HEADERS_FIELD, newToken);
- return ResultUtil.requestSuccess(null);
- }
- } catch (Exception e) {
- logger.warn("Token 校验失败,{}:{}", e.getClass().getName(), e.getMessage());
- return ResultUtil.requestFaild("Token验证失败");
- }
- return ResultUtil.requestSuccess(null);
- }
- /**
- * controller 执行之后,且页面渲染之前调用
- */
- @Override
- public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
- }
- /*
- *
- * 页面渲染之后调用,一般用于资源清理操作
- */
- @Override
- public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
- }
- }
测试
写一个HTTP接口 测试Token验证
- /**
- * 功能描述 test
- *
- * @param
- * @return com.example.pojo.Result
- * @author lty
- * @date 2019/11/28
- */
- @RequestMapping(value = "/t")
- public Result t() throws Exception {
- return testService.testList();
- }
控制台输出日志
- 2019-12-17 13:43:46,095 INFO TokenInterceptor:78 - 843328437@1576551621592, date: Tue Dec 17 11:00:21 CST 2019, user: 843328437
- 生成新token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJsdHkiLCJUb2tlbktleSI6Ijg0MzMyODQzN0AxNTc2NTYxNDI2MDk4In0.X-qsFfMAP9ebYIHjkpaD3rgJqqPp3QnTuek7aRKvEVE刷新时间 1576561426111
- 2019-12-17 13:43:46,113 INFO HttpAspect:50 - url=http://localhost:8080/example/test/t
- 2019-12-17 13:43:46,113 INFO HttpAspect:52 - method=POST
- 2019-12-17 13:43:46,114 INFO HttpAspect:54 - ip=0:0:0:0:0:0:0:1
- 2019-12-17 13:43:46,114 INFO HttpAspect:56 - class_method=com.example.controller.TestController.t
- 2019-12-17 13:43:46,114 INFO HttpAspect:58 - args={}
- 2019-12-17 13:43:46,114 INFO HttpAspect:59 - >>>>>>>>>>>>>>
Gtihub 地址: github.com/liangtengyu
公众号 java宝典
本文分享自微信公众号 - java宝典(java_bible)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
 
  
  
  
 
 
  
 
 
 