目录

简单示例

WebSecurityConfig

requestMatchers

​​​​​​​requestMatchers

​​​​​​​antMatchers

​​​​​​​chainRequestMatchers

​​​​​​​setMatchers

​​​​​​​requestMatcher

​​​​​​​WebSecurity

​​​​​​​performBuild

securityFilterChainBuilder对象

​​​​​​​init

​​​​​​​addSecurityFilterChainBuilder

​​​​​​​securityFilterChainBuilder.build

​​​​​​​build

​​​​​​​doBuild

​​​​​​​httpSecurity.performBuild

​​​​​​​FilterChainProxy

​​​​​​​doFilterInternal

​​​​​​​getFilters

​​​​​​​matches

​​​​​​​OrRequestMatcher.matches

authorizeRequests

​​​​​​​authorizeRequests

​​​​​​​ExpressionUrlAuthorizationConfigurer

​​​​​​​AbstractInterceptUrlConfigurer

​​​​​​​FilterSecurityInterceptor

​​​​​​​invoke

​​​​​​​beforeInvocation

总结


       本篇文章我们来研究spring-security框架里http.requestMatchers和http.authorizeRequests的作用。只要使用了spring-security框架,那么,开发人员就免不了要对这两个配置项的配置了。

简单示例

       首先,新建一个config包用于存放spring-security通用配置;然后,新建一个WebSecurityConfig类,使其继承WebSecurityConfigurerAdapter。

       然后,给WebSecutiryConfig类中加上@EnableWebSecurity 注解后,这样便会自动被 Spring发现并注册。

​​​​​​​WebSecurityConfig

@EnableWebSecurity

class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Override

  protected void configure(HttpSecurity http) throws Exception {

    http.requestMatchers()

.antMatchers("web/oauth/authorize", "/oauth/**", "/admin/**")

      .and().authorizeRequests()

      .antMatchers("/admin/**").hasRole("ADMIN")

.anyRequest().authenticated()

      ... ...

}

       在这里,首先调用httpSecurity的requestMatchers()方法,紧接着调用antMatchers()方法,并输入web/oauth/authorize, /oauth/**, /admin/**三个通配符路径,表示当前配置只允许满足这三个路径的请求通过;

       然后,调用authorizeRequests()方法,再调用antMatchers()方法,并输入/admin/**通配符路径,紧接着调用hasRole()方法,并输入ADMIN参数,表示对于满足/admin/**路径的请求,需要登录用户有ADMIN的角色;

       最后,调用anyRequest()方法,紧接着调用authenticated()方法,表示/admin/**之外的所有请求,需要登录用户认证后才能访问。

requestMatchers

        在简单示例里,点击requestMatchers()方法,如下所示:

​​​​​​​requestMatchers

public RequestMatcherConfigurer requestMatchers() {

return requestMatcherConfigurer;

}

       在这里,返回配置器RequestMatcherConfigurer对象。 

       在简单示例里,点击requestMatchers()后面的antMatchers方法,如下所示:

​​​​​​​antMatchers

public abstract class AbstractRequestMatcherRegistry<C> {

... ...

public C antMatchers(String... antPatterns) {

  return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));

}

        点击chainRequestMatchers()方法,如下所示:

​​​​​​​chainRequestMatchers

@Override

protected RequestMatcherConfigurer chainRequestMatchers(List<RequestMatcher> requestMatchers) {

setMatchers(requestMatchers);

return this;

}

        在这里,点击setMatchers()方法,如下所示:

​​​​​​​setMatchers

private void setMatchers(List<? extends RequestMatcher> requestMatchers) {

this.matchers.addAll(requestMatchers);

requestMatcher(new OrRequestMatcher(this.matchers));

}

       在这里,将配置的请求地址转换为OrRequestMatcher对象。然后调用requestMatcher()方法进行后续的处理。

       点击requestMatcher()方法,如下所示:

​​​​​​​requestMatcher

public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity> implements SecurityBuilder<DefaultSecurityFilterChain>,

HttpSecurityBuilder<HttpSecurity> {

    private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;

... ...

public HttpSecurity requestMatcher(RequestMatcher requestMatcher) {

  this.requestMatcher = requestMatcher;

  return this;

}

       在这里,将配置的请求地址OrRequestMatcher对象保存到HttpSecurity对象的requestMatcher 属性对象里。

       从前面的文章里,我们知道spring-security框架最终会创建一个FilterChainProxy类的过滤器对象,接下来我们探究一下该FilterChainProxy过滤器如何引用HttpSecurity对象的requestMatcher 属性对象。

       从前面的文章里,我们也了解到过滤器FilterChainProxy对象,是在WebSecurity类里创建的。

​​​​​​​WebSecurity

       点击performBuild()方法,如下所示:

​​​​​​​performBuild

protected Filter performBuild() throws Exception {

    int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();

    List<SecurityFilterChain> securityFilterChains = new ArrayList(chainSize);

    ... ...

    var3 = this.securityFilterChainBuilders.iterator();

    while(var3.hasNext()) {

        SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder = (SecurityBuilder)var3.next();

        securityFilterChains.add(securityFilterChainBuilder.build());

    }

    FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);

    if (this.httpFirewall != null) {

        filterChainProxy.setFirewall(this.httpFirewall);

    }

    filterChainProxy.afterPropertiesSet();

    Filter result = filterChainProxy;

    this.postBuildAction.run();

    return (Filter)result;

}

       在这里,FilterChainProxy实例的构造参数是securityFilterChainBuilder.build()。

       接下来我们探究两个问题:

  1. securityFilterChainBuilder对象来自哪里?
  2. securityFilterChainBuilder.build()方法跟HttpSecurity对象的requestMatcher 属性对象有什么关系?
  3. 过滤器FilterChainProxy对象如何使用requestMatcher 属性?

securityFilterChainBuilder对象

       回顾前面的文章,我们知道在WebSecurityConfigurerAdapter 类里会创建HttpSecurity对象,找到WebSecurityConfigurerAdapter 类的init()方法,如下所示:

​​​​​​​init

public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {

    ... ...

public void init(final WebSecurity web) throws Exception {

final HttpSecurity http = getHttp();

web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {

FilterSecurityInterceptor securityInterceptor = http

.getSharedObject(FilterSecurityInterceptor.class);

web.securityInterceptor(securityInterceptor);

});

}

        在这里,通过getHttp()方法创建了HttpSecurity对象,然后调用web.addSecurityFilterChainBuilder(http)方法,如下所示:

​​​​​​​addSecurityFilterChainBuilder

public WebSecurity addSecurityFilterChainBuilder(SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {

this.securityFilterChainBuilders.add(securityFilterChainBuilder);

return this;

}

       在这里,将HttpSecurity对象加入到WebSecurity 对象的属性securityFilterChainBuilders里。

       接下来, 我们探究securityFilterChainBuilder.build()方法跟HttpSecurity对象的requestMatcher 属性对象有什么关系?

​​​​​​​securityFilterChainBuilder.build

       在WebSecurity类的performBuild()方法,点击securityFilterChainBuilder.build(),如下所示:

​​​​​​​build

public final O build() throws Exception {

if (this.building.compareAndSet(false, true)) {

this.object = doBuild();

return this.object;

}

throw new AlreadyBuiltException("This object has already been built");

}

        点击doBuild()方法,如下所示:

​​​​​​​doBuild

@Override

protected final O doBuild() throws Exception {

synchronized (configurers) {

buildState = BuildState.INITIALIZING;

beforeInit();

init();

buildState = BuildState.CONFIGURING;

beforeConfigure();

configure();

buildState = BuildState.BUILDING;

O result = performBuild();

buildState = BuildState.BUILT;

return result;

}

}

        点击performBuild()方法,如下所示:

​​​​​​​httpSecurity.performBuild

@Override

protected DefaultSecurityFilterChain performBuild() {

filters.sort(comparator);

return new DefaultSecurityFilterChain(requestMatcher, filters);

}

       在这里,会创建DefaultSecurityFilterChain对象,该对象的第一个构造参数就是httpSecurity对象的属性requestMatcher对象了。

       接下来,我们探究过滤器FilterChainProxy对象如何使用requestMatcher属性?

​​​​​​​FilterChainProxy

       点击FilterChainProxy 类的doFilter()方法,如下所示:

public class FilterChainProxy extends GenericFilterBean {

    ... ...

@Override

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;

if (clearContext) {

try {

request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

doFilterInternal(request, response, chain);

} finally {

SecurityContextHolder.clearContext();

request.removeAttribute(FILTER_APPLIED);

}

} else {

doFilterInternal(request, response, chain);

}

}

        点击doFilterInternal()方法,如下所示:

​​​​​​​doFilterInternal

private void doFilterInternal(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

FirewalledRequest fwRequest = firewall

.getFirewalledRequest((HttpServletRequest) request);

HttpServletResponse fwResponse = firewall

.getFirewalledResponse((HttpServletResponse) response);

List<Filter> filters = getFilters(fwRequest);

if (filters == null || filters.size() == 0) {

fwRequest.reset();

chain.doFilter(fwRequest, fwResponse);

return;

}

VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);

vfc.doFilter(fwRequest, fwResponse);

}

        点击getFilters()方法,如下所示:

​​​​​​​getFilters

private List<Filter> getFilters(HttpServletRequest request) {

for (SecurityFilterChain chain : filterChains) {

if (chain.matches(request)) {

return chain.getFilters();

}

}

return null;

}

        点击chain.matches()方法,如下所示:

​​​​​​​matches

public boolean matches(HttpServletRequest request) {

return requestMatcher.matches(request);

}

        点击requestMatcher.matches()方法,如下所示:

​​​​​​​OrRequestMatcher.matches

public boolean matches(HttpServletRequest request) {

for (RequestMatcher matcher : requestMatchers) {

if (logger.isDebugEnabled()) {

logger.debug("Trying to match using " + matcher);

}

if (matcher.matches(request)) {

logger.debug("matched");

return true;

}

}

logger.debug("No matches found");

return false;

}

        至此,我们总算把spring-security框架的http.requestMatchers研究透彻了。

        接下来,我们探究spring-security框架的http.authorizeRequests的作用。

authorizeRequests

       在简单示例里,点击authorizeRequests()方法,如下所示:

​​​​​​​authorizeRequests

public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests() throws Exception {

ApplicationContext context = getContext();

return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context)).getRegistry();

}

        在这里,使用的是配置器ExpressionUrlAuthorizationConfigurer对象。点击ExpressionUrlAuthorizationConfigurer类,如下所示:

​​​​​​​ExpressionUrlAuthorizationConfigurer

public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>

extends AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {

... ...

private final ExpressionInterceptUrlRegistry REGISTRY;

    ... ...

        在这里,找不到configure(H http)方法,点击继承类AbstractInterceptUrlConfigurer,如下所示:

​​​​​​​AbstractInterceptUrlConfigurer

abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>>

extends AbstractHttpConfigurer<C, H> {

private Boolean filterSecurityInterceptorOncePerRequest;

private AccessDecisionManager accessDecisionManager;

@Override

public void configure(H http) throws Exception {

FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);

if (metadataSource == null) {

return;

}

FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(

http, metadataSource, http.getSharedObject(AuthenticationManager.class));

if (filterSecurityInterceptorOncePerRequest != null) {

securityInterceptor

.setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);

}

securityInterceptor = postProcess(securityInterceptor);

http.addFilter(securityInterceptor);

http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);

}

        在这里,创建了过滤器FilterSecurityInterceptor 对象。

       点击FilterSecurityInterceptor 类,如下所示:

​​​​​​​FilterSecurityInterceptor

public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

FilterInvocation fi = new FilterInvocation(request, response, chain);

invoke(fi);

}

        点击invoke()方法,如下所示:

​​​​​​​invoke

public void invoke(FilterInvocation fi) throws IOException, ServletException {

if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)

&& observeOncePerRequest) {

  ... ...

} else {

if (fi.getRequest() != null && observeOncePerRequest) {

fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);

}

InterceptorStatusToken token = super.beforeInvocation(fi);

try {

fi.getChain().doFilter(fi.getRequest(), fi.getResponse());

} finally {

super.finallyInvocation(token);

}

super.afterInvocation(token, null);

}

}

        点击beforeInvocation()方法,如下所示:

​​​​​​​beforeInvocation

protected InterceptorStatusToken beforeInvocation(Object object) {

  boolean debug = this.logger.isDebugEnabled();

  if (!this.getSecureObjectClass().isAssignableFrom(object.getClass())) {

      ... ...

  } else {

      Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);

      if (attributes != null && !attributes.isEmpty()) {

          ... ...

          Authentication authenticated = this.authenticateIfRequired();

          try {

              this.accessDecisionManager.decide(authenticated, object, attributes);

          } catch (AccessDeniedException var7) {

              ... ...

          }

          ... ...

  }

}

       在这里,通过this.obtainSecurityMetadataSource().getAttributes(object)获取配置的权限信息,通过this.accessDecisionManager.decide进行授权决策。

总结

       http.requestMatchers()是一个请求过滤器,决定是否要应用安全配置。对于不需要进行安全控制的URL,可以不要配置在requestMatchers里,这样可以避免走到下一个环节。

       http.authorizeRequests()是一个授权配置器,决定如何应用安全配置。用于配置 URL 的访问权限控制规则。

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

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

相关文章

Bessel位势方程求解步骤

问题 考虑偏微分方程&#xff08;PDE&#xff09;&#xff1a; − Δ u u f , x ∈ R n , -\Delta u u f, \quad x \in \mathbb{R}^n, −Δuuf,x∈Rn, 其中 f ∈ L 2 ( R n ) f \in L^2(\mathbb{R}^n) f∈L2(Rn)。这是一个线性椭圆型方程&#xff0c;称为 Bessel 位势方…

if __name__ == ‘__main__‘:

基本概念 if __name__ __main__: 是一个条件判断语句&#xff0c;用于确定当前模块是作为主程序运行&#xff0c;还是被其他模块导入。 __name__ 变量 __name__ 是Python的一个内置变量&#xff0c;表示当前模块的名称当一个模块被直接运行时&#xff0c;__name__ 的值会被…

浅谈Apache HttpClient的相关配置和使用

Apache HttpClient是由Apache软件基金会维护的一款开源HTTP客户端库&#xff0c;对比最基础的 HttpURLConnection 而言,它的优势时支持连接池管理&#xff0c;拦截器&#xff08;Interceptor&#xff09;机制&#xff0c;同步/异步请求支持等能力。 在使用这个组件时&#xff…

【Teensy】在ArduinoIDE中配置Teensy4.1

1.文件——首选项 在其他开发板管理器地址这里添加&#xff1a; https://www.pjrc.com/teensy/package_teensy_index.json 点击确定&#xff01; 2.安装Teensy(for Arduino IDE…) 按照图中1&#xff0c;2&#xff0c;3操作&#xff01;可以选择上一个版本&#xff08;不使用最…

企业自建云概念解读|私有云、专有云、混合云、分布式云、企业云

随着云计算技术逐渐成熟&#xff0c;越来越多的企业开始在本地数据中心自行搭建云平台&#xff0c;满足数据合规、业务性能与连续性、节约成本等多方面的需求。不过&#xff0c;面对多种多样的自建云产品&#xff0c;不少用户会有类似的疑问&#xff1a;自建云等于私有云吗&…

反弹 Shell 升级为全交互终端的两种高效方法

目录 🚀 升级反弹 Shell 为全交互终端:两种高效方法 🛠️ 方法 1:利用 Python pty.spawn 创建伪终端 📋 操作步骤

Hyper-YOLO: When Visual Object Detection Meets Hypergraph Computation论文精读(逐段解析)

Hyper-YOLO: When Visual Object Detection Meets Hypergraph Computation论文精读&#xff08;逐段解析&#xff09; 论文地址&#xff1a;https://arxiv.org/abs/2408.04804 CVPR 2024 Yifan Feng, Jiangang Huang, Shaoyi Du, Senior Member, IEEE, Shihui Ying, Jun-Hai Y…

Windows 下配置多个 GitHub 账号的 SSH Key

Windows 下配置多个 GitHub 账号的 SSH Key 假设你有以下两个 SSH key 文件&#xff1a; 第一个账号&#xff1a;id_rsa&#xff08;默认&#xff09;第二个账号&#xff1a;id_rsa_github ✅ 步骤&#xff1a;在 Windows 上配置多个 GitHub 账号 SSH Key 1️⃣ 打开 SSH 配…

技术选型:时序数据库(三)

IoTDB vs InfluxDB vs TDengine 时序数据库横评对比。 从 架构设计、性能、功能、生态、适用场景 等维度&#xff0c;对三款时序数据库进行深度对比&#xff0c;助您精准选型。 一、核心架构对比 数据库 存储模型 数据模型 扩展性 Apache IoTDB 分层存储&#xff08;TsFi…

电子电路原理第十九章(非线性运算放大器电路的应用)

单片集成运算放大器价格便宜、用途广泛且性能可靠。它们不仅可以用于线性电路,如电压放大器、电流源和有源滤波器,而且可以用于非线性电路,如比较器、波形生成器和有源二极管电路。非线性运放电路的输出通常与输入信号的波形不同,这是因为运放在输入周期的某个时间段内达到…

FPGA实现CameraLink视频解码转SDI输出,基于LVDS+GTX架构,提供2套工程源码和技术支持

目录 1、前言工程概述免责声明 2、CameraLink协议理论学习3、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目FPGA实现CameraLink视频编解码方案本博已有的 SDI 编解码方案 4、工程详细设计方案工程设计原理框图输入CameraLink相机LVDS视频解码模块LV…

户外人像要怎么拍 ?

前言&#xff1a; ” 接上篇&#xff0c;培养你的眼力 - 摄影构图&#xff0c;本文是整理自《美国纽约摄影学院 摄影教材》&#xff0c;第三单元 - 第9课 - 自然光&#xff0c;课后习题及解答。“ 1. 正面光产生无深浅反差的平面感觉。 理解这题&#xff0c;首先得明白什么是…

华为云Flexus+DeepSeek征文 | 华为云 ModelArts Studio 赋能高情商AI聊天助手:用技术构建有温度的智能对话体验

前言 华为云 ModelArts Studio 是基于 ModelArts 构建的一站式大模型即服务平台&#xff08;MaaS&#xff09;&#xff0c;可通过与开源 Agent 框架 Dify.AI 结合来开发对接 AI 聊天助手。 在打造 “高情商” 特性的过程中&#xff0c;华为云ModelArts Studio 的自定义提示词…

Spring Boot属性配置方式

一、Spring Boot属性配置方式。 在编写完成后端程序之前&#xff0c;可以通过yml配置文件键值对的方式修改配置环境&#xff0c;一旦打包完成&#xff0c;再次修改yml配置文件较为麻烦&#xff0c;此时&#xff0c;可以使用以下配置方式&#xff1a; 1.命令行参数方式 …

Webpack原理剖析与实现

1. 整体架构设计 Webpack 5 的整体架构设计包括以下几个核心模块: Compiler:负责整个编译过程,从读取配置、解析模块、生成依赖图,到输出最终的打包结果,主要文件是 lib/Compiler.js 。 Compilation:代表一次编译过程,包括所有模块、依赖关系和编译结果,主要文件是 li…

【Python使用】嘿马python运维开发全体系教程第2篇:日志管理,Linux概述【附代码文档】

教程总体简介&#xff1a;网络设定 学习目标 1、手动设定 2、DHCP自动获取 系统基本优化 一、永久关闭SELinux 1. 永久关闭 二、关闭防火墙 2. 临时启动关闭防火墙 三、设定运行级别为3&#xff08;命令行模式&#xff09; 四、修改ssh端口号 ssh服务 一、ssh介绍 二、客户端远…

Hibernate报No Dialect mapping for JDBC type 1111(APP)

文章目录 环境症状问题原因解决方案报错编码 环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 症状 客户应用中报错No Dialect mapping for JDBC type 1111。 问题原因 客户使用Hibernate&#xff0c;实体类的中设置的数据类型与数…

【数据分析】环境数据降维与聚类分析教程:从PCA到可视化

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍教程内容数据预处理主成分分析(PCA)聚类分析可视化分析结果提取簇特征教程目的加载R包数据下载导入数据数据预处理主成分分析(PCA)计算相关矩阵绘制相关矩阵热图执行PCA可视化…

mac 安装python,切换python版本

一、安装多版本的PYTHON 在macOS上&#xff0c;你可以通过Homebrew包管理器安装多个版本的Python 安装Homebrew 首先&#xff0c;如果你的macOS上没有安装Homebrew&#xff0c;需要先进行安装。打开终端&#xff08;Terminal&#xff09;并输入以下命令&#xff1a; /bin/b…

AMD图形和计算架构:RNDA

AMD图形和计算架构&#xff1a;RNDA AMD RDNA 是 AMD 为显卡&#xff08;GPU&#xff09;设计的 图形和计算架构&#xff0c;专为高性能游戏、实时渲染和并行计算优化。目前已经迭代到 RDNA 3&#xff08;如 RX 7000 系列显卡&#xff09;&#xff0c;与 NVIDIA 的 RTX 系列和…