问题复现

问题复现

发送了一个请求,但拦截器拦截了两个请求,打断点一看

第一次请求

第二次请求

好家伙,还拦截了/error,查资料才发现:

SpringBoot 默认提供了一个全局的 handler 来处理所有的 HTTP 错误, 并把它映射为 /error。当发生一个错误,例如系统异常抛出了Exception,SpringBoot会将页面重定向到 /error 中。

所以当请求/user/generate时,拦截器拦截该请求,处理请求时发生错误,默认全局的 handler把它映射为/error,因此拦截器会再次拦截/error请求。这会多进行一次系统处理,消耗了宝贵的系统资源。

解决方法

需要关闭工程中的资源文件建立映射

1
spring.resources.add-mappings=false
1
2
3
spring:
resources:
add-mappings: false

效果

只log.error了一次图中所示的内容

效果

问题代码

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.gxmzu.score.interceptor;

import com.gxmzu.score.domain.entity.User;
import com.gxmzu.score.exception.AccessDeniedException;
import com.gxmzu.score.service.TokenService;
import com.gxmzu.score.service.VerifyUser;
import com.gxmzu.score.utils.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* @Author: https://github.com/gxmzu
* @Date: 2022/11/10/21:00
* @Description: 全局拦截器
*/
public class GlobalInterceptor implements HandlerInterceptor {

private static final Logger log = LoggerFactory.getLogger(GlobalInterceptor.class);

@Resource
private TokenService tokenService;

@Resource
private VerifyUser verifyUser;

/**
* 拦截所有请求,只有符合要求才放行
* {@link com.gxmzu.score.config.SpringMvcConfig} 设置匿名访问
* {@link com.gxmzu.score.service.VerifyUser}查看对应的角色接口权限
*
* @param request 请求
* @param response 响应
* @param handler 处理器
* @return 返回true表示通过该请求
* @throws Exception 异常处理
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
User user = tokenService.getUser(request);
String url = request.getRequestURI();
if (user == null) {
throw new AccessDeniedException("未认证", HttpStatus.UNAUTHENTICATED);
}
//验证用户请求有效性,只有所属的角色才可以使用对应的接口
if (!verifyUser.verify(url, user)) {
log.error("用户:{" + user.getUserName() + "}请求越权接口");
throw new AccessDeniedException("未授权", HttpStatus.UNAUTHORIZED);
}
// 验证令牌有效期,只有离过期还差20分钟内查刷新token
tokenService.verifyToken(user);
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

}
}
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package com.gxmzu.score.exception;

import com.gxmzu.score.domain.AjaxResult;
import com.gxmzu.score.utils.HttpStatus;
import com.gxmzu.score.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

/**
* @Author: https://github.com/gxmzu
* @Date: 2022/11/24
* @Description: 全局异常处理
*/
@RestControllerAdvice
public class GlobalExceptionHandler extends RuntimeException {

private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

/**
* 权限校验异常
*/
@ExceptionHandler(AccessDeniedException.class)
public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
Integer code = e.getCode();
log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(HttpStatus.ERROR, e.getMessage());
}

/**
* 请求方式不支持
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
return AjaxResult.error(HttpStatus.NO_SUPPORT_REQUEST_METHOD, e.getMessage());
}

/**
* 业务异常
*/
@ExceptionHandler(ServiceException.class)
public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) {
log.error(e.getMessage(), e);
Integer code = e.getCode();
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(HttpStatus.ERROR, e.getMessage());
}

/**
* 拦截未知的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return AjaxResult.error(HttpStatus.ERROR, e.getMessage());
}

/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(HttpStatus.ERROR, e.getMessage());
}

/**
* 自定义验证异常
*/
@ExceptionHandler(BindException.class)
public AjaxResult handleBindException(BindException e) {
log.error(e.getMessage(), e);
String message = e.getAllErrors().get(0).getDefaultMessage();
return AjaxResult.error(HttpStatus.ERROR, message);
}

/**
* 自定义验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error(e.getMessage(), e);
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return AjaxResult.error(HttpStatus.ERROR, message);
}

}

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.gxmzu.score.exception;

/**
* @Author: https://github.com/gxmzu
* @Date: 2022/11/13
* @Description: 权限校验异常类
*/
public class AccessDeniedException extends RuntimeException {

/**
* 错误码
*/
private Integer code;

/**
* 错误提示
*/
private String message;

public AccessDeniedException() {
}

public AccessDeniedException(String msg, Integer code) {
this.code = code;
this.message = msg;
}

public Integer getCode() {
return this.code;
}

public void setCode(Integer code) {
this.code = code;
}

@Override
public String getMessage() {
return this.message;
}

public void setMessage(String message) {
this.message = message;
}
}

参考文章

springboot全局异常处理中的404的/error重复拦截问题