一般springBoot自带的全局异常捕获机制都是在业务层发生的异常来进行捕获的,因为过滤器的执行顺序是在全局异常机制启动之前执行的,所以一旦过滤器中发生异常,全局异常捕获机制无法使用
现在有一个围魏救赵的思路
在过滤器中try-catch掉需要捕获的异常,然后转发到controller在重新抛出,这样全局异常机制可以起作用,返回自定义信息
举个例子:
SpringSecurity使用过滤器链来实现用户校验,一旦签名出错,过滤器链中抛出响应的异常,我们不想抛出异常,而是返回相关错误信息。此时使用上述思路很容易做到
下面是真实代码:
这是一个过滤器,我们try-catch掉异常,然后转发给相应的controller
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private static final String LOGIN_URL = "/login";
private static String token = null;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
String tokenHeader = request.getHeader(TokenEnum.TOKEN_HEADER.getValue());
// 如果请求头中没有Authorization信息或者是登录接口直接放行了
if (tokenHeader == null || !tokenHeader.startsWith(TokenEnum.TOKEN_PREFIX.getValue()) || request.getRequestURL().toString().contains(LOGIN_URL)) {
chain.doFilter(request, response);
return;
}
// 如果请求头中有token,则进行解析,并且设置认证信息
try {
if (JWTAuthorizationFilter.token != null) {
refreshToken(token);
SecurityContextHolder.getContext().setAuthentication(getAuthentication(token));
} else {
refreshToken(tokenHeader);
SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));
}
} catch (RefreshTokenException | ExpiredJwtException e) {
// 异常捕获,发送到expiredJwtException
request.setAttribute("expiredJwtException", e);
//将异常分发到/expiredJwtException控制器
request.getRequestDispatcher("/expiredJwtException").forward(request, response);
} catch (AccessDeniedException | SignatureException e) {
// 异常捕获,发送到signatureException
request.setAttribute("signatureException", e);
//将异常分发到/signatureException控制器
request.getRequestDispatcher("/signatureException").forward(request, response);
}
super.doFilterInternal(request, response, chain);
}
异常controller
package com.hongseng.app.controller;
import com.hongseng.app.config.exception.RefreshTokenException;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @program: fire_control
* @description:
* @author: fbl
* @create: 2021-01-18 07:54
**/
@RestController
public class JwtExceptionController {
/**
* 重新抛出异常
*/
@RequestMapping("/expiredJwtException")
public void expiredJwtException(HttpServletRequest request) throws ExpiredJwtException, RefreshTokenException {
if (request.getAttribute("expiredJwtException") instanceof ExpiredJwtException) {
throw ((ExpiredJwtException) request.getAttribute("expiredJwtException"));
} else {
throw new RefreshTokenException();
}
}
@RequestMapping("/signatureException")
public void signatureException(HttpServletRequest request) throws SignatureException {
throw ((SignatureException) request.getAttribute("signatureException"));
}
}
然后在使用全局异常机制捕获并返回自定义信息即可
package com.hongseng.app.config.exception;
import enums.ErrorCodeEnum;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import result.Result;
/**
* @program: fire_control
* @description: 处理自定义的业务异常
* @author: fbl
* @create: 2021-01-15 16:21
**/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* token过期
*
* @return
*/
@ExceptionHandler(value = {ExpiredJwtException.class, RefreshTokenException.class})
@ResponseBody
public Result expiredJwtException() {
return Result.failure(ErrorCodeEnum.SYS_ERR_TOKEN_EXPIRED);
}
/**
* token错误
*
* @return
*/
@ExceptionHandler(value = SignatureException.class)
@ResponseBody
public Result signatureException() {
return Result.failure(ErrorCodeEnum.SYS_ERR_TOKEN_SIGNATURE);
}
}