请先看下面代码:
package tech.xueyao.filter.interceptor;
import tech.xueyao.contant.properties.SystemProperties;
import tech.xueyao.exception.NoPermissionException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @Note:
* @auther: Simon.Xue
* @create: 2019/11/8 12:07
*/
@Component
publicclass CheckSignInterceptor implements HandlerInterceptor {
privatestatic Logger logger = LoggerFactory.getLogger(CheckSignInterceptor.class);
@Autowired
private SystemProperties systemProperties;
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
Exception arg3) throws Exception {
}
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
ModelAndView arg3) throws Exception {
}
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse response, Object object)
throws Exception {
String token = req.getHeader("token");
String current_timestamp = req.getHeader("current-timestamp");
String nonce_str = req.getHeader("nonce-str");
String sign = req.getHeader("sign");
logger.debug("token = {}, current_timestamp = {}, nonce_str = {}, sign = {}"
, token, current_timestamp, nonce_str, sign);
if (StringUtils.isEmpty(current_timestamp)
|| current_timestamp.length() != 13
|| StringUtils.isEmpty(nonce_str)
|| nonce_str.length() < 16
|| StringUtils.isEmpty(sign)) {
//返回调用方,没有接口权限
logger.error("返回调用方信息:没有接口权限");
thrownew NoPermissionException("没有接口访问权限");
}
// 验证sign签名
String apiSecurityKey = systemProperties.getSecurity().getMd5ValidKey();
token = StringUtils.isEmpty(token) ? "" : token;
String waitSignStr =
token + current_timestamp.substring(0, 10) + nonce_str.substring(0, 16) + apiSecurityKey;
String mySign = DigestUtils.md5Hex(waitSignStr);
if (!mySign.equalsIgnoreCase(sign)) {
logger.error("调用接口时,验证签名:不一致");
thrownew NoPermissionException("签名不一致");
}
returntrue;
}
}
以下是对这段 Java 代码的分析:
这段代码定义了一个名为CheckSignInterceptor
的拦截器,它实现了 Spring 的HandlerInterceptor
接口。该拦截器的主要功能是在处理 HTTP 请求之前,对请求头中的签名信息进行验证,以确保请求具有合法的访问权限。
这段代码是定义一个拦截器,实现了HandlerInterceptor
接口,主要是在处理HTTP请求之前,对请求头的中签名信息进行校验,只有检验通过,才能访问接口。注意,肯定有不需要检验的接口,如登录功能,所以需要配置忽略拦截路径,本文不介绍。
@Component
public class CheckSignInterceptor implements HandlerInterceptor {
@Component
:这是 Spring 的注解,说明该类为一个组件,程序启动时,Spring会自己扫描管理它。implements HandlerInterceptor
,需要实现接口中的三个方法。HandlerInterceptor
方法实现afterCompletion
方法,该方法在接口请求完成后调用。
postHandle
方法,该方法在接口请求后、视图渲染之前调用。
preHandle
方法,该方法在接口请求前调用,因为我们需要判断是否有权限访问接口,所以我们要在之前做校验判断。
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse response, Object object)
throws Exception {
String token = req.getHeader("token");
String current_timestamp = req.getHeader("current-timestamp");
String nonce_str = req.getHeader("nonce-str");
String sign = req.getHeader("sign");
logger.debug("token = {}, current_timestamp = {}, nonce_str = {}, sign = {}"
, token, current_timestamp, nonce_str, sign);
if (StringUtils.isEmpty(current_timestamp)
|| current_timestamp.length() != 13
|| StringUtils.isEmpty(nonce_str)
|| nonce_str.length() < 16
|| StringUtils.isEmpty(sign)) {
//返回调用方,没有接口权限
logger.error("返回调用方信息:没有接口权限");
thrownew NoPermissionException("没有接口访问权限");
}
// 验证sign签名
String apiSecurityKey = systemProperties.getSecurity().getMd5ValidKey();
token = StringUtils.isEmpty(token) ? "" : token;
String waitSignStr =
token + current_timestamp.substring(0, 10) + nonce_str.substring(0, 16) + apiSecurityKey;
String mySign = DigestUtils.md5Hex(waitSignStr);
if (!mySign.equalsIgnoreCase(sign)) {
logger.error("调用接口时,验证签名:不一致");
thrownew NoPermissionException("签名不一致");
}
returntrue;
}
上面代码步骤:
token
、current-timestamp
、nonce-str
和sign
信息。current-timestamp
为空或长度不为 13,nonce-str
为空或长度小于 16,sign
为空,则抛出NoPermissionException
异常,代表没有接口权限。SystemProperties
中获取加密密钥getMd5ValidKey,
此密钥为固定的常量。waitSignStr
,并使用 MD5 算法生成最终的签名mySign
。mySign
和请求头中的传来的签名sign
比较,如果不一致,则抛出NoPermissionException
异常,代表没有接口权限。true
,代表有接口权限,继续执行业务代码。在不同的公司中,我都看到相似的业务逻辑,都会对接口做一些签名校验,本篇文章中,每次都要计算签名,这种方法并不很好,建议把生成的签名信息放在Redis缓存中,这是现在比较流行的做法。