Unified interception technology for login authentication function (interceptor)

Table of contents

1. Description

2. How to use

(1) Define the interceptor

(2) Register configuration interceptor

(3) Example:

3.Interceptor detailed description

(1) interception path

(2) Execution process

(3) The difference between filters and interceptors

4. Implementation of the interceptor for login verification

5. Global exception handling (supplementary explanation)


1. Description

An interceptor is a mechanism for dynamically intercepting method calls, similar to a filter.
Interceptors are provided in the Spring framework to dynamically intercept the execution of controller methods.

Function of the interceptor: intercept the request, and execute the pre-set code according to the business needs before and after the specified method call.

In the interceptor, we usually do some general operations, for example: we can use the interceptor to intercept the request initiated by the front end, and write all the logic of login verification in the interceptor. During the verification process, if it is found that the user has logged in (carrying a JWT token and it is a legal token), it can be directly released to access the resources in the spring. If it is found that there is no login or an illegal token during the verification, it can directly respond to the front end with an error message of not being logged in.

2. How to use

(1) Define the interceptor

Implement the HandlerInterceptor interface and override all its methods

//自定义拦截器
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    //目标资源方法执行前执行。 返回true:放行    返回false:不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle .... ");

        return true; //true表示放行
    }

    //目标资源方法执行后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle ... ");
    }

    //视图渲染完毕后执行,最后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion .... ");
    }
}

Note:
preHandle method: executed before the execution of the target resource method. Return true: pass through Return false: not pass through.
postHandle method: Executed after the target resource method is executed.
afterCompletion method: Executed after the view is rendered, and finally executed.

(2) Register configuration interceptor

Implement the WebMvcConfigurer interface and override the addInterceptors method.

@Configuration  
public class WebConfig implements WebMvcConfigurer {

    //自定义的拦截器对象
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       //注册自定义拦截器对象
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
    }
}

(3) Example:

package com.example.demo.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Author linaibo
 * @Date 2023/4/9 15:29
 * @PackageName:com.example.demo.interceptor
 * @ClassName: DemoInterceptor
 * @Version 1.0
 */
@Component
public class DemoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("方法执行前");
        // 返回true,代表放行。返回false,代表不放行
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("方法执行后");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("视图渲染完毕后执行");
    }
}
package com.example.demo.config;

import com.example.demo.interceptor.DemoInterceptor;
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;

/**
 * @Author linaibo
 * @Date 2023/4/9 15:33
 * @PackageName:com.example.demo.config
 * @ClassName: WebConfig
 * @Version 1.0
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private DemoInterceptor demoInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(demoInterceptor).addPathPatterns("/**");
    }
}

3.Interceptor detailed description

(1) interception path

First of all, let's look at the configuration of the interceptor's interception path. When registering and configuring the interceptor, we need to specify the interceptor's interception path. Through the addPathPatterns("path to intercept") method, we can specify which resources to intercept.

In the entry program, we configure `/**`, which means to intercept all resources. When configuring the interceptor, you can not only specify which resources to intercept, but also which resources to not intercept. You only need to call `excludePathPatterns("not Intercept path")` method, specify which resources do not need to be intercepted.

example:

@Configuration  
public class WebConfig implements WebMvcConfigurer {

    //拦截器对象
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册自定义拦截器对象
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**")//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
                .excludePathPatterns("/login");//设置不拦截的请求路径
    }
}

In addition to setting `/**` to intercept all resources in the interceptor, there are some common interception path settings:

(2) Execution process

When we open the browser to access the web application deployed in the web server, the filter we defined will intercept this request. After intercepting this request, it will first execute the logic before release, and then execute the release operation. And because we are currently developing based on springboot, after the release, we have entered the spring environment, that is, to access the interface methods in the controller we defined.
Tomcat does not recognize the written Controller program, but it recognizes the Servlet program, so a very core Servlet is provided in Spring's Web environment: DispatcherServlet (front controller). All requests will first go to DispatcherServlet, and then transfer the request to to Controller.
When we define the interceptor, the request will be intercepted by the interceptor before executing the Controller method. Execute the `preHandle()` method, which needs to return a Boolean value after the execution is completed. If it returns true, it means that this operation will be released, and then the method in the controller will continue to be accessed; if it returns false, it will not be released ( The method in the controller will not be executed).
After the method in the controller is executed, go back and execute the `postHandle()` method and the `afterCompletion()` method, and then return to the DispatcherServlet, and finally execute the logic of this part of the logic after the filter is released. After the execution is completed, the browser finally responds with data. 

(3) The difference between filters and interceptors

The interface specifications are different: filters need to implement the Filter interface, while interceptors need to implement the HandlerInterceptor interface.
The scope of interception is different: Filter will intercept all resources, while Interceptor will only intercept resources in the Spring environment.

4. Implementation of the interceptor for login verification

Login verification interceptor

//自定义拦截器
@Component //当前拦截器对象由Spring创建和管理
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
    //前置方式
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle .... ");
        //1.获取请求url
        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行

        //3.获取请求头中的令牌(token)
        String token = request.getHeader("token");
        log.info("从请求头中获取的令牌:{}",token);

        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
        if(!StringUtils.hasLength(token)){
            log.info("Token不存在");

            //创建响应结果对象
            Result responseResult = Result.error("NOT_LOGIN");
            //把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
            String json = JSONObject.toJSONString(responseResult);
            //设置响应头(告知浏览器:响应的数据类型为json、响应的数据编码表为utf-8)
            response.setContentType("application/json;charset=utf-8");
            //响应
            response.getWriter().write(json);

            return false;//不放行
        }

        //5.解析token,如果解析失败,返回错误结果(未登录)
        try {
            JwtUtils.parseJWT(token);
        }catch (Exception e){
            log.info("令牌解析失败!");

            //创建响应结果对象
            Result responseResult = Result.error("NOT_LOGIN");
            //把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
            String json = JSONObject.toJSONString(responseResult);
            //设置响应头
            response.setContentType("application/json;charset=utf-8");
            //响应
            response.getWriter().write(json);

            return false;
        }

        //6.放行
        return true;
    }

Register configuration interceptor

@Configuration  
public class WebConfig implements WebMvcConfigurer {
    //拦截器对象
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       //注册自定义拦截器对象
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}

5. Global exception handling (supplementary explanation)

* It is very simple to define a global exception handler. It is to define a class and add an annotation @RestControllerAdvice to the class. Adding this annotation means that we have defined a global exception handler.
* In the global exception handler, a method needs to be defined to catch the exception, and the annotation @ExceptionHandler needs to be added to this method. Use the value attribute in the @ExceptionHandler annotation to specify which type of exception we want to capture.

Example:

@RestControllerAdvice
public class GlobalExceptionHandler {

    //处理异常
    @ExceptionHandler(Exception.class) //指定能够处理的异常类型
    public Result ex(Exception e){
        e.printStackTrace();//打印堆栈中的异常信息

        //捕获到异常之后,响应一个标准的Result
        return Result.error("对不起,操作失败,请联系管理员");
    }
}

@RestControllerAdvice = @ControllerAdvice + @ResponseBody
The return value of the exception handling method will be converted to json and then responded to the front end

The use of global exception handlers mainly involves two annotations:

@RestControllerAdvice //Indicates that the current class is a global exception handler
@ExceptionHandler //Specify which type of exception can be caught for processing

Guess you like

Origin blog.csdn.net/m0_72167535/article/details/130042495