SpringBoot中自定义拦截器 (Token校验与放行、反馈前端)

简介

后端框架:springboot
数据库:clickhouse
本系统是针对大数据进行分析的可视化系统,涉及到接口访问过程中token的校验与放行等问题,本文解决两个问题:
1、token校验,具体思路为:

  • 如果接口不带token数据&该接口为登录接口,放行,将token和token过期时间存入数据库;
  • 如果接口不带token数据&该接口为登录接口,拦截,提示”未登录或token过期“
  • 如果接口带token数据,数据库中查询该token过期时间,如果当前系统时间没有超过token过期时间,放行,并对token做续期操作;否则,拦截,提示”未登录或token过期“

2、拦截器完成拦截操作后,如何反馈数据给前端

  • 如果不做处理,return false,前端不会接受到任何数据
  • 此部分代码片段如下
PrintWriter writer = response.getWriter();
					writer.println(new Response(ExceptionMsg.TokenTimeOut));
					writer.flush();
					writer.close();

详细代码

1、clickhouse中token_record表结构

在clickhouse中生成一个token记录表token_record,包括token和outtime两个数据列。

-- sys.token_record definition

CREATE TABLE sys.token_record
(

    `token` String,

    `outtime` DateTime
)
ENGINE = ReplacingMergeTree
PRIMARY KEY token
ORDER BY token
SETTINGS index_granularity = 8192;

2、设置拦截器

import javax.annotation.Resource;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Resource
    private CorsInterceptor corsInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 设置拦截类
        registry.addInterceptor(corsInterceptor).addPathPatterns("/**");
    }

}

3、自定义拦截器,关于token校验、放行、反馈前端的具体实现

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import com.fri.mapper.master.UserMapper;
import com.fri.util.JwtSysuserTokenUtils;
import com.fri.util.result.ExceptionMsg;
import com.fri.util.result.Response;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;

@Component
public class CorsInterceptor implements HandlerInterceptor {
	@Value("${token.expiration}") 
	private String expirationString;
//	private static final long EXPIRATION = 60L;

	@Autowired
	private UserMapper userMapper;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
		// 返回重新

		response.setHeader("Access-Control-Allow-Origin", "*");
		response.setHeader("Access-Control-Allow-Credentials", "true");
		response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");
		response.setHeader("Access-Control-Max-Age", "86400");
		response.setHeader("Access-Control-Allow-Headers", "*");
		response.setCharacterEncoding("UTF-8");
		response.setContentType("application/json;charset=UTF-8");

		String path = request.getServletPath();

		String tokenHeader = request.getHeader(JwtSysuserTokenUtils.TOKEN_HEADER);
		// 如果有头部有token
		if (!StringUtils.isBlank(tokenHeader)) {

			String token = tokenHeader.replace(JwtSysuserTokenUtils.TOKEN_PREFIX, "");
			try {
				Claims tokenBody = JwtSysuserTokenUtils.getTokenBody(token);
				System.out.println("tokenBody" + tokenBody);
				// 如果没有超过token过期时间,续期
				if (tokenBody != null) {
					Date updateTokenTime = new Date(System.currentTimeMillis() + Long.valueOf(expirationString) * 1000);
//					System.out.println("当前访问时间:" + new Date(System.currentTimeMillis()));
//					System.out.println("更新时间:" + updateTokenTime);
					userMapper.updateToken(tokenHeader, updateTokenTime);

				} else {
					// 删除token记录
					PrintWriter writer = response.getWriter();
					writer.println(new Response(ExceptionMsg.TokenTimeOut));
					writer.flush();
					writer.close();
					userMapper.deleteToken(tokenHeader);
					return false;
				}

			} catch (ExpiredJwtException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

		} else {
			if (!path.contains("login")) {
				try {
					PrintWriter writer = response.getWriter();
					writer.println(new Response(ExceptionMsg.TokenTimeOut));
					writer.flush();
					writer.close();
					userMapper.deleteToken(tokenHeader);
					return false;
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		// OPTIONS
		if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
			response.setStatus(HttpStatus.NO_CONTENT.value());
			return false;
		}

		return true;

	}
}

JWT生成token工具类

import java.util.Date;
import java.util.HashMap;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtSysuserTokenUtils {
	
	public static final String TOKEN_HEADER = "Authorization";
	public static final String TOKEN_PREFIX = "Bearer ";
	// 秘钥
	private static final String SECRET = "jwtSECRET";
	// 签发者
	private static final String ISS = "freen";

	// 角色的key
	private static final String ROLE_CLAIMS = "rol";

	// 过期时间是3600秒,既是1个小时
//    private static final long EXPIRATION = 3600L;
//	private static final long EXPIRATION = 60L;

	// 选择了记住我之后的过期时间为7天
//    private static final long EXPIRATION_REMEMBER = 604800L;
	private static final long EXPIRATION_REMEMBER = 3600L;

	// 创建token
	public static String createToken(String username, String role, boolean isRememberMe,String expirationString) {
		long EXPIRATION = Long.valueOf(expirationString);
		long expiration = isRememberMe ? EXPIRATION_REMEMBER : EXPIRATION;
		HashMap<String, Object> map = new HashMap<>();
		System.out.println("当前时间"+new Date(System.currentTimeMillis()));
		System.out.println("token过期时间"+new Date(System.currentTimeMillis() + expiration * 1000));
		map.put(ROLE_CLAIMS, role);
		return Jwts.builder().signWith(SignatureAlgorithm.HS512, SECRET).setClaims(map).setIssuer(ISS)
				.setSubject(username).setIssuedAt(new Date())
				.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)).compact();
	}

	// 从token中获取用户名
	public static String getUsername(String token) {
		return getTokenBody(token).getSubject();
	}

	// 获取用户角色
	public static String getUserRole(String token) {
		return (String) getTokenBody(token).get(ROLE_CLAIMS);
	}

	// 是否已过期
	public static boolean isExpiration(String token) {
		Claims tokenBody = getTokenBody(token);
		if (tokenBody == null) {
			return true;
		}
		return getTokenBody(token).getExpiration().before(new Date());
	}

	public static Claims getTokenBody(String token) {
		Claims claims = null;
		try {
			claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
		} catch (ExpiredJwtException e) {
//			e.printStackTrace();
			System.out.println("令牌过期");
		}
		return claims;
	}
}

上一篇:神经网络学习小记录1——Pytorch当中Tensorboard的使用


下一篇:聊聊xxl-job-executor-go