SpringBoot统一功能处理(AOP思想实现)(统一用户登录权限验证 / 异常处理 / 数据格式返回)

主要是三个处理:

        1、统一用户登录权限验证;

        2、统一异常处理;

        3、统一数据格式返回。

目录

一、用户登录权限校验

🍅  1、使用拦截器

        🎈 1.1自定义拦截器

        🎈 1.2 设置自定义拦截器

        🎈创建controller类,并且运行项目

 🍅 2、拦截器原理

二、统一异常处理

三、统一数据返回

 🍅 为什么需要统一数据返回格式

🍅 统一数据返回格式

       🎈定义同已返回类型

       🎈 同以数据处理

        🎈业务类


一、用户登录权限校验

🍅  1、使用拦截器

可以对一部分方法进行拦截,而另一部分不拦截。

        🎈 1.1自定义拦截器

/*
* 全局变量
* */
public class AppVar {
//    Session key
    public static final String SESSION_KEY = "SESSION KEY";
}

/*
* 自定义拦截器
* 返回true -> 表示拦截器验证成功,继续指定后续方法
* 返回false -> 表示验证失败,不会执行后续的方法了
* */
@Component
public class UserInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
//        业务方法
        HttpSession session = request.getSession(false);
        if (session!=null &&
                session.getAttribute(AppVar.SESSION_KEY)!=null){
//            用户已经登录
            return true;
        }
        return false;
    }
}

        🎈 1.2 设置自定义拦截器

将自定义拦截器设置当前项目的配置文件中,并且设置拦截规则。

拦截器要注入到Spring中才能运行,他是伴随着Spring的启动而启动的

@Configuration
public class AppConfig implements WebMvcConfigurer {
    @Autowired
    private UserInterceptor userInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//        registry.addInterceptor(new UserInterceptor());
        registry.addInterceptor(userInterceptor)
                .addPathPatterns("/**")    //("/**")表示拦截所有的请求
                .excludePathPatterns("/user/reg")//表示过滤拦截,不拦截(/user/reg)
                .excludePathPatterns("/user/login");//表示过滤拦截,不拦截("/user/login")
    }
}

        🎈创建controller类,并且运行项目


@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getuser")
    public String getUser(){
        System.out.println("do getUser()");
        return "user";
    }

    @RequestMapping("/reg")
    public String reg(){
        System.out.println("do reg()");
        return "reg";
    }

    @RequestMapping("/login")
    public String login(){
        System.out.println("do login()");
        return "login";
    }
}

其中:

  • addPathPatterns:表示要拦截的url,“/**”表示拦截任意方法
  • elcludePathPatterns:表示需要排除的URL
  • 以上得拦截规则可以拦截URL,包括静态文件(图片文件、JS、CSS等),一般拦截静态文件的时候,我们可以把这些静态文件分类放在static文件中

 🍅 2、拦截器原理

在使用拦截器之前

 

 

使用拦截器之后:会在调用Controller之前进行相应的业务处理

实现原理源码分析:

          所有的controller指定都会通过一个调度器DispatcherServlet来实现,

 

所有的请求都会执行DispatcherServlet中的doDispatcher方法,在doDispatcher会执行一系列的事件,该事件是在执行拦截器之前的,如果该事件返回false,后续就不会执行Controller。

以下是doDispatcher中的一部分代码,发现在执行controller之前都会追先执行预处理

// 调⽤预处理【重点】

 if (!mappedHandler.applyPreHandle(processedRequest, respon
se)) {
 return;
 }
 // 执⾏ Controller 中的业务

 mv = ha.handle(processedRequest, response, mappedHandler.g
etHandler());
 if (asyncManager.isConcurrentHandlingStarted()) {
 return;
}

那么,关于预处理⽅法 applyPreHandle方法:从上面的源码可以看出,着和我们之前定义的拦截器相似,着就是拦截器的实现原理

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex

                = i++) {
            // 获取项⽬中使⽤的拦截器 HandlerInterceptor

            HandlerInterceptor interceptor = (HandlerInterceptor)this.intercep
            torList.get(i);
            if (!interceptor.preHandle(request, response, this.handler)) {
                this.triggerAfterCompletion(request, response, (Exception)null

                );
                return false;
            }
        }
        return true;
    }

二、统一异常处理

统一异常处理:就是指常规的异常,统一处理。

统一异常处理使用的是@ControllerAdvice + @ExceptionHandler来执行的,@ControllerAdvice表示控制器通知类,@ExceptionHandler是异常处理,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件。

首先认为构造一个空指针异常:

 @RequestMapping("/reg")
    public String reg(){
        System.out.println("do reg()");
        Object obj = null;
        System.out.println(obj.hashCode());
        System.out.println();
        System.out.println("do reg()");
        return "reg";
    }

报错了: 这种直接给你报错的方式并不直观,所以我们可以进行统一的异常处理,返回直观的数据。

 然后我们进行统一异常处理:

        首先定义一个统一的返回对象:

@Data
public class ResultAjax {
    private int code;   //状态码
    private String msg; //状态码的描述信息
    private Object data;//返回数据
}

        然后定义异常管理器:

@RestControllerAdvice
public class ExceptionAdvice {

    @ExceptionHandler(NullPointerException.class)
    public ResultAjax doNullPointerException(NullPointerException e){
        ResultAjax resultAjax = new ResultAjax();
        //错误的信息使用-1描述状态码
        resultAjax.setCode(-1);
        resultAjax.setMsg("空指针异常:"+ e.getMessage());
        resultAjax.setData(null);
        return resultAjax;
    }
}

这时候就会返回状态的描述信息:


 也可以直接使用NullPointerException的父类

@ExceptionHandler(Exception.class)
    public ResultAjax doException(Exception e){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(-1);
        resultAjax.setMsg("空指针异常:"+ e.getMessage());
        resultAjax.setData(null);
        return resultAjax;
    }

三、统一数据返回

 🍅 为什么需要统一数据返回格式

统一数据返回格式的优点(为什么要统一):

  • 方便程序员更好的接收和解析后端数据接口返回的数据
  • 降低前端程序源和后端程序员的沟通成本,按照找某个格式实现,所有接口都这样返回
  • 有利于项目统一数据的维护和修改
  • 有利于后端技术部分的统一规范的标准制定,不会出现稀奇古怪的返回内容

🍅 统一数据返回格式

       🎈定义同已返回类型

@Data
public class ResultAjax {
    private int code;   //状态码
    private String msg; //状态码的描述信息
    private Object data;//返回数据

    /*
    * 返回成功
    * */
    public static ResultAjax success(Object data){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(200);
        resultAjax.setMsg("");
        resultAjax.setData(data);
        return resultAjax;
    }


    /*
    * 返回失败
    * */
    public static ResultAjax fail(int code,String msg){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(null);
        return resultAjax;
    }

    public static ResultAjax fail(int code,String msg,Object data){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(null);
        return resultAjax;
    }
}

              🎈 同以数据处理

统一数据处理(强制执行):

  1. @ControllerAdvice
  2. 实现ResponseBodyAdvice接口,并且重写它其中的两个方法,supports必须返回true,beforeBodyWrite方法进行重新判断和重写操作
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    /*
    * 默认翻会true的时候
    * 才会执行beforeBodyWrite方法
    * */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        //已经包装好的对象
        if (body instanceof ResultAjax){
            return body;
        }
//        没有包装
        return ResultAjax.success(body);

    }
}

        🎈业务类

@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
    public ResultAjax login(){
        System.out.println("do login()");
        return ResultAjax.success("login");
    }
    
    @RequestMapping("/getnum")
    public int getNum(){
        return 1;
    }
}

其中login没有定义返回类型,getNum定义了返回类型,返回结果分别如下:

 

注意:

        如果定义的返回值类型是String,那么会报错

  @RequestMapping("/getstring")
    public String getString(){
        return "qqq";
    }

那么可以对String类型作出单独处理:

@Autowired
    private ObjectMapper objectMapper;

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        //已经包装好的对象
        if (body instanceof ResultAjax){
            return body;
        }
//        对字符串进行单独处理
        if (body instanceof String){
            ResultAjax resultAjax = ResultAjax.success(body);
            try {
                return objectMapper.writeValueAsString(resultAjax);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
//        没有包装
        return ResultAjax.success(body);
    }

返回结果:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot AOP可以用来实现基于注解的权限控制。通过在需要进行权限控制的方法上添加注解,然后使用AOP来拦截这些方法,从而实现对访问权限的控制。 以下是实现基于注解的权限控制的步骤: 1. 定义一个注解,用于标注需要进行权限控制的方法。例如: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresPermission { String value() default ""; } ``` 2. 在需要进行权限控制的方法上添加注解。例如: ```java @RequiresPermission("admin") public void deleteItem(int id) { // ... } ``` 3. 编写一个AOP切面,用于拦截带有注解的方法,并进行权限验证。例如: ```java @Aspect @Component public class PermissionAspect { @Autowired private HttpServletRequest request; @Around("@annotation(com.example.demo.annotation.RequiresPermission)") public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable { String permission = joinPoint.getTarget().getClass().getAnnotation(RequiresPermission.class).value(); if (request.isUserInRole(permission)) { return joinPoint.proceed(); } else { throw new AccessDeniedException("Access Denied"); } } } ``` 4. 在应用程序的配置类中启用AOP自动代理。例如: ```java @EnableAspectJAutoProxy @SpringBootApplication public class Application { // ... } ``` 这样,就可以通过注解来实现基于权限的访问控制了。在需要进行权限控制的方法上添加注解,AOP切面会自动拦截这些方法,并进行权限验证。如果用户没有相应的权限,则会抛出AccessDeniedException异常。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值