拦截器代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("preHandle Running");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle Running");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion Running");
}
}
|
配置拦截器的代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/", "/login", "/css/**");
}
}
|
遇到的问题:
- WebConfig类上忘记
@Configuration
,导致配置的拦截器不生效。
拦截器原理
- 根据当前请求,找到
HandlerExecutionChain
(可以处理请求的handler以及handler的所有拦截器)
-
先来顺序执行 所有拦截器的 preHandle方法
- 如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
- 如果当前拦截器返回为false。直接倒序执行所有已经执行了的拦截器的afterCompletion
- 如果任何一个拦截器返回false。直接跳出不执行目标方法
-
所有拦截器都返回true。执行目标方法
-
倒序执行所有拦截器的postHandle方法。
-
前面的步骤有任何异常都会直接倒序触发afterCompletion
-
页面成功渲染完成以后,也会倒序触发afterCompletion
拦截器的应用
UserInfoInterceptor
这个拦截器是用于拦截token信息的,我现在逐行研究该拦截器的实现:
1
2
3
4
5
|
if (response.getStatus() == HttpServletResponse.SC_NOT_FOUND) {
return false;
}
|
这行代码是想处理404错误,目前的实现方案会导致调用接口时如果接口不存在,则返回空请求体。我建议是实现ErrorController解决该问题。
1
2
3
4
5
6
7
8
9
10
11
|
// 如果不是映射到方法直接通过
if (!(object instanceof HandlerMethod)) {
return true;
}
if (request.getRequestURI().contains("/internal/")) {
return true;
}
|
我不知道这两行代码的含义,我不确定在什么情况下不是映射到方法。而且,在我看来,我们应该在拦截器处时,排除掉静态资源的URL。同样的对于/internal
资源,我们可以在配置拦截器时排除掉。
1
2
3
4
5
6
7
8
9
10
11
|
//检查是否有passtoken注释,有则跳过认证
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
|
这一段代码是说,如果我们的方法被@PassToken
注解了,则直接放行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
String userJson = request.getHeader("user");
JSONObject user;
if (StringUtils.isEmpty(userJson)) {
if (StringUtils.isEmpty(request.getHeader(APICons.TOKEN))) {
throw new TokenNotFoundException("token required");
} else {
user = apiUtils.currentUser();
if (user == null) {
throw new PermissionException("not authorized");
}
}
} else {
try {
userJson = URLDecoder.decode(userJson, "utf-8");
user = JSONObject.parseObject(userJson);
} catch (UnsupportedEncodingException e) {
throw new ValidateException("user info not validate");
}
}
request.setAttribute(APICons.REQUEST_USER, user);
request.setAttribute(APICons.REQUEST_USER_ID, user.getString("id"));
request.setAttribute(APICons.REQUEST_COMPANY_ID, user.getString("companyId"));
|
接下来,我们尝试从Header中获取User信息,如果获取的用户信息为空,我们尝试获取Token信息,如果Token信息也为空,则我们抛出异常。如果Token信息不为框,则我们用Token从Redis中获取用户的信息;如果获取的用户信息不为空,则我们直接解析Token的信息。
最后将解析的信息塞到Attributes中,完美收工。
最后,对该拦截器的配置如下,配置的项过于粗糙,导致我们需要在拦截器中进行假设。
1
2
3
|
registry.addInterceptor(userInfoInterceptor).addPathPatterns("/**").order(3);
|
RequestInfoForwardInterceptor
该拦截器是为了让我们的所有返回体带上我们的trace code,核心代码如下:
1
2
3
|
response.setHeader(APICons.TRACKING_CODE, request.getHeader(APICons.TRACKING_CODE));
|