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

早期使用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
不拦截的请求

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JAVA架构师或者teamleader要了解的东西

一、java架构师必备基本知识 什么是Java中的内存泄漏?如何避免内存泄漏? Java中的内存泄漏是指程序在申请内存后,无法释放未再使用的内存空间。避免内存泄漏的方法包括:避免使用finalize方法,使用缓存时需要手动清理不再使用的对象,及时关闭资源等。 什么是Java中的多态…

dockerfile---创建镜像

dockerfile创建镜像&#xff1a;创建自定义镜像。 包扩配置文件的创建&#xff0c;挂载点&#xff0c;对外暴露的端口。设置环境变量。 docker镜像的方式: 1、基于官方源进行创建 根据官方提供的镜像源&#xff0c;创建镜像&#xff0c;然后拉起容器。是一个白板&#xff0c…

<JavaEE> 网络编程 -- 网络通信基础(协议和协议分层、数据封装和分用)

目录 一、IP地址 1&#xff09;IP地址的概念 2&#xff09;IP地址的格式 二、端口号 1&#xff09;端口号的概念 2&#xff09;端口号的格式 3&#xff09;什么是知名端口号&#xff1f; 三、协议 1&#xff09;协议的概念 2&#xff09;协议的作用 3&#xff09;TC…

IBIS AMI Model 算法模式的选择

常规的信号完整性仿真&#xff0c;只会包含传统的基于IBIS的芯片行为级模型&#xff0c;但高速串行总线在使用过程中&#xff0c;经常会由于传输信道或链路过长以及信号频率较高而造成信号衰减过大&#xff0c;接收端无法正确判别信号&#xff0c;因此&#xff0c;这类SerDes芯…

Amazon SageMaker:让机器学习变得更简单、更强大

授权说明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道。 前言&#xff1a; 在大数据时代的浪潮中&#xff0c;数据不再只…

指令数据:训练大模型的“隐形助力”

作者&#xff1a;谭婧 &#xff08;一&#xff09;指令数据&#xff0c;了解一下 先聊一件圈内趣事&#xff1a; 2023年初&#xff0c;大约在1月到2月份前后&#xff0c; 百度公司如流工作卡上有一个任务&#xff0c; 让百度员工打开脑洞&#xff0c;写“问答对”。 一问一答都…

HashMap常见面试问题

简述HashMap原理&#xff1f; HashMap基于数组加链表的方式来实现&#xff0c;数组下标通过hash值来计算&#xff0c;当下表冲突时&#xff0c;就会进行遍历链表&#xff0c;当链表长度大于8的时候会转化为红黑树。 HashMap的put过程&#xff1f; put的第一步是计算hash值&a…

什么时候使用匿名类,匿名类解决了什么问题?为什么需要匿名类 ?

匿名类通常在以下场景下使用&#xff1a; 一次性使用&#xff1a; 当你需要创建一个类的实例&#xff0c;但该类只在一个地方使用&#xff0c;而不打算在其他地方重复使用时&#xff0c;可以考虑使用匿名类。 简化代码&#xff1a; 当创建一个小型的、一次性的类会让代码更简洁…

VUE中如果让全局组件在某一页面不显示

目录 前言 方法一 1.在全局组件中添加一个变量用于控制显示与隐藏。 2.在全局组件的模板中使用 v-if 条件来决定是否显示该组件 3.在不需要显示全局组件的页面中&#xff0c;修改 showGlobalComponent 变量的值为 false&#xff0c;以隐藏全局组件。 4.在需要隐藏全局组…

TrustZone之总线请求

接下来&#xff0c;我们将查看系统中的总线请求者&#xff0c;如下图所示&#xff1a; 系统中的A型处理器具有TrustZone感知&#xff0c;并在每个总线访问中发送正确的安全状态。然而&#xff0c;大多数现代SoC还包含非处理器总线请求者&#xff0c;例如GPU和DMA控制器。 与完成…

​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展

在数字化时代&#xff0c;云计算成为推动创新和业务发展的关键驱动力。Amazon Lightsail 作为亚马逊云科技家族中的一员轻量应用服务器&#xff0c;为小型企业和创业公司提供了简便、强大的云计算服务。无论您是刚刚起步的初创公司&#xff0c;还是需要灵活而高效的云解决方案&…

第5次实验:ICMP

目的&#xff1a; 要看ICMP&#xff08;互联网控制信息协议&#xff09;是如何使用的。ICMP是IP的一个配套协议&#xff0c;通过处理各种错误和测试情况&#xff0c;帮助IP执行其功能。通过处理各种错误和测试情况来帮助IP执行其功能。它在第5.6.4节中有介绍。文中有涉及。在做…

vue 中国省市区级联数据 三级联动

vue 中国省市区级联数据 三级联动 安装插件 npm install element-china-area-data5.0.2 -S 当前版本以测试&#xff0c;可用。组件中使用了 element-ui, https://element.eleme.cn/#/zh-CN/component/installation 库 请注意安装。插件文档 https://www.npmjs.com/package/ele…

【CANoe】CANoe中使用RS232

文章目录 1、CANoe中自带示例2、示例讲解2.1CANoe自带Port A和Port B通讯2.2CANoe自带Port A和串口助手通讯 1、CANoe中自带示例 我使用的事CANoe12&#xff0c;RS232路径如下&#xff1a; C:\Users\Public\Documents\Vector\CANoe\Sample Configurations 12.0.75\IO_HIL\RS23…

如何安全运行别人上传的Python代码?

写后端的同学&#xff0c;有时候需要在网站上实现一个功能&#xff0c;让用户上传或者编写自己的Python代码。后端再运行这些代码。 涉及到用户自己上传代码&#xff0c;我们第一个想到的问题&#xff0c;就是如何避免用户编写危险命令。如果用户的代码里面涉及到下面两行&…

人工智能联盟的首件神兵利器——“Purple Llama” 项目,旨为保护工智能模型安全性

Meta公司&#xff08;Meta Platform Inc&#xff09;&#xff0c;原名Facebook&#xff0c;创立于2004年2月4日&#xff0c;市值5321.71亿美元。总部位于美国加利福尼亚州门洛帕克。 Meta 公司推出了名为“Purple Llama”的项目&#xff0c;旨在保护和加固其开源人工智能模型。…

部署Nextcloud详细步骤及优化方法

一、安装PHP8.0以上 我这里使用PHP8.0.30 [rootlocalhost ~]# php -v PHP 8.0.30 (cli) (built: Aug 3 2023 17:13:08) ( NTS gcc x86_64 ) Copyright (c) The PHP Group Zend Engine v4.0.30, Copyright (c) Zend Technologies [rootlocalhost ~]# 安装方法参考 二、安装MY…

安卓版pytCharm怎么启动,安卓版手游免费下载

大家好&#xff0c;给大家分享一下安卓版python编辑器安装tkinter&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 我的GitHub 是的&#xff0c;Python 代码可以在 Android 平台上运行&#xff0c;尽管这并不是 Python 传统上最常用的运行环…

正则表达式(6):分组与后向引用

正则表达式&#xff08;6&#xff09;&#xff1a;分组与后向引用 总结 本博文转载自 在本博客中&#xff0c;”正则表达式”为一系列文章&#xff0c;如果你想要从头学习怎样在Linux中使用正则&#xff0c;可以参考此系列文章&#xff0c;直达链接如下&#xff1a; 在Linux中…

【Stm32-F407】Keil uVision5 下新建工程

①双击鼠标左键打开Keil uVision5&#xff0c;选择 Project 下的 New uVision Project &#xff1b; ②在期望的文件夹下创建一个工程&#xff0c;并按如下要求操作&#xff1b; ③添加文件类型&#xff0c;按如下要求操作 ④如有需要可添加相关启动文件在工程文件夹下并添加到…