高端企业网站设计公司,怎么帮公司做网站建设,网站制作需要多少钱k,超链接怎么做前言#xff1a;
在改造老项目登录功能的时候#xff0c;使用了过滤器对 token 进行有效性验证#xff0c;验证通过继续进行业务请求#xff0c;验证不通过则抛出校验异常。
过程#xff1a;
技术方案拟定后#xff0c;就着手开始改造#xff0c;一切都很顺畅#x…前言
在改造老项目登录功能的时候使用了过滤器对 token 进行有效性验证验证通过继续进行业务请求验证不通过则抛出校验异常。
过程
技术方案拟定后就着手开始改造一切都很顺畅可是在异常场景模拟的时候怎么也得不到想要的异常 code我在过滤器的校验中明明是抛出了异常为什么没有得到想要的结果呢
过滤器代码如下
Slf4j
//WebFilter(filterName myFilter, urlPatterns /*)
public class MyAuthenticationFilter implements Filter {//不拦截的 URLprivate final static String EXCLUDES_URI /api/workflow/*;Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//为了方便获取 header 信息 对 HttpServletRequest 进行强转HttpServletRequest request (HttpServletRequest) servletRequest;String requestUri request.getRequestURI();//需要忽略的 url 地址Pattern pattern Pattern.compile(EXCLUDES_URI);//是否放行boolean isExclude pattern.matcher(requestUri).find();//用户信息UserInfoVO userInfoVO null;if (!isExclude) {//校验认证信息userInfoVO validateAuthorization(request);if (ObjectUtil.isNull(userInfoVO)) {//校验认证信息 失败 可能解析 token 异常 可能没有解析到正确的工号throw new AuthorizationValidationException(ResultCode.CAS_AUTHORIZATION);}}//设置用户信息UserContextHolder.setUser(userInfoVO);filterChain.doFilter(servletRequest, servletResponse);}Overridepublic void destroy() {//将ThreadLocal数据清空UserContextHolder.remove();Filter.super.destroy();}/*** Description: 校验 Authorization* Date: 2024/4/3 10:25*/public UserInfoVO validateAuthorization(HttpServletRequest request) {//获取 AuthorizationString authorization request.getHeader(CommConstant.AUTHORIZATION);if (StringUtils.isBlank(authorization)) {StringBuffer requestUrl request.getRequestURL();log.info(Authorization 为空的请求url:{}, requestUrl);//Authorization 为空 没有登录return null;}//过滤器是 servlet 规范中定义的 不归 spring 容器管理 无法直接注入 spring 中的 bean 直接注入会为 null//只能够自己去 容器中获取 beanApplicationContext context WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());assert context ! null;AuthServiceImpl iAuthService (AuthServiceImpl) context.getBean(authServiceImpl);//检验 authorizationreturn iAuthService.validateToken(authorization);}
}配置过滤器代码
package com.zt.zteam.main.configurer;import com.zt.zteam.main.filter.MyAuthenticationFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;Configuration
public class MyAuthenticationFilterConfig {Bean(myFilterAuthentication)public FilterRegistrationBeanMyAuthenticationFilter filterAuthenticationRegistration() {//设置过滤器FilterRegistrationBeanMyAuthenticationFilter registration new FilterRegistrationBean();registration.setFilter(new MyAuthenticationFilter());//设置过滤器优先级 数字越小优先级越高registration.setOrder(-1);return registration;}}配置过滤方式二
过滤器类上加 WebFilter(filterName “myFilter”, urlPatterns “/*”) 注解同时在启动类上加 ServletComponentScan({“com.xxx.xxx.xxx.filter”}) 注解。
全局异常处理器代码如下
RestControllerAdvice(basePackages com.my.study)
//Order(Ordered.HIGHEST_PRECEDENCE)
Slf4j
public class GlobalExceptionHandler {/*** 交易异常*/ExceptionHandler(ValidateException.class)public Result? validateExceptionHandler(HttpServletRequest request, ValidateException e) {log.error(校验异常,方法{}, request.getRequestURI(), e);return ResultGenerator.genResult(e.getCode(), e.getMessage());}/*** 处理业务异常*/ExceptionHandler(ServiceException.class)public Result? bizExceptionHandler(HttpServletRequest request, ServiceException e) {log.error(业务异常,方法{}, request.getRequestURI(), e);return ResultGenerator.genResult(e.getCode(), e.getMessage());}/*** 处理空指针异常*/ExceptionHandler(NullPointerException.class)public Result? npeHandler(HttpServletRequest request, NullPointerException e) {log.error(空指针异常,方法{}, request.getRequestURI(), e);return ResultGenerator.genFailResult(e.getMessage());}/*** 处理其他异常*/ExceptionHandler(Exception.class)public Result? exceptionHandler(HttpServletRequest request, Exception e) {log.error(其它异常,方法{}, request.getRequestURI(), e);Result? result new Result();result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage(e.getMessage() --接口 [ request.getRequestURI() ] 内部错误请联系管理员);return result;}ExceptionHandler({BusinessException.class})public Result? businessException(HttpServletRequest request, Exception e) {log.error(业务处理异常信息,方法{},异常信息 ,request.getRequestURI(), e);Result? result new Result();result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage(e.getMessage());return result;}/*** 这个是valid注解校验参数时校验不通过的异常*/ExceptionHandler(MethodArgumentNotValidException.class)public Result? validateMethodExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException e) {log.error(请求参数异常,方法{}, request.getRequestURI(), e);Result? result new Result();result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage(e.getBindingResult().getFieldErrors().get(0).getDefaultMessage());return result;}/*** token 认证验证*/ExceptionHandler(AuthorizationValidationException.class)public Result? authorizationValidationExceptionHandler(HttpServletRequest request, Exception e) {log.error(CAS 认证异常,方法{}, request.getRequestURI(), e);Result? result new Result();result.setCode(ResultCode.UNAUTHORIZED).setMessage(e.getMessage() --接口 [ request.getRequestURI() ] 内部错误请联系管理员);return result;}}测试结果
{timestamp: 2024-04-16T02:04:48.09100:00,status: 500,error: Internal Server Error,message: ,path: /api/logout
}没有得到我们预期的 code401。
问题分析
看起来代码显示的抛出了异常也设置了全局异常处理器但是并没有返回想要的异常状态码至此感觉走到了死胡同此时想到了老办法debug 调试经过多次 debug 调试发现全局异常处理器没有拦截到任何异常这就很能说明问题了也就是全局异常处理器根本捕获不到过滤器 filter 抛出的异常那怎么办呢我们知道全局异常过滤器是一定可以捕获到 Controller 的异常的此时灵机一动当出现异常后在过滤器 filter 中使用 try catch 自己处理然后使用 forward 转发请求到指定 Controller 不就可以了吗方案有了着手开始测试。
注意ControllerAdvice 注解只处理经过 Controller 的异常不经过 Controller 的异常 ControllerAdvice 注解不进行处理。
调整后的过滤器 filter 代码如下
Slf4j
public class MyAuthenticationFilter implements Filter {//不拦截的 URLprivate final static String EXCLUDES_URI /api/workflow/*;Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//为了方便获取 header 信息 对 HttpServletRequest 进行强转HttpServletRequest request (HttpServletRequest) servletRequest;String requestUri request.getRequestURI();//需要忽略的 url 地址Pattern pattern Pattern.compile(EXCLUDES_URI);//是否放行boolean isExclude pattern.matcher(requestUri).find();//用户信息UserInfoVO userInfoVO null;try {if (!isExclude) {//校验认证信息userInfoVO validateAuthorization(request);if (ObjectUtil.isNull(userInfoVO)) {//校验认证信息 失败 可能解析 token 异常 可能没有解析到正确的工号throw new AuthorizationValidationException(ResultCode.CAS_AUTHORIZATION);}}//设置用户信息UserContextHolder.setUser(userInfoVO);filterChain.doFilter(servletRequest, servletResponse);} catch (AuthorizationValidationException e) {request.setAttribute(CommConstant.FILTER_ERROR, e);request.getRequestDispatcher(CommConstant.FILTER_ERROR_PATH).forward(request, servletResponse);} catch (IOException e) {e.printStackTrace();} catch (ServletException e) {e.printStackTrace();}}Overridepublic void destroy() {//将ThreadLocal数据清空UserContextHolder.remove();Filter.super.destroy();}/*** Description: 校验 Authorization* Date: 2024/4/3 10:25*/public UserInfoVO validateAuthorization(HttpServletRequest request) {//获取 AuthorizationString authorization request.getHeader(CommConstant.AUTHORIZATION);if (StringUtils.isBlank(authorization)) {StringBuffer requestUrl request.getRequestURL();log.info(Authorization 为空的请求url:{}, requestUrl);//Authorization 为空 没有登录return null;}//过滤器是 servlet 规范中定义的 不归 spring 容器管理 无法直接注入 spring 中的 bean 直接注入会为 null//只能够自己去 容器中获取 beanApplicationContext context WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());assert context ! null;AuthServiceImpl iAuthService (AuthServiceImpl) context.getBean(authServiceImpl);//检验 authorizationreturn iAuthService.validateToken(authorization);}
}请求转发的关键代码
//filterErrorpublic final static String FILTER_ERROR filterError;//filterError pathpublic final static String FILTER_ERROR_PATH /throw-error;//设置异常信息request.setAttribute(CommConstant.FILTER_ERROR, e);//转发request.getRequestDispatcher(CommConstant.FILTER_ERROR_PATH).forward(request, servletResponse);异常处理 Controller 代码
Slf4j
RestController
public class FilterExceptionController {ApiOperation(value 过滤器异常处理, produces application/json)RequestMapping(CommConstant.FILTER_ERROR_PATH)public ResultString testRedis(HttpServletRequest request) {Object attribute request.getAttribute(CommConstant.FILTER_ERROR);if(attribute instanceof AuthorizationValidationException){throw new AuthorizationValidationException(ResultCode.CAS_AUTHORIZATION);}throw new BusinessException(业务异常);}
}测试结果 总结
通过请求转发的方式我们解决了过滤器 filter 异常无法捕获的问题在转发的过程中我们尽量使用 request.getRequestDispatcher(“/path”).forward(request, response) 这种方式此方式只会在服务端内部转发客户端地址不会发生任何改变如果使用response.sendRedirect(“/path”) 进行请求转发客户端地址会发生改变。
在 Spring 应用中我们不建议优先使用过滤器 filter建议优先使用拦截器 Interceptor本文只是分享过滤器 filter 中的异常处理方式希望帮助到有需要的伙伴们。
过滤器和拦截器的区别传送门 Spring 拦截器实现请求拦截与参数处理【拦截器Interceptor和过滤器Filter的区别】
如有错误的地方欢迎指出纠正。