SpringSecurity源码学习一:过滤器执行原理
目录
- 1. web过滤器Filter
- 1.1 filter核心类
- 1.2 GenericFilterBean
- 1.3 DelegatingFilterProxy
- 1.3.1 原理
- 1.3.2 DelegatingFilterProxy源码
- 2. FilterChainProxy源码学习
- 2.1 源码
- 2.1.1 doFilterInternal方法源码
- 2.1.1.1 getFilters()方法源码
- 2.1.1.2 VirtualFilterChain方法源码
- 3. 总结
在Spring Security源代码中,过滤器是用于处理安全相关任务的关键组件之一。它们用于在请求到达应用程序之前或之后执行特定的安全操作。
Spring Security中的过滤器链是一个由多个过滤器组成的链式结构。每个过滤器都负责执行特定的安全任务,并将请求传递给下一个过滤器。这些过滤器一起协同工作,以实现身份验证、授权、会话管理等安全功能。
在Spring Security源码中,有一个关键的过滤器叫做 FilterChainProxy ,它是整个过滤器链的入口点。 FilterChainProxy 负责协调和执行所有其他过滤器,并确保请求按照正确的顺序通过它们。 也就是我们自定义的过滤器是通过 FilterChainProxy插入到web过滤器链中的
一般过滤器链路
使用Spring Security的过滤器链路
1. web过滤器Filter
Filter就是过滤器,在JavaWeb体系中,他位于服务端,卡在请求/响应与Servlet之间做一些操作。通过过滤器我们可以做很多操作,例如:
-
认证和授权:Spring Security框架中的过滤器可以用于对用户进行身份验证和授权,以保护Web应用程序的安全性。
-
日志记录:可以使用过滤器记录HTTP请求和响应的详细信息,以便进行故障排除和性能优化。
-
缓存:可以使用过滤器实现缓存机制,以提高Web应用程序的性能和响应速度。
-
压缩:可以使用过滤器对响应进行压缩,以减少数据传输量,提高Web应用程序的性能。
-
字符编码:可以使用过滤器对请求和响应进行字符编码,以确保正确的数据传输和显示。
1.1 filter核心类
在Spring Web中,实现过滤器的核心类是 javax.servlet.Filter 。这是Java Servlet API中定义的标准接口。要在Spring Web中创建过滤器,通常需要创建一个实现 Filter 接口的类,并重写其方法,例如 doFilter() 。
以下是与Spring Web过滤器相关的一些核心类:
- OncePerRequestFilter :这是Spring提供的一个抽象类,扩展了 GenericFilterBean 。它确保每个请求只调用一次过滤器的 doFilter() 方法。
- GenericFilterBean :这是一个方便的基类,用于创建过滤器。它实现了 Filter 接口,并提供了额外的功能,如bean生命周期管理。
- DelegatingFilterProxy :这是一个Spring特定的类,它将过滤器功能委托给Spring应用程序上下文中定义的目标bean。通常在想要将过滤器应用于特定URL或向过滤器注入依赖项时使用。
这些核心类有助于在Spring Web应用程序中创建和配置过滤器,允许您根据需求拦截和处理HTTP请求和响应。
1.2 GenericFilterBean
GenericFilterBean 是Spring Web框架提供的一个方便的基类,用于创建过滤器。它实现了 Filter 接口,并提供了一些额外的功能,例如bean生命周期管理和依赖注入。 GenericFilterBean 是自带init方法的,我们继承后只需要重写doFilter方法。
直接继承 GenericFilterBean 可以带来以下好处:
-
简化过滤器的创建:继承 GenericFilterBean 可以减少编写过滤器所需的代码量。它提供了一些默认的实现和方法,使得创建过滤器变得简单和方便。
-
生命周期管理: GenericFilterBean 实现了 Filter 接口,并提供了生命周期管理的功能。它可以在过滤器的初始化和销毁过程中执行相应的操作,例如资源的初始化和释放。
-
依赖注入: GenericFilterBean 支持依赖注入,您可以使用 @Autowired 注解将其他Spring bean注入到过滤器中。这使得在过滤器中使用其他组件、服务或依赖变得更加容易。
-
配置灵活性:通过继承 GenericFilterBean ,您可以根据需要自定义和配置过滤器的行为。您可以重写 doFilter() 方法以实现自定义的请求和响应处理逻辑,并使用其他提供的方法来访问过滤器的配置和上下文信息。
1.3 DelegatingFilterProxy
上边我们讲了GenericFilterBean,我们可以通过继承GenericFilterBean编写过滤器。此时的自定义的过滤器是交由Spring容器管理的,并不属于web的Servlet 容器管理。因此可以将过滤器分类两类,第一类是由 Web 容器进行管理,第二类是由 Spring 进行管理的,DelegatingFilterProxy 就是连接二者的桥梁。
1.3.1 原理
DelegatingFilterProxy是Spring框架提供的一个过滤器代理类,它可以将请求转发到一个或多个过滤器。它的原理是将一个实现了javax.servlet.Filter接口的Bean转换为Servlet过滤器,并将这些过滤器应用到Web应用程序中。
在Spring中,DelegatingFilterProxy通常与Spring的IoC容器结合使用。当一个请求到达Web应用程序时,Servlet容器会将请求传递给DelegatingFilterProxy。DelegatingFilterProxy会从Spring的IoC容器中获取目标过滤器的实例,并调用它的doFilter方法来处理请求。
1.3.2 DelegatingFilterProxy源码
@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)throws ServletException, IOException {// Lazily initialize the delegate if necessary.//这个时候经过初始化,filter已经是spring容器中的bean,在spring-Security中,此时delegateToUse就是spring-Security的FilterChainProxyFilter delegateToUse = this.delegate;if (delegateToUse == null) {synchronized (this.delegateMonitor) {delegateToUse = this.delegate;if (delegateToUse == null) {WebApplicationContext wac = findWebApplicationContext();if (wac == null) {throw new IllegalStateException("No WebApplicationContext found: " +"no ContextLoaderListener or DispatcherServlet registered?");}delegateToUse = initDelegate(wac);}this.delegate = delegateToUse;}}// Let the delegate perform the actual doFilter operation.//代理模式 让代理Filter执行实际的doFilter方法,在spring-Security中,此时就是执行spring-Security的FilterChainProxyinvokeDelegate(delegateToUse, request, response, filterChain);}
在源码中我们可以看到,使用了代理模式执行了被代理的过滤器的过滤方法。
2. FilterChainProxy源码学习
在Spring Security中,FilterChainProxy是一个核心的过滤器,它负责处理Web请求的安全过滤链。它的作用是将请求传递给一系列的安全过滤器,以便进行身份验证、授权、会话管理等安全相关的操作。
FilterChainProxy的主要功能是根据配置的安全过滤器链来处理请求。在Spring Security的配置中,可以定义多个过滤器,并指定它们的顺序和应用于特定URL模式的条件。当一个请求到达应用程序时,FilterChainProxy会按照配置的顺序依次调用这些过滤器,直到找到适用于请求的过滤器链。
每个过滤器链都包含一系列的过滤器,它们按照特定的顺序执行。这些过滤器可以执行不同的安全操作,例如身份验证过滤器用于验证用户的身份,授权过滤器用于检查用户是否具有访问权限,会话管理过滤器用于管理用户的会话等。
通过FilterChainProxy,Spring Security提供了一种灵活的方式来定义和管理安全过滤器链。它可以根据应用程序的需求进行配置,并提供了强大的安全功能,以保护Web应用程序免受各种安全威胁。
2.1 源码
public class FilterChainProxy extends GenericFilterBean {
可以看到,FilterChainProxy是继承了GenericFilterBean,FilterChainProxy 也是一个过滤器。
@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {//此处是对请求是否第一次经过这个过滤器的判断,在spring security中的过滤器中这种用法是相当常见的boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;if (!clearContext) {doFilterInternal(request, response, chain);return;}try {request.setAttribute(FILTER_APPLIED, Boolean.TRUE);doFilterInternal(request, response, chain);}catch (RequestRejectedException ex) {this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex);}finally {//清除security上下文中的保存的Authentication对象SecurityContextHolder.clearContext();request.removeAttribute(FILTER_APPLIED);}}
这是FilterChainProxy的大概逻辑,细节点在doFilterInternal方法中。
2.1.1 doFilterInternal方法源码
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {//初始化request,对request进行一层包装,同时校验了一下请求的方式是否允许,以及请求地址中是否有非法字符FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);//初始化response,对response进行了一层包装,若在response中添加header或cookie时,会对header和cookie的一些属性进行校验HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);//SpringSecurity一般都会注册很多默认Filter,//注意这些Filter在容器启动时就初始化好了//获取spring security中第一个匹配请求地址的过滤器列表List<Filter> filters = getFilters(firewallRequest);if (filters == null || filters.size() == 0) {if (logger.isTraceEnabled()) {logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));}firewallRequest.reset();chain.doFilter(firewallRequest, firewallResponse);return;}if (logger.isDebugEnabled()) {logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));}//VirtualFilterChain是一个过滤器链FilterChain//将过滤器列表和servlet过滤器链包装成一个新的过滤器链 VirtualFilterChain 是FilterChainProxy的内部类VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);virtualFilterChain.doFilter(firewallRequest, firewallResponse);}
这段主要功能是:
- getFilters()方法,获取spring security中第一个匹配请求地址的过滤器列表
- 创建VirtualFilterChain过滤器链,执行所有过滤器。
2.1.1.1 getFilters()方法源码
/*** Returns the first filter chain matching the supplied URL.* @param request the request to match* @return an ordered array of Filters defining the filter chain*/private List<Filter> getFilters(HttpServletRequest request) {int count = 0;for (org.springframework.security.web.SecurityFilterChain chain : this.filterChains) {if (logger.isTraceEnabled()) {logger.trace(LogMessage.format("Trying to match request against %s (%d/%d)", chain, ++count,this.filterChains.size()));}if (chain.matches(request)) {return chain.getFilters();}}return null;}
根据请求地址匹配过滤器列表
2.1.1.2 VirtualFilterChain方法源码
//VirtualFilterChain是一个过滤器链FilterChain//将过滤器列表和servlet过滤器链包装成一个新的过滤器链 VirtualFilterChain 是FilterChainProxy的内部类VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);virtualFilterChain.doFilter(firewallRequest, firewallResponse);
/*** Internal {@code FilterChain} implementation that is used to pass a request through* the additional internal list of filters which match the request.*/private static final class VirtualFilterChain implements FilterChain {private final FilterChain originalChain;private final List<Filter> additionalFilters;private final FirewalledRequest firewalledRequest;private final int size;private int currentPosition = 0;private VirtualFilterChain(FirewalledRequest firewalledRequest, FilterChain chain,List<Filter> additionalFilters) {this.originalChain = chain;this.additionalFilters = additionalFilters;this.size = additionalFilters.size();this.firewalledRequest = firewalledRequest;}@Overridepublic void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {//如果没有下一个过滤器,则回到servlet过滤器链if (this.currentPosition == this.size) {if (logger.isDebugEnabled()) {logger.debug(LogMessage.of(() -> "Secured " + requestLine(this.firewalledRequest)));}// Deactivate path stripping as we exit the security filter chainthis.firewalledRequest.reset();this.originalChain.doFilter(request, response);return;}this.currentPosition++;Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);if (logger.isTraceEnabled()) {logger.trace(LogMessage.format("Invoking %s (%d/%d)", nextFilter.getClass().getSimpleName(),this.currentPosition, this.size));}//传入this,nextFilter中的会继续调用VirtualFilterChain.doFilter()直至所有过滤器执行结束或抛出异常nextFilter.doFilter(request, response, this);}}
VirtualFilterChain一个过滤器链,执行additionalFilters集合中的所有过滤器。 也可以把VirtualFilterChain当作SecurityFilterChain,additionalFilters集合中的元素就是 Security Filter,也就是我们自己写的过滤器。
3. 总结
在 上图中,一个请求进来会进入web容器中,从上到下按顺序执行过滤器。当执行到DelegatingFilterProxy过滤器中,会代理到FilterChainProxy类中。FilterChainProxy 决定应该使用哪个 SecurityFilterChain。只有第一个匹配的 SecurityFilterChain 被调用。
- 如果请求的URL是 /api/messages/,它首先与 /api/** 的 SecurityFilterChain0 模式匹配,所以只有 SecurityFilterChain0 被调用,尽管它也与 SecurityFilterChainn 匹配。
- 如果请求的URL是 /messages/,它与 /api/** 的 SecurityFilterChain0 模式不匹配,所以 FilterChainProxy 继续尝试每个 SecurityFilterChain。假设没有其他 SecurityFilterChain 实例相匹配,则调用 SecurityFilterChainn。
相关文章:
SpringSecurity源码学习一:过滤器执行原理
目录 1. web过滤器Filter1.1 filter核心类1.2 GenericFilterBean1.3 DelegatingFilterProxy1.3.1 原理1.3.2 DelegatingFilterProxy源码 2. FilterChainProxy源码学习2.1 源码2.1.1 doFilterInternal方法源码2.1.1.1 getFilters()方法源码2.1.1.2 VirtualFilterChain方法源码 3…...
8.2 JUC - 4.Semaphore
目录 一、是什么?二、简单使用三、semaphore应用四、Semaphore原理 一、是什么? Semaphore:信号量,用来限制能同时访问共享资源的线程上限 二、简单使用 public class TestSemaphore {public static void main(String[] args) …...
前端try和catch
为什么要使用try catch 使用try...catch语句是为了处理和管理可能会在程序运行过程中发生的异常或错误情况。以下是一些使用try...catch的主要原因: 错误处理:在开发过程中,无法避免地会出现各种错误,如网络请求失败、数据解析错误…...
Unity可视化Shader工具ASE介绍——2、ASE的Shader创建和输入输出
大家好,我是阿赵,这里继续介绍Unity可视化写Shader的ASE插件的用法。上一篇介绍了ASE的安装和编辑器界面分布,这一篇主要是通过一个简单的例子介绍shader的创建和输入输出。 一、ASE的Shader创建 这里先选择Surface类型的Shader,…...
目标检测算法改进系列之Backbone替换为Swin Transformer
Swin Transformer简介 《Swin Transformer: Hierarchical Vision Transformer using Shifted Windows》作为2021 ICCV最佳论文,屠榜了各大CV任务,性能优于DeiT、ViT和EfficientNet等主干网络,已经替代经典的CNN架构,成为了计算机…...
【技术干货】如何通过 DP 实现支持经典蓝牙的联网单品设备与 App 配对
经典蓝牙模块(Classic Bluetooth)主要用于呼叫和音频传输,所以经典蓝牙最主要的特点就是功耗大,传输数据量大。蓝牙耳机、蓝牙音箱等场景大多采用经典蓝牙,因为蓝牙是为传输声音而设计的,是短距离音频传输的…...
【Unity Build-In管线的SurfaceShader剖析_PBS光照函数】
Unity Build-In管线的SurfaceShader剖析 在Unity Build-In 管线(Universal Render Pipeline)新建一个Standard Surface Shader文件里的代码如下:选中"MyPBR.Shader",在Inspector面板,打开"Show generat…...
thinkphp5实现ajax图片上传,压缩保存到服务器
<div class"warp"><input type"file" id"file" accept"image/*" onchange"upimg(this)" /></div> <img src"" /> <script>//上传图片方法function upimg(obj){var fileData obj.…...
王道考研计算机网络——传输层
一、传输层概述 复用:发送方不同的应用进程都可以使用同一个传输层的协议来传送数据 分用:接收方的传输层在去除报文段的首部之后能把数据交给正确的应用进程 熟知端口号就是知名端口号0-1023 客户端使用的端口号是动态变化的,不是唯一确定…...
08 集群参数配置(下)
Kafka Broker不需要太大的堆内存? Kafka Broker不需要太大的堆内存?应该把内存留给页缓存使用? kafka刷盘时宕机 kafka认为写入成功是指写入页缓存成功还是数据刷到磁盘成功算成功呢?还是上次刷盘宕机失败的问题,页…...
mac文件为什么不能拖进U盘?
对于Mac用户来说,可能会遭遇一些烦恼,比如在试图将文件从Mac电脑拖入U盘时,却发现文件无法成功传输。这无疑给用户带来了很大的不便。那么,mac文件为什么不能拖进U盘,看完这篇你就知道了。 一、U盘的读写权限问题 如果…...
RK3568的CAN驱动适配
目录 背景: 1.内核驱动模块配置 2.设备树配置 3.功能测试 4.bug修复 背景: 某个项目上使用RK3568的芯片,需要用到4路CAN接口进行通信,经过方案评审后决定使用RK3568自带的3路CAN外加一路spi转的CAN实现功能,在这个…...
Opengl之立方体贴图
简单来说,立方体贴图就是一个包含了6个2D纹理的纹理,每个2D纹理都组成了立方体的一个面:一个有纹理的立方体。你可能会奇怪,这样一个立方体有什么用途呢?为什么要把6张纹理合并到一张纹理中,而不是直接使用6个单独的纹理呢?立方体贴图有一个非常有用的特性,它可以通过一…...
EF Core报错:Error Number:-2146893019
appsettings.json中的连接字符串要添加上:TrustServerCertificatetrue; 所以这里的连接字符串为:Data SourceLAPTOP-61GDB2Q7\\SQLEXPRESS;Initial CatalogMvcMovie.Data;Persist Security InfoTrue;TrustServerCertificatetrue;User IDsa;Passwordroot…...
QT之可自由折叠和展开的布局
介绍和功能分析 主要是实现控件的折叠和展开,类似抽屉控件,目前Qt自带的控件QToolBox具有这个功能,但是一次只能展开一个,所以针对自己的需求可以自己写一个类似的功能,这里实现的方法比较多,其实原理也比较…...
javascript二维数组(7)数组指定元素求和
项目需求 对指定数据中的score求和 const data [ { name: Alice, age: 23, score: 85 }, { name: Bob, age: 30, score: 90 }, { name: Charlie, age: 35, score: 80 } ];1.封装函数 这个函数接受两个参数:一个对象数组和一个键名(也就是你想要…...
网络安全——黑客自学(笔记)
想自学网络安全(黑客技术)首先你得了解什么是网络安全!什么是黑客!!! 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队…...
Docker 安装 Elasticsearch7.16.x
docker hub地址:https://hub.docker.com 拉取镜像 docker pull elasticsearch:7.16.3创建容器 docker run -di --nameelasticsearch -p 9200:9200 -p 9300:9300 -p 5601:5601 -e "discovery.typesingle-node" -e "cluster.nameelasticsearch" -…...
springmvc-controller视图层配置SpringMVC处理请求的流程
目录 1. 什么是springmvc 2.项目中加入springmvc支持 2.1 导入依赖 2.2 springMVC配置文件 2.3 web.xml配置 2.4 中文编码处理 3. 编写一个简单的controller 4. 视图层配置 4.1 视图解析器配 4.2 静态资源配置 4.2 编写页面 4.3 页面跳转方式 5. SpringMVC处理请求…...
三模块七电平级联H桥整流器电压平衡控制策略Simulink仿真
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
【window10】Dart+Android Studio+Flutter安装及运行
安装Dart SDK安装Android Studio安装Flutter在Android Studio中创建并运行Flutter项目 安装前,请配置好你的jdk环境,准备好你的梯子~ 安装Dart SDK 浅浅了解一下Dart: Dart 诞生于2011年,是由谷歌开发的一种强类型、跨平台的客户…...
【C++】运算符重载 ⑩ ( 下标 [] 运算符重载 | 函数原型 int operator[](int i) | 完整代码示例 )
文章目录 一、下标 [] 运算符重载1、数组类回顾2、下标 [] 运算符重载 二、完整代码示例1、Array.h 数组头文件2、Array.cpp 数组实现类3、Test.cpp 测试类4、执行结果 一、下标 [] 运算符重载 1、数组类回顾 在之前的博客 【C】面向对象示例 - 数组类 ( 示例需求 | 创建封装类…...
ROS机械臂开发-开发环境搭建【一】
目录 前言环境配置docker搭建Ubuntu环境安装ROS 基础ROS文件系统 bugs 前言 想系统学习ROS,做一些机器人开发。因为有些基础了,这里随便写写记录一下。 环境配置 docker搭建Ubuntu环境 Dockerfile # 基础镜像 FROM ubuntu:18.04 # 设置变量 ENV ETC…...
深度思考rpc框架面经之五:rpc限流:rpc事务:tps测试
11 注册中心监控和rpc限流(用友云产品部二面) 11.1 你这个注册中心有实现相关的监控吗 11.1.1 如何实现注册中心有实现相关的监控 是的,我可以为你提供关于RPC注册中心及其监控的相关信息。RPC注册中心是用于管理微服务之间调用关系的中心…...
[AIGC] Java序列化利器 gson
前言: 本篇文章主要介绍了Gson库,从是什么、为什么、怎么用三个方面进行了详细的介绍。在是什么部分,介绍了Gson库的作用和特点;在为什么部分,介绍了为什么要使用Gson库;在怎么用部分,介绍了如何…...
VMware 虚拟机删除+重建
由于本人暴力地关闭虚拟机,导致虚拟机出现了一些问题,并且还没有给虚拟机拍快照,虽然还能用,但本人不想将就,于是乎打算重新新建一个虚拟机 一、删除 1.打开workstation,选择虚拟机,右键选择移除。 虽然虚…...
环面上 FHE 的快速自举:LUT/Automata Blind Rotate
参考文献: [AP14] Alperin-Sheriff J, Peikert C. Faster bootstrapping with polynomial error[C]//Advances in Cryptology–CRYPTO 2014: 34th Annual Cryptology Conference, Santa Barbara, CA, USA, August 17-21, 2014, Proceedings, Part I 34. Springer B…...
VScode配置文件launch.json 和 tasks.json配置项详细说明
tasks.json tasks.json为编译配置文件 {"version": "2.0.0", // tasks.json 文件的版本号"tasks": [ // 任务数组,包含一个编译任务配置对象{"type": "cppbuild", // 任务类型,这里是 cppbuild …...
DNSlog 注入简单笔记
无回显的盲注可以想办法回显到 dns 日志上: 1、打开 http://www.dnslog.cn 获取域名 2、注入: ?id1 and (select load_file(concat(//,(select database()),.3.mw0gxd.dnslog.cn/a)))-- 3、点击刷新得到回显:...
HDLbits: Dualedge
FPGA没有双边缘触发触发器,(posedge clk或negedge clk)会报错 “FPGA(以及其他任何地方)上的触发器是一个具有一个时钟且仅对该时钟的一个边缘敏感的器件。”参考verilog为什么不能双边沿触发 实现双边沿的两种方法 …...
wordpress 爱主题/千锋教育课程
手机自带的无线投屏总说找不到设备?“爱奇艺的视频可以投屏到电视。但手机自带的无线投屏总说找不到设备?”这是一位网友在知乎上的咨询。经过详细了解,它的电视是海信液晶电视,手机是魅族的;平时能够通过爱奇艺、腾讯…...
那个网站做足球测/建站模板网站
人生态度一般来说主要由()基本要素组成。A、人生认知B、人生情感C、人生意向D、人生目标职业目标包括总体目标和一个个阶段性目标,要善于把总体目标分解成一个个阶段性的目标,职务目标美好的人生价值目标靠()才能化为现实。A、客观条件B、主观条件C、社会…...
wordpress前端编辑器/网站制作推广
运行-〉regedit 打开下面两个位置,根据名称找出要删除的程序右键菜单。 1.[HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers]...所有文件的右键;2.[HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers]...文件夹的右键; 要是不放心可…...
三合一网站建设是指/seo排名优化怎么样
文源网络,仅供学习之用,如有侵权请联系删除。24点游戏对于任意给定的四张扑克牌,计算是否有赢得24点游戏的方法(即使用加、减、乘、除四则运算凑成24的方法);如果有的话,列出所有可能的方法。在大小王以外的52张牌中&a…...
来个黑黑的网站/关键字搜索引擎
前言本文讲解了从零开始学习Python数据科学的全过程,涵盖各种工具和方法你将会学习到如何使用python做基本的数据分析你还可以了解机器学习算法的原理和使用说明先说一段题外话。我是一名数据工程师,在用SAS做分析超过5年后,决定走出舒适区&a…...
中小型网站设计哪家好/官网seo优化找哪家做
347. 前 K 个高频元素 本题还是细节处理比较多,我不太熟悉小顶堆大顶堆,这个题花了一个多小时在搞基础,具体细节在代码部分。 class Solution {public int[] topKFrequent(int[] nums, int k) {Map<Integer,Integer> map…...