简介
后端框架: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;
}
}