Unified interception technology (filter) for login authentication function

Table of contents

1 Introduction

2. Filter

(1) Description

(2) Steps to use

(3) Explanation example

(4) Specific examples

(5) Filter details

3. Filter implementation of login verification

(1) Implementation process

(2) Concrete implementation


1 Introduction

When the front end initiates a request, it will carry the JWT token in the request header to the server every time, and the server needs to intercept all requests uniformly, so as to judge whether it carries a legal JWT token. So how to uniformly intercept all requests to verify the validity of the token? Here we will learn two solutions:

1. Filter filter
2. Interceptor interceptor

2. Filter

(1) Description

Filter represents a filter, which is one of the three major components of JavaWeb (Servlet, Filter, Listener).
Filters can intercept requests for resources to achieve some special functions. After
  using filters, if you want to access resources on the web server, you must first pass through the filters. After the filters are processed, you can access the corresponding resources.
Filters generally complete some common operations, such as: login verification, Unicode processing, sensitive character processing, etc.

(2) Steps to use

Step 1, define the filter: define a class, implement the Filter interface, and rewrite all its methods.

Step 2, configure the filter: Add the @WebFilter annotation to the Filter class to configure the path to intercept resources. Add @ServletComponentScan to the boot class to enable Servlet component support.

(3) Explanation example

define filter

@WebFilter(urlPatterns = "/*") //配置过滤器要拦截的请求路径( /* 表示拦截浏览器的所有请求 )
public class DemoFilter implements Filter {
    @Override //初始化方法, 只调用一次
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init 初始化方法执行了");
    }

    @Override //拦截到请求之后调用, 调用多次
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("Demo 拦截到了请求...放行前逻辑");
        //放行
        chain.doFilter(request,response);
    }

    @Override //销毁方法, 只调用一次
    public void destroy() {
        System.out.println("destroy 销毁方法执行了");
    }
}

init method: the initialization method of the filter. The Filter object will be created automatically when the web server is started, and the init initialization method will be called automatically when the filter object is created, and this method will only be called once.
doFilter method: This method will be called after each intercepted request, so this method will be called multiple times, and the doFilter() method will be called once every time a request is intercepted.
Destroy method: is the method of destruction. When we shut down the server, it will automatically call the destroy method destroy, and this destroy method will only be called once.

After the Filter is defined, the Filter will not actually take effect, and the configuration of the Filter needs to be completed. The configuration of the Filter is very simple. You only need to add an annotation to the Filter class: @WebFilter, and specify the attribute urlPatterns, and specify the filter through this attribute Which requests to intercept.

Add an annotation @ServletComponentScan on the startup class, and use this @ServletComponentScan annotation to enable SpringBoot project support for Servlet components.

@ServletComponentScan
@SpringBootApplication
public class TliasWebManagementApplication {

    public static void main(String[] args) {
        SpringApplication.run(TliasWebManagementApplication.class, args);
    }

}

(4) Specific examples

filter:

package com.example.demo.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @Author linaibo
 * @Date 2023/4/8 21:36
 * @PackageName:com.example.demo.filter
 * @ClassName: DemoFilter
 * @Version 1.0
 */
@WebFilter("/*") // 配置过滤器要拦截的请求路径( /* 表示拦截浏览器的所有请求 )
public class DemoFilter implements Filter {
    @Override //初始化方法, 只调用一次
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        System.out.println("初始化方法执行");
    }

    @Override //拦截到请求之后调用, 调用多次
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 放行前逻辑
        System.out.println("拦截到了请求,放行前的逻辑");
        // 放行到后面进行处理,如果没有拦截器,直接进入controller,如果有,则进入拦截器
        filterChain.doFilter(servletRequest, servletResponse);
        // 放行后逻辑
        System.out.println("拦截到了请求,放行后的逻辑");
    }

    @Override //销毁方法, 只调用一次
    public void destroy() {
        System.out.println("销毁方法执行了");
        Filter.super.destroy();
    }
}

Startup class:

package com.example.demo;

import com.example.demo.domain.Student;
import com.example.demo.service.IBmsBillMemoService;
import com.example.demo.service.impl.BmsBillMemoServiceImpl;
import org.apache.commons.beanutils.PropertyUtils;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;

@SpringBootApplication
//开启定时任务功能
@EnableScheduling
// @EnableConfigurationProperties(Student.class)
//@MapperScan("com.example.demo.mapper")
@ServletComponentScan
public class DemoApplication {

//    @Bean
//    public RestTemplate getRestTemplate(){
//        return new RestTemplate();
//    }
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);
        Object bean = ctx.getBean("bmsBillMemoServiceImpl");
        System.out.println(bean);
        IBmsBillMemoService bean1 = ctx.getBean(IBmsBillMemoService.class);
        System.out.println(bean1);
        BmsBillMemoServiceImpl bean2 = ctx.getBean("bmsBillMemoServiceImpl", BmsBillMemoServiceImpl.class);
        System.out.println(bean2);
        Object get = ctx.getBean("get");
        System.out.println(get);
        Map<String, Object> get1 = PropertyUtils.describe("get");
        Student bean3 = ctx.getBean(Student.class);
        System.out.println(bean3.toString());
    }

}

Results of the:

After executing the initialization method, first execute the logic before release, then enter the interceptor, or enter the controller, after the processing is completed, execute the logic after release, and then return to the front end.

(5) Filter details

1. Filter execution process

After we intercept the request in the filter, if we want to continue to access the following web resources, we must perform the release operation. The release operation is to call the doFilter() method in the FilterChain object. The code written before calling the doFilter() method belongs to the release operation. previous logic.

After accessing the web resource after release, it will return to the filter. After returning to the filter, if necessary, the logic after release can be executed. We write the logic after release after the line of code doFilter().

2. Intercept path

3. Filter chain

The so-called filter chain means that in a web application, multiple filters can be configured, and multiple filters form a filter chain.

For example: In our web server, two filters are defined, and these two filters form a filter chain.

And the filters on this chain will be executed one by one when they are executed. The first Filter will be executed first, and then the second Filter will be executed after the release. If the last filter is executed, the corresponding web page will be accessed. resource.

After accessing web resources, according to the execution process of the filter we just introduced, it will return to the filter to execute the logic after the filter is released, and the order is reversed when executing the logic after the release.

First, execute the logic after the release of filter 2, then execute the logic after the release of filter 1, and finally respond to the browser with data.

The order of filter execution is related to the class name. The execution priority of the filter configured in annotation mode is determined by the automatic sorting of the filter class name. The higher the class name, the higher the priority.

3. Filter implementation of login verification

(1) Implementation process

The basic process of login verification:

  • To enter the background management system, we must first complete the login operation, and then we need to access the login interface login.
  • After successful login, we will generate a JWT token on the server side, and return the JWT token to the front end, and the front end will store the JWT token.
  • In each subsequent request, the JWT token will be carried to the server. After the request reaches the server, if we want to access the corresponding business function, we must first verify the validity of the token.
  • For the verification token operation, we use the login verification filter to verify the validity of the token in the filter. If the token is invalid, it will respond with an error message, and will not allow access to the corresponding resource. If the token exists and it is valid, it will be released to access the corresponding web resources and perform corresponding business operations.

We probably know the implementation steps of the Filter filter. Before we formally develop the login verification filter, we think about two questions:

1. After all requests are intercepted, do they need to verify the token?
   Answer: **Login request exception**
2. After intercepting the request, under what circumstances can it be released and perform business operations?
  Answer: **There is a token, and the token verification is passed (legal); otherwise, an error result of not being logged in will be returned

Based on the above business process, we analyze the specific operation steps:

1. Obtain the request url
2. Determine whether the request url contains login, if it does, it means a login operation, let it go
3. Obtain the token in the request header
4. Determine whether the token exists, if not, return an error Result (not logged in)
5. Parse the token, if the parsing fails, return an error result (not logged in)
6. Release

(2) Concrete implementation

@Slf4j
@WebFilter(urlPatterns = "/*") //拦截所有请求
public class LoginCheckFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        //前置:强制转换为http协议的请求对象、响应对象 (转换原因:要使用子类中特有方法)
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        //1.获取请求url
        String url = request.getRequestURL().toString();
        log.info("请求路径:{}", url); //请求路径:http://localhost:8080/login


        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
        if(url.contains("/login")){
            chain.doFilter(request, response);//放行请求
            return;//结束当前方法的执行
        }


        //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);
            response.setContentType("application/json;charset=utf-8");
            //响应
            response.getWriter().write(json);

            return;
        }

        //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;
        }


        //6.放行
        chain.doFilter(request, response);

    }
}

Guess you like

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