SpringBoot :API接口拦截器验证(Token验证)并将数据存入Request中供接口调用
一、为什么需要拦截器?在前后端分离的现在,项目中的所有的前端的页面都需要通过调用后台的Api进行获取数据接口的功能点不同,就会有很多种情况,比如说涉及敏感数据(登录,获取个人信息,个人金额修改)相关的接口需要token验证获取不敏感数据则不需要进行校验vue等前端调用后台api,如果没有引入(nginx),则有可能有跨域问题所以说需要一个拦截器去区分哪些路径下需要token校验,那...
·
一、为什么需要拦截器?
在前后端分离的现在,项目中的所有的前端的页面都需要通过调用后台的Api进行获取数据
接口的功能点不同,就会有很多种情况,比如说
- 涉及敏感数据(登录,获取个人信息,个人金额修改)相关的接口需要token验证
- 获取不敏感数据则不需要进行校验
- vue等前端调用后台api,如果没有引入(nginx),则有可能有跨域问题
所以说需要一个拦截器去区分哪些路径下需要token校验,那些不需要。
二、实现思路
- 新增一个配置类继承WebMvcConfigurer
- 在这个配置类中新增自定义逻辑的拦截器(实现HandlerInterceptor接口),同时设定哪些路径需要调用自定义拦截器
三、具体代码实现(示例代码:https://github.com/zz790609619/LeetCodeRecord.git)
配置类
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;
import java.util.List;
/**
* 拦截器(可做跨域,token验证等)
*/
@Configuration
public class WebRequestInterceptor implements WebMvcConfigurer {
/**
* 自定义拦截器()
* @param registry
*/
public void addInterceptors(InterceptorRegistry registry) {
// RequestInterceptor为具体拦截逻辑的执行类 实现了HandlerInterceptor接口
// addPathPatterns("/test/**") 意义是访问路径下/test 下所有的访问路径都需要被RequestInterceptor拦截
// excludePathPatterns 这个访问路径/test/exception则不在被RequestInterceptor拦截的范围
// /user/** user下所有路径都包含在内 例:/user/api 、/user/api/zz
// /user/* 只有user下一层路径包含在内 例:/user/api(包含) 、/user/api/zz(不包含)
// /test/queryUser接口则是token验证后,把token为xx的玩家信息放入Request中,方便接口拿取
registry.addInterceptor(new RequestInterceptor())
.addPathPatterns("/test/**")
.addPathPatterns("/test/queryUser")
.excludePathPatterns("/test/exception");
}
/**
* 跨域支持 比如说vue 的axios访问
* @param registry
*/
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.maxAge(3600 * 24);
}
/**
* 修改访问路径
* @param configurer
*/
public void configurePathMatch(PathMatchConfigurer configurer) {
// 设置为true后,访问路径后加/ 也能正常访问 /user == /user/
// configurer.setUseTrailingSlashMatch(true);
}
/**
* 内容协商机制,主要是方便一个请求路径返回多个数据格式
* @param configurer
*/
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
/**
* 处理异步请求的。只能设置两个值,一个超时时间(毫秒,Tomcat下默认是10000毫秒,即10秒),还有一个是AsyncTaskExecutor,异步任务执行器
* @param configurer
*/
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
// //设置超时时间
// configurer.setDefaultTimeout(1000000);
// //设置异步任务执行器
// configurer.setTaskExecutor(new AsyncTaskExecutor() {
// @Override
// public void execute(Runnable runnable, long l) {
//
// }
//
// @Override
// public Future<?> submit(Runnable runnable) {
// return null;
// }
//
// @Override
// public <T> Future<T> submit(Callable<T> callable) {
// return null;
// }
//
// @Override
// public void execute(Runnable runnable) {
//
// }
// });
}
/**
* 这个接口可以实现静态文件可以像Servlet一样被访问。
* @param configurer
*/
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
/**
* 增加转化器或者格式化器。这边不仅可以把时间转化成你需要时区或者样式。还可以自定义转化器和你数据库做交互,比如传进来userId,经过转化可以拿到user对象
* @param registry
*/
public void addFormatters(FormatterRegistry registry) {
}
/**
* 添加静态资源--过滤swagger-api (开源的在线API文档)
* @param registry
*/
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
public void addViewControllers(ViewControllerRegistry registry) {
}
public void configureViewResolvers(ViewResolverRegistry registry) {
}
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}
/**
* 配置消息转换器
* @param converters
*/
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
@Nullable
public Validator getValidator() {
return null;
}
@Nullable
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
自定义的拦截器
package com.example.demo.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.model.ResponseDto;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Enumeration;
public class RequestInterceptor implements HandlerInterceptor {
/**
* 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
* 返回值:
* true表示继续流程(如调用下一个拦截器或处理器);
* false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
//将头部信息都转换成map
JSONObject map = new JSONObject();
Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
String value = httpServletRequest.getHeader(key);
map.put(key, value);
}
map.put("token", httpServletRequest.getHeader("AUTH-TOKEN"));
//判断从前端传来的头部信息中AUTH-TOKEN的值是否与我们后台定义的token值一致
if("111".equals(map.get("token"))){
//token正确 继续下一步拦截器(如果有)
System.out.println("token is right");
//从Spring上下文中拿到UserMapper
UserMapper userMapper= ApplicationContextUtil.getBean(UserMapper.class);
//获取该token对应的用户信息
User user=userMapper.getUserByToken(String.valueOf(map.get("token")));
//将用户信息放入Request中
httpServletRequest.setAttribute("test",JSON.toJSONString(user));
return true;
}else{
//token错误 返回错误response
System.out.println("token is error");
PrintWriter writer = null;
try {
ResponseDto dto=new ResponseDto();
dto.setErrorCode(1002);
dto.setMessage("RequestInterceptor");
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setHeader("Content-Type","application/json");
writer = httpServletResponse.getWriter();
//将返回的错误提示压入流中
writer.write(JSON.toJSONString(dto));
writer.flush();
} catch (Exception e) {
} finally {
if (null != writer) {
writer.close();
}
return false;
}
}
}
/**
* 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
接口Controller
package com.example.demo.controller;
import com.aliyun.openservices.shade.com.alibaba.fastjson.JSON;
import com.example.demo.entity.model.ResponseDto;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping(value = "/test")
public class UserNodeController {
@RequestMapping(value = "/index")
public String index(){
return "/index";
}
/**
* 异常报错
* 访问路径:http://127.0.0.1:8090/test/exception
* 返回结果: {"errorCode": 1001,"message": "Exception:4","data": null }
*/
@RequestMapping(value = "/exception")
public String getException(){
int[] arr = {1, 2, 3};
System.out.println(arr[4]);
ResponseDto dto=new ResponseDto();
dto.setMessage("NoEnterGlobalException");
return JSON.toJSONString(dto);
}
/**
*
* 空指针异常报错
* 访问路径:http://127.0.0.1:8090/test/nullPointException
* 返回结果:{"errorCode": 1002,"message": "NullPointerException:null","data": null }
*/
@RequestMapping(value = "/nullPointException")
public ResponseDto getNullPointException(){
Object obj = null;
obj.toString();
ResponseDto dto=new ResponseDto();
dto.setMessage("NoEnterGlobalNullPointException");
return dto;
}
/**
* 获取全局变量
* 访问路径:http://127.0.0.1:8090/test/getGlobalParm
* 返回结果:{"ww":{"ww":"helloQ"}}
*/
@RequestMapping(value = "/getGlobalParm")
public String getGlobalParm(Model model){
Map<String, Object> map = model.asMap();
return JSON.toJSONString(map);
}
/**
* 获取预处理后的数据
* 访问地址及参数:http://127.0.0.1:8090/test/getPreprocessedData?helloA.errorCode=1&helloA.message=a&helloA.data=a&helloB.errorCode=2&helloB.message=b&helloB.data=b
* 返回结果:dtoA:{"data":"a","errorCode":1,"message":"a"},dtoB:{"data":"b","errorCode":2,"message":"b"}
*/
@RequestMapping(value = "/getPreprocessedData")
public String getPreprocessedData(@ModelAttribute("helloA")ResponseDto dtoA,@ModelAttribute("helloB")ResponseDto dtoB){
return "dtoA:"+JSON.toJSONString(dtoA)+",dtoB:"+JSON.toJSONString(dtoB);
}
@GetMapping("/queryUser")
public void queryUser(HttpServletRequest request,@RequestParam("token") String token){
//在拦截器中验证token并获取到这个token对应的信息
System.out.println(request.getAttribute("test"));
System.out.println(token);
}
}
全局异常处理类
package com.example.demo.config;
import com.example.demo.entity.model.ResponseDto;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalControllerAdvice {
/**
* 所有异常处理
* @param request
* @param e
* @return
* @throws Exception
*/
@ExceptionHandler(value = Exception.class)
public @ResponseBody
ResponseDto exceptionHandler(HttpServletRequest request,Exception e)throws Exception{
ResponseDto dto=new ResponseDto();
dto.setErrorCode(1001);
dto.setMessage("Exception:"+e.getMessage());
return dto;
}
/**
* 空指针异常处理
* @param request
* @param e
* @return
* @throws NullPointerException
*/
@ExceptionHandler(value = NullPointerException.class)
public @ResponseBody
ResponseDto exceptionHandler(HttpServletRequest request,NullPointerException e)throws NullPointerException{
ResponseDto dto=new ResponseDto();
dto.setErrorCode(1002);
dto.setMessage("NullPointerException:"+e.getMessage());
return dto;
}
/**
* 全局变量 拦截器
* @return
*/
@ModelAttribute(name="ww")
public Map<String,Object> globalData() {
HashMap<String, Object> map = new HashMap<>();
map.put("ww", "helloQ");
return map;
}
/**
* 数据预处理
* @param binder
*/
@InitBinder("helloA")
public void b(WebDataBinder binder) {
binder.setFieldDefaultPrefix("helloA.");
}
@InitBinder("helloB")
public void a(WebDataBinder binder) {
binder.setFieldDefaultPrefix("helloB.");
}
}
ApplicationContextUtil(在项目初始化的时候将Spring上下文放入ApplicationContextUtil中,方便后面定时器/拦截器等获取实体类bean)
import org.springframework.context.ApplicationContext;
public class ApplicationContextUtil{
private static ApplicationContext applicationContext;
//在项目初始化的时候将SpringApplication.run(DemoApplication.class, args)set进去
public static ApplicationContext setApplicationContext(ApplicationContext context){
this.applicationContext=context;
}
public static object getBean(String beanId){
return applicationContext.getBean(beanId);
}
public static <T>T getBean(Class<T> clazz){
return applicationContext.getBean(clazz);
}
}
四、测试结果
如配置类中设置的除了/test/exception ,其他的/test下的路径都应该被我们自定义拦截器拦截
-
访问路径:http://127.0.0.1:8090/test/exception
结果: -
访问路径:http://127.0.0.1:8090/test/nullPointException
输入正确的token的结果:
输入错误token值的结果:
-
http://127.0.0.1:8090/test/queryUser?token=111 验证用户信息能否能在接口中获取
控制台如图:
数据库如图:
更多推荐
已为社区贡献2条内容
所有评论(0)