目录
1、统一用户登录权限验证
为什么要进行用户登录权限验证呢?咱们想想如果不适用的话,例如博客系统,除了登录和注册页面不需要用户登录权限验证以外,其它的都需要,那就会导致有很多冗余代码~
有伙伴想到了,可以使用AOP来解决,想法很不错,可现实是残酷的,例如,我们使用Spring AOP前置通知和环绕通知来实现,但我们会面临两个很难搞的问题:
- 在切面类中,很难获取到HttpSession对象
- 我们要对一部分方法进行拦截,另一部分不拦截,所以定义拦截规则很难
解决问题:使用Spring拦截器:HandlerInterceptor
1.1、Spring拦截器
对于上述的问题,Spring提供了具体的实现拦截器:HandlerInterceptor,拦截器分为以下两个步骤:
- 创建自定义拦截器,实现HandlerInterceptor接口的preHandle(执行具体方法之前的预处理)方法
- 将自定义拦截器加入WebMvcConfigurer的addInterceptors方法中
1.1.1、自定义拦截器
实现一个用户登录权限校验,自定义拦截器是一个普通的类,具体实现如下:
- 实现HandlerInterceptor接口
- 重写preHeadler方法,在方法中编写自己的业务代码
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//用户登录业务判断
HttpSession session = request.getSession(false);
if(session !=null && session.getAttribute("userinfo") != null) {
//用户已登录
return true;
}
//用户未登录
//可以调整到登录页面,返回一个401/403没有权限码
response.sendRedirect("login.html");
// response.setStatus(401);
return false;
}
}
1.1.2、自定义拦截
将自定义拦截器加入WebMvcConfigurer的addInterceptors方法中,也就是将上一步中的自定义拦截器加入到系统配置信息中,并且设置拦截规则,具体实现如下:
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //拦截所有请求
.excludePathPatterns("/user/login") //不拦截的url地址
.excludePathPatterns("/**/*.html"); //不拦截html页面
}
}
一个登录权限验证的拦截器就实现啦~
1.2、 拦截器实现原理
有了拦截器之后,会在调用Controller之前进行相应的业务处理,流程如下:
1.3、扩展统一访问前缀添加
1.3.1、在系统的配置文件中设置:
在上述的自定义拦截的方法下面加一个重写的方法:
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/user",c->true);
}
第二个参数是一个表达式,设置为true表示启动前缀
1.3.2、在配置文件中配置
如:我在yml配置文件中配置:
server:
servlet:
context-path: /user
2、统一数据格式返回
2.1、统一数据返回格式的实现
两个步骤:
- 创建一个类,并添加注解@ControllerAdvice
- 实现ResponseBodyAdvice接口,并重写supports和beforeBodyWrite(统一对象就是此方法中实现)
代码实现:
package com.example.demo.config;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.HashMap;
/**
* Created with IntelliJ IDEA.
* Description:
* User:龙宝
* Date:2023-03-29
* Time:15:18
*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;//true为开启,执行beforeBodyWrite方法,否则不执行
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
HashMap<String,Object> ret = new HashMap<>();
ret.put("code",200);//状态码
ret.put("msg","");//报错信息,程序都执行到返回数据了,自然是没有报错~
ret.put("data",body);
return ret;
}
}
写一个测试:
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created with IntelliJ IDEA.
* Description:
* User:龙宝
* Date:2023-03-29
* Time:15:23
*/
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/int")
public Integer retInt() {
return 2;
}
}
结果:
但此时如果返回的是String类型,就会报错:
@RequestMapping("/string")
public String retInt() {
return "222";
}
为什么呢?String是一个很特殊的类型,既不是基本类型,也不属于对象,并且在重写方法时,除String以外都是使用同一个格式化工具,而String用的是自己的一套格式化工具,因此在转换成HashMap时,还没有被加载好,而其他的转换器都已经加载好了,就引出上面的异常了。
因此当返回类型为String类型时,就要进行特殊处理,使用JSON的writeValueAsString方法将java对象转换成JSON格式再返回~
代码修改如下:
2.2、统一数据返回格式的好处
- 方便前端程序员更好地接收和解析后端数据接口返回的数据
- 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就行了,因为所有接口都是这样返回的
- 有利于项目统一数据的维护和修改
- 有利于后端技术部门的统一规范的标准制定,不会出现稀奇古怪的返回内容~
3、统一异常处理
两个步骤:
- 创建一个类,并在类上标识@ControllerAdvice
- 添加方法@ExceptionHandle来订阅异常
代码实现【举例】:
package com.example.demo.config;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
/**
* Created with IntelliJ IDEA.
* Description:
* User:龙宝
* Date:2023-03-29
* Time:15:44
*/
@ControllerAdvice
@ResponseBody
public class MyExHandle {
@ExceptionHandler(NullPointerException.class)
public HashMap<String,Object> nullEx(NullPointerException e) {
HashMap<String,Object> ret = new HashMap<>();
ret.put("code",-1);
ret.put("msg","空指针异常"+e.getMessage());//错误码的描述信息
ret.put("data",null);
return ret;
}
@ExceptionHandler(Exception.class)
public HashMap<String, Object> exception(Exception e) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", "-1");
result.put("msg", "异常:" + e.getMessage()); // 错误码的描述信息
result.put("data", null);
return result;
}
}
好啦~本期到这里就结束了,下期见~