Filter是Servlet的規(guī)范旨别,它的定義如下:
public interface Filter {
public void init(FilterConfig filterConfig)throwsServletException;
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throwsIOException,ServletException;
public void destroy();
主要作用就是苍姜,對(duì)ServletRequest做預(yù)處理,執(zhí)行過程中任何時(shí)候都可以打斷炊苫,只要不執(zhí)行chain.doFilter()就不會(huì)再執(zhí)行后面的過濾器和請(qǐng)求的內(nèi)容。而在實(shí)際使用時(shí)冰沙,就要特別注意過濾鏈的執(zhí)行順序問題
而Spring MVC是基于Servlet來響應(yīng)用戶請(qǐng)求對(duì)侨艾,所以,在Spring中可以可以用Filter來干預(yù)Http請(qǐng)求拓挥,解決諸如跨域蒋畜、認(rèn)證方面的要求。例子如下:
@Component
public classSimpleCORSFilterimplementsFilter {
public voiddoFilter(ServletRequest req,ServletResponse res,FilterChain chain)throwsIOException,
ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Methods","POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age","3600");
response.setHeader("Access-Control-Allow-Headers","Token,Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since");
chain.doFilter(req,res);
}
public voidinit(FilterConfig filterConfig) {}
public voiddestroy() {}
}
而攔截器撞叽,是基于Spring IOC的姻成。可以在方法級(jí)別愿棋,對(duì)流程進(jìn)行預(yù)處理科展、后處理、綜合(前后同時(shí))處理糠雨。我們需要做的就是:
1才睹、創(chuàng)建自己的攔截器實(shí)現(xiàn)HandlerInterceptor接口
2.? 加入到攔截器鏈條,繼承WebMvcConfigurerAdapter類,重寫addInterceptors方法
? ? 將攔截器加入到攔截鏈條(需要添加攔截規(guī)則)琅攘,如下:
public class CustomInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion (HttpServletRequest?request, ?HttpServletResponse?response, ?Object?object, Exception?exception)throwsException?{
//在整個(gè)請(qǐng)求結(jié)束之后被調(diào)用垮庐,也就是在DispatcherServlet?渲染了對(duì)應(yīng)的視圖之后執(zhí)行(主要是用于進(jìn)行資源清理工作)
System.out.println("3.?整個(gè)請(qǐng)求結(jié)束之后被調(diào)用......CustomInterceptor1......");
}
@Override
publicvoidpostHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?object,?ModelAndView?view)
throwsException?{
//?請(qǐng)求處理之后進(jìn)行調(diào)用,但是在視圖被渲染之前
System.out.println("2.?請(qǐng)求處理之后進(jìn)行調(diào)用坞琴,但是在視圖被渲染之前......CustomInterceptor1......");
}
@Override
publicbooleanpreHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?object)throwsException?{
//?在請(qǐng)求處理之前進(jìn)行調(diào)用
System.out.println("1.?在請(qǐng)求處理之前進(jìn)行調(diào)用......CustomInterceptor1......");
//?只有返回true才會(huì)繼續(xù)向下執(zhí)行哨查,返回false取消當(dāng)前請(qǐng)求
returntrue;
}
@Configuration
publicclassWebAdapterextendsWebMvcConfigurerAdapter{
@Override
publicvoidaddInterceptors(InterceptorRegistry?registry)?{
//眾多的攔截器組成了一個(gè)攔截器鏈
/**
*?主要方法說明:
*?addPathPatterns?用于添加攔截規(guī)則
*?excludePathPatterns?用戶排除攔截
*/
registry.addInterceptor(newCustomInterceptor()).addPathPatterns("/*");
registry.addInterceptor(newCustomInterceptor2()).addPathPatterns("/*");
super.addInterceptors(registry);
}
或者直接寫一個(gè)完整的自定義攔截器
@Aspect
@Component
public classControllerInterceptor {
private static finalLoggerlogger= LoggerFactory.getLogger(ControllerInterceptor.class);
@Value("${spring.profiles}")
privateStringenv;
/**
* 定義攔截規(guī)則:攔截com.xjj.web.controller包下面的所有類中,有@RequestMapping注解的方法剧辐。
*/
@Pointcut("execution(* com.xjj.web.controller..*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)")
public voidcontrollerMethodPointcut(){}
/**
* 攔截器具體實(shí)現(xiàn)
*@parampjp
*@returnJsonResult(被攔截方法的執(zhí)行結(jié)果寒亥,或需要登錄的錯(cuò)誤提示。)
*/
@Around("controllerMethodPointcut()")//指定攔截器規(guī)則荧关;也可以直接把“execution(* com.xjj.........)”寫進(jìn)這里
public Object Interceptor(ProceedingJoinPoint pjp){
long beginTime = System.currentTimeMillis();
Method Signature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();//獲取被攔截的方法
String methodName = method.getName();//獲取被攔截的方法名
Set allParams =newLinkedHashSet<>();//保存所有請(qǐng)求參數(shù)溉奕,用于輸出到日志中
logger.info("請(qǐng)求開始,方法:{}",methodName);
Object result =null;
Object[] args = pjp.getArgs();
for(Object arg : args){
//logger.debug("arg: {}", arg);
if(arginstanceofMap) {
//提取方法中的MAP參數(shù)忍啤,用于記錄進(jìn)日志中
@SuppressWarnings("unchecked")
Map map = (Map) arg;
allParams.add(map);
}else if(arginstanceofHttpServletRequest){
HttpServletRequest request = (HttpServletRequest) arg;
if(isLoginRequired(method)){
if(!isLogin(request)){
result =newJsonResult(ResultCode.NOT_LOGIN,"該操作需要登錄加勤!去登錄嗎?", null);
}
}
//獲取query string 或 posted form data參數(shù)
Map paramMap = request.getParameterMap();
if(paramMap!=null&& paramMap.size()>0){
allParams.add(paramMap);
}
}else if(arginstanceofHttpServletResponse){
//do nothing...
}else{
//allParams.add(arg);
}
}
try{
if(result ==null){
// 一切正常的情況下同波,繼續(xù)執(zhí)行被攔截的方法
result = pjp.proceed();
}
}catch(Throwable e) {
logger.info("exception: ",e);
result =newJsonResult(ResultCode.EXCEPTION,"發(fā)生異常:"+e.getMessage());
}
if(resultinstanceofJsonResult){
longcostMs = System.currentTimeMillis() - beginTime;
logger.info("{}請(qǐng)求結(jié)束胸竞,耗時(shí):{}ms",methodName,costMs);
}
returnresult;
}
/**
* 判斷一個(gè)方法是否需要登錄
*@parammethod
*@return
*/
private booleanisLoginRequired(Method method){
if(!env.equals("prod")){//只有生產(chǎn)環(huán)境才需要登錄
return false;
}
booleanresult =true;
if(method.isAnnotationPresent(Permission.class)){
result = method.getAnnotation(Permission.class).loginReqired();
}
returnresult;
}
//判斷是否已經(jīng)登錄
private booleanisLogin(HttpServletRequest request) {
return true;
/*String token = XWebUtils.getCookieByName(request, WebConstants.CookieName.AdminToken);
if("1".equals(redisOperator.get(RedisConstants.Prefix.ADMIN_TOKEN+token))){
return true;
}else {
return false;
}*/
}
}