统一用户登录权限验证
统一数据格式返回
统一异常处理
springBoot拦截器 实现 统一用户登录权限验证
1. 自定义拦截器
//自定义拦截器
public class LoginInterceptor implements HandlerInterceptor {
//调用目标方法之前执行的方法
//此方法返回 boolean 类型的值:
// 如果返回的是true 表示(拦截器)验证成功,继续走后续的流程,执行目标方法;
// 如果返回false,表示拦截器执行失败,验证未通过,后续的流程和目标方法不要执行了
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//用户登录的判断业务
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("session_userinfo") != null) {
//用户已经登陆
return true;
}
// response.sendRedirect("https://www.baidu.com");
response.setStatus(401);
return false;
}
}
2. 将自定义拦截器配置到系统配置项,并设置合理的拦截规则
package com.example.demo.config;
import com.example.demo.common.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(new LoginInterceptor()); // 都可以,推荐使用注入的方式
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // /** 拦截所有的url, /* 一级 /** 多级
.excludePathPatterns("/user/login") // 排除url/user/login 不拦截该路径
.excludePathPatterns("/user/reg")
.excludePathPatterns("/image/**"); //排除 image 文件夹下的所有文件
}
}
拦截器实现原理
统一异常处理
- 创建一个异常处理类
@ControllerAdvice
public class MyExceptionAdvice {
}
- 创建异常检测的类和处理业务方法
@ControllerAdvice
@ResponseBody
public class MyExceptionAdvice {
///处理空指针异常
@ExceptionHandler(NullPointerException.class)
public HashMap<String, Object> doNullPointerException(NullPointerException e) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", -1);
result.put("msg", "空指针:" + e.getMessage());
result.put("data", null);
return result;
}
}
//默认异常处理
//当具体的异常匹配不到时,会执行此方法
@ExceptionHandler(Exception.class)
public HashMap<String, Object> doException(Exception e) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", -1);
result.put("msg", "Exception:" + e.getMessage());
result.put("data", null);
return result;
}
统一数据格式返回
统一数据格式返回的优点:
- 降低前端和后端沟通的成本
- 方便前端程序员和后端程序员写公共的代码
统一数据格式返回(强制性统一数据返回)【在返回数据之前进行数据重写】:
//统一数据格式处理
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
//实现 ResponseBodyAdvice 接口 必须重写 supports 和 beforeBodyWrite 方法
//内容是否需要重写
/**
* 是否执行 beforeBodyWrite 方法,true执行,重写返回结果
* @param returnType
* @param converterType
*/
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
/**
* 返回数据之前进行数据重写
* @param body 原始返回值
* @param returnType
*
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 先定义:标准返回格式为 HashMap<String, Object> -> code,msg,data
if (body instanceof HashMap) {
return body;
}
//重写返回结果,让其返回一个统一的数据格式
HashMap<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("msg", "");
result.put("data", body);
return result;
}
}
统一数据格式返回在遇到String类型返回时会报错:
@RequestMapping("/sayHi")
public String sayHi() {
return "say hiiii";
}
@ExceptionHandler(Exception.class)
public HashMap<String, Object> doException(Exception e) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", -300);
result.put("msg", "Exception:" + e.getMessage());
result.put("data", null);
return result;
}
执行流程:
- 方法返回的是String类型
- 统一数据返回之前的处理,会将String转成HashMap
- 将HashMap转换成 application / json 字符串给前端(接口)
报错发生在第三步。
判断原body的类型:
- 如果是string,使用StringHttpMessageConverter进行类型转换 – 出错
- 不是string,使用HttpMessageConverter进行类型转换
解决方案:
- 将 StringHttpMessageConverter 通过项目的配置文件,从项目当中去除掉
- 在统一数据重写时,单独处理String类型,让其返回一个String 字符串,而非HashMap。【常用】
解决方案2的两种写法
一、 在最开始先判断是否为string类型,手动写成json格式
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//先判断是不是 string 类型
if (body instanceof String) {
// 返回一个 String 字符串
return "{\"code\":200,\"msg\": \"\", \"data\":\"" + body + "\"}";
}
// 先定义:标准返回格式为 HashMap<String, Object> -> code,msg,data
if (body instanceof HashMap) {
return body;
}
//重写返回结果,让其返回一个统一的数据格式
HashMap<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("msg", "");
result.put("data", body);
return result;
}
二、使用ObjectMapper ,objectMapper.writeValueAsString(result),将对象转化成string
@Autowired
private ObjectMapper objectMapper;
@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 先定义:标准返回格式为 HashMap<String, Object> -> code,msg,data
if (body instanceof HashMap) {
return body;
}
HashMap<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("msg", "");
result.put("data", body);
if (body instanceof String) {
return objectMapper.writeValueAsString(result); //将对象转化成string
}
return result;
}
解决方案1:移除StringHttpMessageConverter
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(converter -> converter instanceof StringHttpMessageConverter);
}
}