使用过滤器Filter实现请求拦截

news2024/12/28 20:30:34

早期使用servlet进行网络开发时,没有拦截器这些内容,那时做请求拦截都是使用Filter过滤器实现的,配置Filter要对哪些请求路径处理,有权限或不需要拦截的路径放行,没有权限的路径直接拦截请求。

一、Filter直接进行拦截

下面就是模拟使用Filter做权限控制的场景,由于现在都是使用spring进行开发,这里定义Filter就使用注解方式实现注入,代码如下:

import org.example.pojo.ApiResult;
import org.example.pojo.StatusCode;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 过滤器处理
 * @Author xingo
 * @Date 2023/12/12
 */
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Component
@WebFilter(filterName = "myFilter", urlPatterns = "/*")
public class MyFilter implements Filter {

    private ServletContext context;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        context = filterConfig.getServletContext();
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        String uri = request.getRequestURI();
        // 静态资源直接放行
        if(uri.endsWith(".js") || uri.endsWith(".css") || uri.endsWith(".png") || uri.endsWith(".jpg")) {
            chain.doFilter(servletRequest, servletResponse);
            return;
        }
        // 登录请求或注册请求放行
        if(uri.startsWith("/login") || uri.startsWith("/register")) {
            chain.doFilter(servletRequest, servletResponse);
            return;
        }
        // 其他请求判断是否有权限拦截
        String userName = this.getLoginUserName(request);
        if(userName == null) {
            String sReqType = request.getHeader("X-Requested-With");
            String contentType = request.getHeader("Content-Type");
            if ("XMLHttpRequest".equalsIgnoreCase(sReqType) || (contentType != null && contentType.toLowerCase().contains("application/json"))) {
                servletResponse.setContentType("application/json; charset=utf-8");
                servletResponse.getWriter().print(JacksonUtils.toJSONString(ApiResult.fail(StatusCode.C_10001)));
            } else {
                servletResponse.setContentType("text/html;charset=utf-8");
                servletResponse.getWriter().print("用户未登录");
            }
        } else {
            // 执行调用链方法
            chain.doFilter(servletRequest, servletResponse);
        }
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }

    /**
     * 获取登录用户信息
     * @param request   http请求体
     * @return
     */
    private String getLoginUserName(HttpServletRequest request) {
        try {
            String token = "";
            if (request.getCookies() != null) {
                for (Cookie c : request.getCookies()) {
                    if (null != c && "TOKEN".equals(c.getName())) {
                        token = c.getValue();
                        break;
                    }
                }
            }
            // 简单通过cookie中的token做权限判断,如果有token且符合要求就返回用户名,否则表示用户身份认证失败
            if(token != null && token.length() > 10) {
                String userName = token.substring(10);
                return userName.equals("admin") ? userName : null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

测试接口还是使用之前的那个controller:

import org.example.handler.AuthLogin;
import org.example.handler.BusinessException;
import org.example.handler.ManagerAuthInteceptor;
import org.example.pojo.ApiResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author xingo
 * @Date 2023/12/7
 */
@RestController
public class DemoController {

    @GetMapping("/demo1")
    public Object demo1() {
        int i = 1, j = 0;

        return i / j;
    }

    @GetMapping("/demo2")
    public Object demo2() {
        if(System.currentTimeMillis() > 1) {
            throw BusinessException.fail(88888, "业务数据不合法");
        }

        return System.currentTimeMillis();
    }

    @GetMapping("/demo3")
    public Map<String, Object> demo3() {
        Map<String, Object> map = new HashMap<>();
        map.put("key1", "Hello,world!");
        map.put("key2", new Date());

        return map;
    }

    @GetMapping("/demo4")
    public List<Object> demo4() {
        List<Object> list = new ArrayList<>();
        list.add(new Date());
        list.add("Hello,world!");
        list.add(Long.MAX_VALUE);
        list.add(Integer.MAX_VALUE);

        return list;
    }

    @GetMapping("/demo5")
    public Map<String, Object> demo5() throws InterruptedException {
        Map<String, Object> map = new HashMap<>();
        map.put("method", "demo5");
        map.put("start", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));
        TimeUnit.SECONDS.sleep(1);
        map.put("end", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));

        return map;
    }

    @GetMapping("/demo6")
    public ApiResult demo6() throws InterruptedException {
        Map<String, Object> map = new HashMap<>();
        map.put("method", "demo6");
        map.put("start", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));
        TimeUnit.SECONDS.sleep(1);
        map.put("end", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));

        return ApiResult.success(map);
    }

    @AuthLogin
    @GetMapping("/demo7")
    public Map<String, Object> demo7() {
        Map<String, Object> map = new HashMap<>();
        map.put("method", "demo7");
        map.put("userName", ManagerAuthInteceptor.LocalUserName.get());
        map.put("time", new Date());

        return map;
    }
}

使用/demo6接口进行测试,当有权限和无权限时的请求返回内容如下:
成功请求
错误的token
无token请求

二、Filter配合注解实现拦截

仿造前面的拦截器实现,在过滤器中配合注解实现拦截,这种方式更加灵活,不用在代码中编写拦截和放行的请求路径,它的实现原理与拦截器类似:

  1. 定义注解
import java.lang.annotation.*;

/**
 * 登录检查注解注解
 * @author xingo
 * @date 2023/12/12
 *
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface AuthLogin {
	
	/**
	 * 检查是否已经登录
	 * @return
	 */
	boolean check() default false;

}
  1. 使用过滤器对请求进行拦截处理
import org.example.pojo.ApiResult;
import org.example.pojo.StatusCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 过滤器处理
 * @Author xingo
 * @Date 2023/12/12
 */
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Component
@WebFilter(filterName = "authFilter", urlPatterns = "/*")
public class AuthFilter extends OncePerRequestFilter {

//        //获取WebApplicationContext,用于获取Bean
//        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
//        //获取spring容器中的RequestMappingHandlerMapping
//        RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) webApplicationContext.getBean("requestMappingHandlerMapping");

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        // 在 chain.doFilter(request, response) 执行前调用返回null
        Object handler1 = request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
        System.out.println(handler1);

        // 获取处理请求
        HandlerMethod handler = getHandlerMethod(request);
        if(handler != null) {
            Method method = handler.getMethod();
            Class<?> clazz = handler.getBeanType();

            // 判断添加了注解的方法或类才进行拦截
            boolean annotationType = clazz.isAnnotationPresent(AuthLogin.class);
            boolean annotationMethod = method.isAnnotationPresent(AuthLogin.class);
            if(annotationType || annotationMethod) {
                // 拦截到的请求需要判断用户名是否为空,如果为空表示用户身份验证失败,表示用户无访问权限
                String userName = this.getLoginUserName(request);
                if(userName == null) {
                    String sReqType = request.getHeader("X-Requested-With");
                    String contentType = request.getHeader("Content-Type");
                    if ("XMLHttpRequest".equalsIgnoreCase(sReqType) || (contentType != null && contentType.toLowerCase().contains("application/json"))) {
                        response.setContentType("application/json; charset=utf-8");
                        response.getWriter().print(JacksonUtils.toJSONString(ApiResult.fail(StatusCode.C_10001)));
                    } else {
                        response.setContentType("text/html;charset=utf-8");
                        response.getWriter().print("用户未登录");
                    }
                    return;
                } else {
                    // 执行调用链方法
                    chain.doFilter(request, response);
                }
            } else {
                // 没有添加注解的请求直接放行
                chain.doFilter(request, response);
            }
        }

        // 在 chain.doFilter(request, response) 执行后调用可以返回值
        Object handler4 = request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
        System.out.println(handler4);
    }

    /**
     * 获取登录用户信息
     * @param request   http请求体
     * @return
     */
    private String getLoginUserName(HttpServletRequest request) {
        try {
            String token = "";
            if (request.getCookies() != null) {
                for (Cookie c : request.getCookies()) {
                    if (null != c && "TOKEN".equals(c.getName())) {
                        token = c.getValue();
                        break;
                    }
                }
            }
            // 简单通过cookie中的token做权限判断,如果有token且符合要求就返回用户名,否则表示用户身份认证失败
            if(token != null && token.length() > 10) {
                String userName = token.substring(10);
                return userName.equals("admin") ? userName : null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 缓存handler
     */
    public static final ConcurrentHashMap<String, HandlerMethod> handlerMap = new ConcurrentHashMap<>();
    /**
     * 获取请求对应的handler方法
     * @param request
     * @return
     */
    private HandlerMethod getHandlerMethod(HttpServletRequest request) {
        String uri = request.getRequestURI();
        String method = request.getMethod();

        String key = (method + ":" + uri).intern();
        HandlerMethod handler = handlerMap.get(key);
        if(handler == null) {
            //获取应用中所有的请求
            Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
            Set<Map.Entry<RequestMappingInfo, HandlerMethod>> entries = handlerMethods.entrySet();

            for(Map.Entry<RequestMappingInfo, HandlerMethod> entry : entries) {
                RequestMethodsRequestCondition methodsCondition = entry.getKey().getMethodsCondition();
                boolean checkMethod = false;
                if(methodsCondition.getMethods().isEmpty()) {
                    checkMethod = true;
                } else {
                    for(RequestMethod reqMethod : methodsCondition.getMethods()) {
                        checkMethod = reqMethod.name().equals(method);
                        if(checkMethod) {
                            break;
                        }
                    }
                }

                PathPatternsRequestCondition pathCondition = entry.getKey().getPathPatternsCondition();
                if(checkMethod && pathCondition.getDirectPaths().contains(uri)) {
                    handler = entry.getValue();
                }
            }
            if(handler != null) {
                handlerMap.put(key, handler);
            }
        }
        return handler;
    }
}

这里也可以使用Filter进行处理,但是我选择继承spring实现的OncePerRequestFilter,在每次请求时过滤器只会执行一次,这里面最主要的处理逻辑是获取到HandlerMethod,获取到这个对象后剩余的处理逻辑跟拦截器基本一致了,只不过拦截器是通过返回布尔值true或false来进行放行或拦截。而在Filter中是通过是否调用 chain.doFilter(request, response) 这个方法来进行放行和拦截的。
还是用上面的controller进行测试,这次只对添加了注解的方法进行拦截,没有添加注解的请求直接放行。

成功请求错误token
无token
不拦截的请求

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1306077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot之请求的详细解析

1. 请求 在本章节呢&#xff0c;我们主要讲解&#xff0c;如何接收页面传递过来的请求数据。 1.1 Postman 之前我们课程中有提到当前最为主流的开发模式&#xff1a;前后端分离 在这种模式下&#xff0c;前端技术人员基于"接口文档"&#xff0c;开发前端程序&…

I.MX RT1170双核学习(1):双核通信之MU消息单元详解

在I.MX RT1170中&#xff0c;它有CM7和CM4核&#xff0c;而消息单元(MU)模块使SoC内的两个处理器能够通过MU接口传递消息以进行通信和协调。 文章目录 1 MU特性2 功能描述3 MU通信实例3.1 轮训实现多核通信3.1.1 MU_SetFlags和MU_GetFlags3.1.2 MU_SendMsg和MU_ReceiveMsg3.1.…

阿里云国际版无法远程连接Windows服务器的排查方法

如果您遇到紧急情况&#xff0c;需要尽快登录Windows实例&#xff0c;请参见以下操作步骤&#xff0c;先检查ECS实例的状态&#xff0c;然后通过云助手向Windows实例发送命令或通过VNC登录实例&#xff0c;具体步骤如下&#xff1a; 步骤一&#xff1a;检查ECS实例状态 无论何…

CentOS 7 源码部署 Nginx

文章目录 1. 概述2. 部署示例2.1 下载和解压 Nginx 源码2.2 安装编译依赖包2.3 编译和安装2.4 启动 Nginx2.5 配置防火墙2.6 设置 Nginx 为系统服务2.7 配置访问 3. 扩展知识 1. 概述 Nginx 是一款高性能的开源 Web 服务器软件&#xff0c;广泛应用于互联网领域。本篇博客将介…

pytest-fixtured自动化测试详解

fixture的作用 1.同unittest的setup和teardown,作为测试前后的初始化设置。 fixture的使用 1.作为前置条件使用 2.fixture的的作用范围 1.作为前置条件使用 pytest.fixture() def a():return 3def test_b(a):assert a3 2.fixture的作用范围 首先实例化更高范围的fixture…

Ghidra设置主题和字体大小

文章目录 Ghidra调整主题和字体大小设置主题和全局字体大小调整反汇编页面字体大小 Ghidra调整主题和字体大小 ghidra默认字体小,默认主题白色伤眼,摸索找到了设置主题和字体的方法 设置主题和全局字体大小 启动ghidra后选择edit>theme>configure 下拉框选择主题,右上…

maui下sqlite演示增删改查

数据操作类 有分页 todoitemDatabase.cs&#xff1a; using SQLite; using TodoSQLite.Models;namespace TodoSQLite.Data {public class TodoItemDatabase{SQLiteAsyncConnection Database;public TodoItemDatabase(){}// 初始化数据库连接和表async Task Init(){if (Databa…

科技提升安全,基于YOLOv7【tiny/yolov7/yolov7x】开发构建商超扶梯场景下行人安全行为姿态检测识别系统

在商超等人流量较为密集的场景下经常会报道出现一些行人在扶梯上摔倒、受伤等问题&#xff0c;随着AI技术的快速发展与不断普及&#xff0c;越来越多的商超、地铁等场景开始加装专用的安全检测预警系统&#xff0c;核心工作原理即使AI模型与摄像头图像视频流的实时计算&#xf…

计算机视觉项目实战-驾驶员疲劳检测

&#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; 本次博客内容将继续讲解关于OpenCV的相关知识 &#x1f389;作者简介&#xff1a;⭐️⭐️⭐️目前计算机研究生在读。主要研究方向是人工智能和群智能算法方向。目前熟悉深度学…

nrm 的使用 可以快速切换下载(npm)镜像,解决资源下载慢和运行失败

nrm是什么&#xff1f; 介绍 nrm(npm registry manager) 是 npm 的镜像源管理工具. 有时候国外资源太慢,使用 nrm 可以快速的在 npm 源之间切换 安装 npm install -g nrm 基本使用 查看可选择的源 nrm ls 切换到对应的镜像源 nrm use 对应的镜像 删除镜像源 nrm del 名字 …

翻译: LLM大语言模型图像生成原理Image generation

文本生成是许多用户正在使用的&#xff0c;也是所有生成式人工智能工具中影响最大的。但生成式人工智能的一部分兴奋点也在于图像生成。目前也开始出现一些可以生成文本或图像的模型&#xff0c;这些有时被称为多模态模型&#xff0c;因为它们可以在多种模式中操作&#xff0c;…

【Matlab】如何将二阶线性微分方程进行Laplace变换得到传递函数

二阶线性微分方程进行Laplace变换 前言正文代码实现 前言 二阶线性微分方程: 一个二阶线性微分方程通常可以写成如下形式: y ′ ′ ( t ) p ( t ) y ′ ( t ) q ( t ) y ( t ) f ( t ) y^{\prime \prime}(t)p(t) y^{\prime}(t)q(t) y(t)f(t) y′′(t)p(t)y′(t)q(t)y(t)f(…

基于Python+WaveNet+MFCC+Tensorflow智能方言分类—深度学习算法应用(含全部工程源码)(三)

目录 前言引言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据预处理2. 模型构建1&#xff09;定义模型结构2&#xff09;优化损失函数 3. 模型训练及保存1&#xff09;模型训练2&#xff09;模型保存3&#xff09;映射保存 相关其它博客工程源代码下载其它资料下载…

Redis系列之简单实现watchDog自动续期机制

在分布锁的实际使用中&#xff0c;可能会遇到一种情况&#xff0c;一个业务执行时间很长&#xff0c;已经超过redis加锁的时间&#xff0c;也就是锁已经释放了&#xff0c;但是业务还没执行完成&#xff0c;这时候其它线程还是可以获取锁&#xff0c;那就没保证线程安全 项目环…

文件操作及函数

什么是文件&#xff1f; 在程序设计中&#xff0c;文件有两种&#xff1a;程序文件和数据文件。 程序文件 包括源程序文件&#xff08;.c&#xff09;&#xff0c;目标文件&#xff08;.obj&#xff09;&#xff0c;可执行程序(.exe)。 数据文件 文件的内容不一定是程序&…

mybatis的快速入门以及spring boot整合mybatis(二)

需要用到的SQL脚本&#xff1a; CREATE TABLE dept (id int unsigned PRIMARY KEY AUTO_INCREMENT COMMENT ID, 主键,name varchar(10) NOT NULL UNIQUE COMMENT 部门名称,create_time datetime DEFAULT NULL COMMENT 创建时间,update_time datetime DEFAULT NULL COMMENT 修改…

P1044 [NOIP2003 普及组] 栈——卡特兰数

传送门&#xff1a; P1044 [NOIP2003 普及组] 栈 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1044 公式一&#xff1a;递推式(注意开 long long &#xff0c;然后 先乘完再除&#xff0c;防止下取整&#xff09; typedef long long ll;…

超过 1450 个 pfSense 服务器因错误链而遭受 RCE 攻击

在线暴露的大约 1450 个 pfSense 实例容易受到命令注入和跨站点脚本漏洞的攻击&#xff0c;这些漏洞如果链接起来&#xff0c;可能使攻击者能够在设备上执行远程代码。 pfSense 是一款流行的开源防火墙和路由器软件&#xff0c;允许广泛的定制和部署灵活性。 它是一种经济高效…

SpringCloud-高级篇(七)

前面在微服务里整合了Seata&#xff0c;下面利用Seata去解决分布式事务的问题&#xff0c;回去学习Seata中的四种解决方案 &#xff1a;首先学习XA模式 &#xff08;1&#xff09;XA模式 RM在前面讲的是资源管理器&#xff0c;在XA标准中RM都是由数据库来实现的&#xff0c;数…

【二分查找】【双指针】LeetCode:2565最少得分子序列

作者推荐 【动态规划】【广度优先】LeetCode2258:逃离火灾 本文涉及的基础知识点 二分查找算法合集 有序向量的二分查找&#xff0c;初始化完成后&#xff0c;向量不会修改。 双指针&#xff1a; 用于计算子字符串是s的字符串的子系列。 题目 给你两个字符串 s 和 t 。 你…