教你2步完成springboot的自定义拦截器,拦截前端请求。
1、应用场景
需要对前端请求进行统一拦截,校验用户是否登录;或请求的签名合法性;主要应用到两个类:
第一个:WebMvcConfigurer,第二个:HandlerInterceptor。
2、实现
首先实现WebMvcConfigurer,重写addInterceptors()方法,我这里的SignInterceptor类是自定义的类,下面再讲。后面的/** 表示对所有请求进行拦截,可以根据自己的实际场景更改。
import com.hdx.admin.biz.annotation.LoginUserHandlerMethodArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* 定义执行的拦截器
* @Author
* @Date
*/
@Configuration
public class AppMvcConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SignInterceptor()).addPathPatterns("/**");
}
}
第二步,自己定义一个要执行的类(我定义的是SignInterceptor)实现HandlerInterceptor,重写其中的perHandle()方法,这个里面就可以写自己的业务实现了。static静态块中定义了不需要进行拦截处理的接口地址。
import com.ht.publicc.common.constans.SystemConstant;
import org.apache.commons.lang3.StringUtils;
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.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* 系统接口签名拦截
*
* @Author
*/
public class SignInterceptor implements HandlerInterceptor {
Logger logger = LoggerFactory.getLogger(SignInterceptor.class);
private static Map<String, String> filterMap = new HashMap<>();
/**
* 请求白名单,这里的请求接口将直接跳过该拦截器。
* 不进行登录校验与签名校验
*/
static {
filterMap.put("/demoApp/login", "登录");
filterMap.put("/demoApp/logout", "退出登录");
filterMap.put("/demoApp/captchaImage", "验证码");
filterMap.put("/demoAdmin/login", "登录");
filterMap.put("/demoAdmin/logout", "退出登录");
filterMap.put("/demoAdmin/captchaImage", "验证码");
filterMap.put("/demoAdmin/tool/gen/batchGenCode", "验证码");
filterMap.put("/captchaImage", "验证码");
}
/**
* 重写拦截器方法
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String url = request.getRequestURI();
if (filterMap.containsKey(url)) {
return Boolean.TRUE;
}
//业务实现写在这里
return Boolean.TRUE;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
下面的是我的实现,可以参考。不可以直接使用。
import com.hdx.admin.manager.constans.SystemConfig;
import com.hdx.admin.manager.redis.RedisKeyUtil;
import com.hdx.admin.manager.redis.RedisUtil;
import com.hdx.admin.manager.system.AppContext;
import com.hdx.admin.manager.system.UserTokenManager;
import com.hdx.system.common.pojo.bo.SysUserBO;
import com.ht.publicc.common.appexception.AppErrorCodeEnum;
import com.ht.publicc.common.appexception.AppErrorMessage;
import com.ht.publicc.common.appexception.AppException;
import com.ht.publicc.common.constans.SystemConstant;
import com.ht.publicc.common.util.MD5Util;
import org.apache.commons.lang3.StringUtils;
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.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* 系统接口签名拦截
*
* @Author
*/
public class SignInterceptor implements HandlerInterceptor {
Logger logger = LoggerFactory.getLogger(SignInterceptor.class);
private static Map<String, String> filterMap = new HashMap<>();
/**
* 请求白名单,这里的请求接口将直接跳过该拦截器。
* 不进行登录校验与签名校验
*/
static {
filterMap.put("/demoApp/login", "退出登录");
filterMap.put("/demoApp/logout", "退出登录");
filterMap.put("/demoApp/captchaImage", "验证码");
filterMap.put("/demoAdmin/login", "登录");
filterMap.put("/demoAdmin/logout", "退出登录");
filterMap.put("/demoAdmin/captchaImage", "验证码");
filterMap.put("/demoAdmin/tool/gen/batchGenCode", "验证码");
filterMap.put("/captchaImage", "验证码");
}
/**
* 重写拦截器方法
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String url = request.getRequestURI();
if (filterMap.containsKey(url)) {
return Boolean.TRUE;
}
//签名类型的key
String signKey = RedisKeyUtil.getFilterUrlKey(SystemConstant.STR_1, url);
//登录类型的key
String loginKey = RedisKeyUtil.getFilterUrlKey(SystemConstant.STR_2, url);
// String token = request.getHeader(config.HEAD_TOKEN_NAME)
RedisUtil redis = AppContext.getBean(RedisUtil.class);
//是否验证签名 签名开关等于0 且 redis中没有该过滤的url,则验证签名
if (SystemConfig.getSignSwitch() && StringUtils.isEmpty(redis.get(signKey))) {
checkSign(request);
}
//是否验证登录状态 验证登录开关等于0开启 且 redis中没有该url的过滤列表,则验证登录信息
if (SystemConfig.getLoginSwitch() && StringUtils.isEmpty(redis.get(loginKey))) {
checkLogin(request);
}
return Boolean.TRUE;
}
private void checkSign(HttpServletRequest request) throws AppException {
String sign = request.getHeader(SystemConstant.HEAD_SIGN).toUpperCase(Locale.ROOT);
String timestamp = request.getHeader(SystemConstant.TIMESTAMP_NAME);
if (StringUtils.isEmpty(sign)) {
throw new AppException(AppErrorCodeEnum.CUSTOM_ERROR, AppErrorMessage.SIGN_ERROR);
}
if (StringUtils.isEmpty(timestamp)) {
throw new AppException(AppErrorCodeEnum.CUSTOM_ERROR, AppErrorMessage.TIMESTAMP_ERROR);
}
StringBuffer buffer = new StringBuffer(SystemConfig.secretKey).append(timestamp);
String sysSign = MD5Util.encryptionToMD5(buffer.toString()).toUpperCase(Locale.ROOT);
if (!sysSign.equals(sign)) {
logger.error("- SignInterceptor.checkSign - 签名错误,请求已被拦截 - 参数 - sign:{}, timestamp:{}, sysSign :{}", sign, timestamp, sysSign);
throw new AppException(AppErrorCodeEnum.CUSTOM_ERROR, AppErrorMessage.SIGN_ERROR);
}
}
private void checkLogin(HttpServletRequest request) throws AppException {
String token = request.getHeader(SystemConstant.TOKEN_NAME);
if (StringUtils.isEmpty(token)) {
logger.error("SignInterceptor.checkLogin - 请求token为空,登录验证不通过");
throw new AppException(AppErrorCodeEnum.UN_LOGIN);
}
SysUserBO userBO = UserTokenManager.getInstance().getLoginUser(token, request);
if (userBO == null) {
logger.error("SignInterceptor.checkLogin - 登录验证不通过 - token={}", token);
throw new AppException(AppErrorCodeEnum.UN_LOGIN);
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}