SpringSecurity6 | 委派筛选器代理和过滤器链代理

在这里插入图片描述

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉
🍎个人主页:Leo的博客
💞当前专栏: Java从入门到精通
✨特色专栏: MySQL学习
🥭本文内容:SpringSecurity6 | 委派筛选器代理和过滤器链代理
🖥️个人小站 :个人博客,欢迎大家访问
📚个人知识库: Leo知识库,欢迎大家访问

文章目录

    • 1.前言
    • 2.剖析DelegatingFilterProxy
      • 2.1DelegatingFilterProxy概述
      • 2.类的结构
      • 3.类的属性
    • 3.DelegatingFilterProxy原理
      • 3.1init
      • 3.2dofilter
      • 3.3destroy
    • 4.DelegatingFilterProxy作用
    • 5.FilterChainProxy
      • 5.1FilterChainProxy概述
      • 5.2FilterChainProxy的作用
    • 6.SecurityFilterChain概述
    • 6.参考文献
    • 7.总结

学习参考 :

  • 讲师:孙帅老师
  • 课程:孙哥说SpringSecurity6

image-20231030235443828

1.前言

大家好,我是Leo哥🫣🫣🫣,上一节我们简单回顾了一下关于Servlet原生过滤器以及简单认识了SpringSecurity中的一些过滤器。但是底层SpringSecurity是如何维护这些过滤器,并通过这些过滤器是如果拦截我们的客户端请求的,我们都还只是停留在表层,今天就让我们去深入了解一下我们今天得主角—委派筛选器代理 DelegatingFilterProxy。好了,话不多说让我们开始吧😎😎😎。

2.剖析DelegatingFilterProxy

2.1DelegatingFilterProxy概述

Spring 提供了一个名为 DelegatingFilterProxy 的 Filter 实现,允许在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间建立桥梁。Servlet容器允许通过使用自己的标准来注册 Filter 实例,但它不知道 Spring 定义的 Bean。你可以通过标准的Servlet容器机制来注册 DelegatingFilterProxy,但将所有工作委托给实现 Filter 的SpringBean。

image-20231107195435608

简单来说,DelegatingFilterProxy就是一个对于servlet filter的代理,用这个类的好处主要是通过Spring容器来管理servlet filter的生命周期,

  • 还有就是如果filter中需要一些Spring容器的实例,可以通过spring直接注入,
  • 另外读取一些配置文件这些便利的操作都可以通过Spring来配置实现。

本质上来说DelegatingFilterProxy就是一个Filter,其间接实现了Filter接口,但是 在doFilter中其实调用的从Spring 容器中获取到的代理Filter的实现类delegate。

2.类的结构

先看下 DelegatingFilterProxy 类的继承链

image-20231107200106838

通过类图,可以看到 DelegatingFilterProxy 继承了 Filter 接口,最终生成了一个过滤器。

3.类的属性

我们通过IDEA打开我们之前的项目,双击Shift键,去查找他的源码,我们来简单看一下他的源码。

image-20231107200323099

根据上面代码,看到了熟悉的 targetBeanName 和 targetFilterLifecycle 参数。看到这些参数,不知道大家有没有想到之前我们最开始学习JavaWeb的时候,最初始配置的过滤器,就是在一个web.xml文件中进行双标签配置,为了让大家更清楚的明白,我们下面写一个简单的xml配置。

    <filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class><init-param><param-name>targetBeanName</param-name><param-value>springSecurityFilterChain</param-value></init-param><init-param><param-name>targetFilterLifecycle</param-name><param-value>true</param-value></init-param></filter>

简单讲下这些参数的作用:

  • targetBeanName: 字符串

targetBeanName 参数是在 DelegatingFilterProxy 的构造函数中使用的一个字符串参数。它用于指定要代理的目标过滤器的 bean 名称。

当 DelegatingFilterProxy 接收到请求时,它将查找 Spring 容器中与 targetBeanName 参数匹配的目标过滤器的 bean。然后,DelegatingFilterProxy 将委托实际的过滤工作给找到的目标过滤器实例。

通过使用 targetBeanName 参数,你可以指定要使用的目标过滤器的 bean 名称,而不需要直接引用目标过滤器的类或实例。

在示例二中,我们将 DelegatingFilterProxy 配置为过滤器,并通过 <init-param> 元素指定了 targetBeanName 参数的值为 springSecurityFilterChain。这意味着 DelegatingFilterProxy 将在 Spring 容器中查找名为 springSecurityFilterChain 的目标过滤器的 bean,并将实际的过滤工作委托给它。

请注意,springSecurityFilterChain 应该替换为实际的目标过滤器的 bean 名称。该名称必须与 Spring 容器中定义的目标过滤器的 bean 名称匹配。

  • targetFilterLifecycle: 布尔

targetFilterLifecycle 参数是在 DelegatingFilterProxy 的构造函数中使用的一个布尔值参数。它用于指定是否由 DelegatingFilterProxy 负责管理目标过滤器的生命周期。

如果将 targetFilterLifecycle 参数设置为 true,则 DelegatingFilterProxy 将负责调用目标过滤器的 init() 和 destroy() 方法。这意味着 DelegatingFilterProxy 将在容器启动时自动调用目标过滤器的 init() 方法,并在容器关闭时调用目标过滤器的 destroy() 方法。

如果将 targetFilterLifecycle 参数设置为 false,则 DelegatingFilterProxy 将不会管理目标过滤器的生命周期。这意味着你需要手动调用目标过滤器的 init() 和 destroy() 方法,确保它们在适当的时候被调用。

默认情况下,targetFilterLifecycle 参数被设置为 false,即 DelegatingFilterProxy 不会管理目标过滤器的生命周期。如果你希望 DelegatingFilterProxy 管理目标过滤器的生命周期,你可以将 targetFilterLifecycle 参数设置为 true。

在上面的代码示例中,通过配置将 targetFilterLifecycle 设置为 true,以便让 DelegatingFilterProxy 管理目标过滤器的生命周期。

3.DelegatingFilterProxy原理

DelegatingFilterProxy 作为一个过滤器,过滤器的基本声明周期当然不会少。标准过滤器函数三大件:init, doFilter, destroy,这三个函数也是我们最关心的函数,至于 DelegatingFilterProxy 的奥秘也是通过这几个函数体现出来,当然最重要的还是 init, doFilter。下面我们就通过源码的方式一一去查看DelegatingFilterProxy他的生命周期。

3.1init

init 函数定义在 DelegatingFilterProxy 所继承的抽象类 GenericFilterBean 中。

@Override
public final void init(FilterConfig filterConfig) throws ServletException {Assert.notNull(filterConfig, "FilterConfig must not be null");this.filterConfig = filterConfig;// Set bean properties from init parameters.PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);if (!pvs.isEmpty()) {try {BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());Environment env = this.environment;if (env == null) {env = new StandardServletEnvironment();}bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, env));initBeanWrapper(bw);bw.setPropertyValues(pvs, true);}catch (BeansException ex) {String msg = "Failed to set bean properties on filter '" +filterConfig.getFilterName() + "': " + ex.getMessage();logger.error(msg, ex);throw new ServletException(msg, ex);}}// Let subclasses do whatever initialization they like.// 重点关注:🔥🔥🔥🔥🔥🔥initFilterBean();if (logger.isDebugEnabled()) {logger.debug("Filter '" + filterConfig.getFilterName() + "' configured for use");}
}

image-20231107202119949

接着看下 initFilterBean 的实现,此函数实现定义为 DelegatingFilterProxy 中。

image-20231107202444609

可以看到在DelegatingFilterProxy我们找到了关于initFilterBean的实现,我们来简单解读一下。

  1. synchronized (this.delegateMonitor):使用synchronized关键字对delegateMonitor进行同步,以确保在多线程环境下只有一个线程可以进入临界区。这样做是为了避免多个线程同时执行初始化操作而导致的竞态条件和数据不一致问题。
  2. if (this.delegate == null):检查delegate对象是否已经被初始化,如果没有,则继续执行初始化操作。
  3. if (this.targetBeanName == null):如果没有指定目标bean的名称,就使用过滤器的名称作为目标bean的名称。
  4. this.targetBeanName = getFilterName():通过getFilterName()方法获取过滤器的名称,并将其作为目标bean的名称。
  5. WebApplicationContext wac = findWebApplicationContext():调用findWebApplicationContext()方法来查找并获取Spring根应用程序上下文。这个上下文是用来获取bean实例的,因此在初始化delegate之前需要先获取它。
  6. if (wac != null) { this.delegate = initDelegate(wac); }:如果找到了Spring根应用程序上下文,则调用initDelegate方法对delegate进行初始化。这个initDelegate方法可能会根据具体需求做一些初始化工作,比如创建并配置delegate对象。

总的来说,这段代码的作用是在过滤器初始化时,尽早地初始化delegate对象。通过同步机制和延迟初始化策略,确保在多线程环境下安全地进行初始化操作,并尽可能地提前准备好delegate对象以供后续使用。

那么如何尽早的初始化delegate对象呢,这个initDelegate方法又做了什么呢,我们接着往下解读。

image-20231107202928322

  1. String targetBeanName = getTargetBeanName();:通过getTargetBeanName方法获取目标bean的名称,这个名称在之前的初始化过程中已经确定。
  2. Assert.state(targetBeanName != null, "No target bean name set");:使用断言确保目标bean的名称不为空,如果为空则抛出异常。这样可以在代码中明确地表达出对目标bean名称的依赖性,以避免潜在的空指针异常。
  3. Filter delegate = wac.getBean(targetBeanName, Filter.class);:通过Spring的应用程序上下文(wac)根据目标bean的名称获取对应的Filter实例。这里利用了Spring的IoC容器来管理和获取Filter实例。
  4. if (isTargetFilterLifecycle()) { delegate.init(getFilterConfig()); }:如果目标Filter需要进行生命周期初始化(例如调用init方法),则调用delegate的init方法并传入过滤器的配置信息。这是为了确保目标Filter在需要时能够正确地进行初始化工作。
  5. return delegate;:返回初始化后的delegate对象,该对象已经准备好被使用了。

总的来说,这个initDelegate方法的主要作用是根据目标bean的名称从Spring的应用程序上下文中获取对应的Filter实例,并根据需要进行生命周期的初始化工作。这样就能够在initFilterBean方法中及时地准备好delegate对象,以供后续的过滤器处理流程使用。

这一下就非常清晰了。

3.2dofilter

doFilter 函数实现定义为 DelegatingFilterProxy 中,我们直接看源码。

image-20231107203046568

我们进行解读:

  1. Filter delegateToUse = this.delegate;:首先将当前已初始化的delegate对象赋给delegateToUse,这里使用了委托模式,即通过delegateToUse来执行具体的过滤器操作。
  2. if (delegateToUse == null) { ... }:检查delegateToUse是否为null,如果为null则表示delegate对象尚未初始化,需要进行延迟初始化。
  3. synchronized (this.delegateMonitor) { ... }:使用synchronized关键字对delegateMonitor进行同步,以确保在多线程环境下只有一个线程可以进入临界区。这样做是为了避免多个线程同时执行初始化操作而导致的竞态条件和数据不一致问题。
  4. delegateToUse = this.delegate;:再次将当前已初始化的delegate对象赋给delegateToUse,因为在同步块外部可能已经有其他线程初始化了delegate对象。
  5. if (delegateToUse == null) { ... }:再次检查delegateToUse是否为null,因为在同步块外部可能已经有其他线程初始化了delegate对象。
  6. WebApplicationContext wac = findWebApplicationContext();:查找并获取Spring根应用程序上下文。如果没有找到上下文,则抛出异常,这是为了确保能够获取到必要的上下文信息用于后续的初始化操作。
  7. delegateToUse = initDelegate(wac);:调用initDelegate方法对delegateToUse进行初始化。这里利用了之前定义的initDelegate方法来完成初始化工作。
  8. this.delegate = delegateToUse;:将初始化后的delegateToUse对象赋给delegate,以便在以后的处理中可以直接使用已经初始化好的delegate对象。
  9. invokeDelegate(delegateToUse, request, response, filterChain);:调用invokeDelegate方法,让已经准备好的delegateToUse对象执行实际的过滤操作。这样就实现了通过代理对象来执行具体过滤器的功能。

3.3destroy

destroy 函数实现定义为 DelegatingFilterProxy 中。

image-20231107203459005

这里就没什么好说的了,DelegatingFilterProxy在这里就像一个甩锅的大爷,根本不需要他去干活,他直接就把其交给委托过滤器来做了。

4.DelegatingFilterProxy作用

上面我们已经了解了关于DelegatingFilterProxy流程,以及简单看了一下他的源码。这里我们来简单总结一下。

作用: 实现把Servlet容器中的Filter 同Spring 容器中的 bean 关联起。是Servlet容器和Spring 的 ApplicationContext 之间的桥梁。

  1. 委托管理:DelegatingFilterProxy允许将对Servlet规范中的Filter接口的调用委托给Spring应用程序上下文中的一个或多个过滤器Bean。这些过滤器Bean可以由Spring的IoC容器进行管理,并且可以利用Spring的依赖注入等特性来进行配置和定制。
  2. 集成Spring特性:通过DelegatingFilterProxy,可以将Spring的AOP、事务管理等特性集成到Servlet规范的过滤器中。这样能够更好地利用Spring框架的各种功能来处理Web应用程序中的过滤逻辑。
  3. 延迟初始化:DelegatingFilterProxy支持延迟初始化和懒加载,它可以在需要时动态地将请求委托给具体的过滤器Bean,而不需要预先在web.xml中配置具体的过滤器类。这样可以避免在应用启动时立即创建所有过滤器实例,从而提高了系统的性能和资源利用率。
  4. 安全性:DelegatingFilterProxy可以被配置为要求认证的请求使用安全通道,从而提供一定程度的安全保障。

原生的Filter运行在Servlet容器里边也就是Tomcat服务器当中,而Spring的所书写的过滤器属于Spring工厂。Spring工厂中的过滤器是没有办法拦截Http请求并进行干预的,但是原生Filter就可以做到直接拦截Http请求并进行干预,就比如DelegatingFilterProxy,所以借助它,Spring当中的Filter就可以过滤和干预Http请求了。

5.FilterChainProxy

5.1FilterChainProxy概述

在上面的源码环节中,我们已经简单的认识到了FilterChainProxy就是一个过滤器链代理。那具体什么是FilterChainProxy呢?

  1. 统一的安全过滤器链管理:
    • FilterChainProxy负责统一管理多个SecurityFilterChain,每个SecurityFilterChain都代表一组安全过滤器,用于处理特定模式的请求。这样可以在一个应用中同时支持多个安全策略,或者针对不同的URL模式使用不同的安全过滤器链。
  2. 根据请求匹配合适的安全过滤器链:
    • 当收到一个HTTP请求时,FilterChainProxy会根据请求的URL和其他条件来选择合适的SecurityFilterChain,然后按照SecurityFilterChain中定义的顺序执行其中的安全过滤器。这样可以确保请求能够得到正确的安全处理。
  3. 灵活的安全策略配置:
    • FilterChainProxy允许开发人员通过配置来定义多个SecurityFilterChain,以适应不同的安全需求。这种灵活性使得可以针对不同的URL模式或特定的安全需求定制化安全过滤器链。
  4. 核心的安全过滤器调度器:
    • 在Spring Security框架中,FilterChainProxy可以看作是核心的安全过滤器调度器,它负责将请求交给正确的安全过滤器链进行处理,从而实现认证、授权、会话管理等安全操作。

下面给出Spring官方给出的解释:

Spring Security 的 Servlet 支持包含在 FilterChainProxy 中。FilterChainProxy 是 SpringSecurity 提供的一个特殊的 Filter,允许通过 SecurityFilterChain 委托给许多 Filter 实例。由于 FilterChainProxy 是一个Bean,它通常被包裹在 DelegatingFilterProxy 中。

我们通过一张图来更清晰地认识他吧。

image-20231107204747766

5.2FilterChainProxy的作用

SecurityFilterChain 中的 Security Filter 通常是Bean,但它们是用 FilterChainProxy 而不是 DelegatingFilterProxy 注册的。与直接向Servlet容器或 DelegatingFilterProxy 注册相比,FilterChainProxy 有很多优势。首先,它为 Spring Security 的所有 Servlet 支持提供了一个起点。由于这个原因,如果你试图对 Spring Security 的 Servlet 支持进行故障诊断,在 FilterChainProxy 中添加一个调试点是一个很好的开始。

其次,由于 FilterChainProxy 是 Spring Security 使用的核心,它可以执行一些不被视为可有可无的任务。 例如,它清除了 SecurityContext 以避免内存泄漏。它还应用Spring Security的 HttpFirewall 来保护应用程序免受某些类型的攻击。

此外,它在确定何时应该调用 SecurityFilterChain 方面提供了更大的灵活性。在Servlet容器中,Filter 实例仅基于URL被调用。 然而,FilterChainProxy 可以通过使用 RequestMatcher 接口,根据 HttpServletRequest 中的任何内容确定调用。

最主要的就是把请求传递给一个或者多个SecurityFilterChain示例进行认证或授权,并且能够需要时进行重定向和返回错误信息。

我们基于上面的图,来分析一下,一个客户端请求到Servlet中,其中到底是如何经过层层过滤器的。

首先客户端发起一个Http请求,这个请求会经过原生过滤器Filter-1,此时我们的FilterChain会通过**doFilter()**方法进行放心。之后这个请求会通过Filter-1传递到第二个原生过滤器,也就是我们的DelegatingFilterProxy。此时会搭建一个前往Spring工厂中的桥梁,但是我们并不能直接去通过这个桥梁访问我们的SecurityFilterChain,而是还需要通过桥梁中的另一个过滤器,也就是我们的过滤器链代理FilterChainProxy,他会将我们的请求重定向到SecurityFilterChain中。然后回基于匹配的规则,一个一个去执行Filter。执行完之后,会进行返回,原路返回到我们的FilterChainProxy这个过滤器链代理中。于是由原生过滤器接着向下面过滤器继续请求,如果下面没有过滤器了,则直接到Web资源了。

6.SecurityFilterChain概述

SecurityFilter 并不是直接放在 Web 项目的原生 FilterChain 中,而是通过一个FlterChainProxy来统管理

FilterChainProxy把 SecurityFilterChain 嵌入到 Web项目的原生过滤器链中DelegatingFilterProxy 把 FilterChainProxy 整合到原生的过滤器链中

FilterChainProxy 是顶层管理者,统一管理 SecurityFilter和 SecurityFllterChain过涉器链。

当请求到达 FilterChainProxy 时,会根据当前请求匹配SecurityFilterChain,然后将请求依次转发给 SecurityFilterChain 中的 SecurityFilter中。

6.参考文献

  • https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html
  • https://springdoc.cn/spring-security/servlet/architecture.html

7.总结

以上便是本文的全部内容,本人才疏学浅,文章有什么错误的地方,欢迎大佬们批评指正!我是Leo,一个在互联网行业的小白,立志成为更好的自己。

如果你想了解更多关于Leo,可以关注公众号-程序员Leo,后面文章会首先同步至公众号。

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

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

相关文章

Unreal Engine 学习笔记 (1)—— 日夜交替

1.创建关卡 文件新建关卡空白关卡保存关卡&#xff08;命名为NewWorld&#xff09; 2.创建蓝图类 创建蓝图类&#xff08;继承自Actor&#xff09; 命名为SunAndMoon 3.编辑SunAndMoon蓝图类 添加SkyAtmosphere添加SkyLight添加DirectionalLight将DirectionalLight重命名为…

C++入门学习(1)命名空间和输入输出

前言 在C语言和基本的数据结构学习之后&#xff0c;我们终于迎来了期待已久的C啦&#xff01;C发明出来的意义就是填补一些C语言的不足&#xff0c;让我们更加方便的写代码&#xff0c;所以今天我们就来讲一下C语言不足的地方和在C中的解决办法&#xff01; 一、命名空间 在学习…

OWASP TOP 10-XML外部实体(XXE)

文章目录 XML基础漏洞原理攻击方式 XML基础 XML用于标记电子文件使其具有结构性的标记语言&#xff0c;可以用来标记数据、定义数据类型&#xff0c;是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义&#xff08;可选&#xff09;、…

【机器学习3】有监督学习经典分类算法

1 支持向量机 在现实世界的机器学习领域&#xff0c; SVM涵盖了各个方面的知识&#xff0c; 也是面试题目中常见的基础模型。 SVM的分类结果仅依赖于支持向量&#xff0c;对于任意线性可分的两组点&#xff0c;它 们在SVM分类的超平面上的投影都是线性不可分的。 2逻辑回归 …

Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库

背景介绍 Apache Doris是一个基于MPP架构的易于使用&#xff0c;高性能和实时的分析数据库&#xff0c;以其极高的速度和易用性而闻名。海量数据下返回查询结果仅需亚秒级响应时间&#xff0c;不仅可以支持高并发点查询场景&#xff0c;还可以支持高通量复杂分析场景。 这些都…

2586. 统计范围内的元音字符串数 --力扣 --JAVA

题目 给你一个下标从 0 开始的字符串数组 words 和两个整数&#xff1a;left 和 right 。 如果字符串以元音字母开头并以元音字母结尾&#xff0c;那么该字符串就是一个 元音字符串 &#xff0c;其中元音字母是 a、e、i、o、u 。 返回 words[i] 是元音字符串的数目&#xff0c;…

Mac版eclipse如何安装,运行bpmn文件

一、下载程序包 网址&#xff1a;https://www.eclipse.org/downloads M2芯片安装包名称&#xff1a;eclipse-jee-2022-12-R-macosx-cocoa-aarch64.dmg 具体安装包版本根据自己电脑型号选择 二、eclipse安装步骤 1&#xff09;双击下载的文件 2&#xff09;将eclipse拖入到…

Nvidia显卡Failed to initialize NVML Driver/library version mismatch错误解决方案

最近GPT比较火&#xff0c;开始折腾了一下gpu,用来跑项目&#xff1a; https://github.com/OpenTalker/SadTalker 今天运行程序突然发现用不了&#xff0c;经排查应该是由于NVIDIA内核驱动版本与系统驱动版本不一致导致的。 下面简单总结了这个错误的解决方案。 问题复现 查看…

Collection子接口——Set接口

1、HashSet: set接口的主要实现类&#xff0c;可存储null&#xff0c;线程不安全。 2、LinkedHashSet&#xff1a;HashSet的子类&#xff1b;遍历时&#xff0c;可按添加的顺序遍历。 3.、TreeSet&#xff1a;可以按添加元素的指定属性&#xff0c;进行排序。 注&#xff1a;Se…

linux修改rocketmq的日志文件位置

文章目录 &#x1f50a;修改rocketmq的日志文件位置&#x1f4d5;原来的文件&#x1f4cc;修改后文件&#x1f4c7;rocketmq中的Rocketmq_client.log文件在配置文件中改不了 需要在代码logback文件中进行修改&#x1f58a;️最后总结 &#x1f50a;修改rocketmq的日志文件位置 …

C#通过TCP发送List<string>

using System; using System.IO; using System.Net.Sockets; using System.Text; using System.Collections.Generic;public static void SendList<string>(Stream stream, List<string> list) {// 将List<string>对象转换为字节数组byte[] data Encoding.U…

没网络也能安装.Net 3.5!如何脱机安装.NET Framework 3.5

.NET框架是由微软制定的一个软件框架。它有助于在Windows上运行控制台、Web或移动应用程序。此有用的工具适用于Windows设备。 如何脱机安装.NET Framework 3.5 如果你拥有Windows 10、8、8.1或7,有时第三方软件可能会导致问题。你可能会在图片中看到这样的问题。 看这张照片…

新版onenet平台安全鉴权的确定与使用

根据onenet官方更新的文档&#xff1a;平台提供开放的API接口&#xff0c;用户可以通过HTTP/HTTPS调用&#xff0c;进行设备管理&#xff0c;数据查询&#xff0c;设备命令交互等操作&#xff0c;在API的基础上&#xff0c;根据自己的个性化需求搭建上层应用。 为提高API访问安…

棋牌室电脑计时灯控,棋牌室计时灯控安装,佳易王计时计费管理系统软件

棋牌室电脑计时灯控&#xff0c;棋牌室计时灯控安装&#xff0c;佳易王计时计费管理系统软件 棋牌室的灯可以用佳易王计时计费软件来控制开关&#xff0c;当开始计时的时候&#xff0c;软件发送开灯的指令&#xff0c;灯打开&#xff0c;在结账后&#xff0c;软件发送关灯指令…

算法通关村第十四关黄金挑战——堆解决数据流的中位数和数组求中位数的方法总结

关注微信公众号&#xff1a;怒码少年。回复关键词【电子书】&#xff0c;领取多本计算机相关电子书 遇到任何问题都可以在后台向我提问&#xff0c;完全免费&#xff01;&#xff01;大家共同进步&#xff01;&#xff01; 大家好&#xff0c;我是怒码少年小码。 本篇主要是讲…

软件测试/测试开发丨性能测试体系学习笔记

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/28412 一、性能测试体系 1.性能测试介绍 为什么要做性能测试 有效的性能测试能给研发&#xff0c;运维团队提供有效的容量规划能力&#xff0c;系统风险…

Oracle 安装及 Spring 使用 Oracle

参考内容&#xff1a; docker安装oracle数据库史上最全步骤&#xff08;带图文&#xff09; Mac下oracle数据库客户端 Docker安装Oracle docker能安装oracle吗 Batch script for add a auto-increased primary key for exist table with records Docker 安装 Oracle11g 注意&a…

element 弹窗浏览器后退-遮照层还存在问题 以及跟vue keep-alive冲突

问题&#xff1a;element 弹窗浏览器后退-遮照层还存在问题 查询官网可以设置 modal-append-to-body“false” 可以全局设置 ElementUI.Dialog.props.modalAppendToBody.default false 后续 基本到这能解决问题&#xff0c;不过本项目比较特殊&#xff0c;使用了 keep-alive…

自定义拖拽列表

效果图 DataAnalysis.vue <template><div class"app-container"><div class"operate"><el-select class"t_select" v-model"templateName" clearable placeholder"模版" size"default" cle…

java计算机毕业设计SpringBoot在线答疑系统

项目介绍 本文从学生的功能要求出发&#xff0c;建立了在线答疑系统&#xff0c;系统中的功能模块主要是实现管理员权限&#xff1b;首页、个人中心、学生管理、教师管理、问题发布管理、疑难解答管理。教师权限&#xff1a;首页、个人中心、疑难解答管理、试卷管理、试题管理…