100 spring-security 中 /oauth/token 发送请求不携带参数 报错 “401 Unauthorized“

前言

最近存在这样的一个问题, 大致的复现方式是 访问 /oauth/token 接口, 然后不携带任何参数, 结果 服务器抛出了一个 "401 Unauthorized" 

针对这个 401, 这里 梳理一下这个流程, 也会衍生出一些其他的问题 

 

 

测试用例

客户端这边大致的情况是 构造参数, 然后发送请求, 上面两个 参数封装到查询字符串的请求是可以正常处理 

后面两个 未携带参数 的请求发送, 报错 "401 Unauthorized" 

我们这里主要关心 这里的未携带参数 的异常场景, 以及 造成这个问题的整个流程 

3. 具体的 ignore-urls 的业务层面的使用

主要是在 FilterSecurityInterceptor, 这里面我们核心需要关注几个部分

    3.1 this.obtainSecurityMetadataSource().getAttributes(object) 根据当前 request 获取认证属性

    3.2 Authentication authenticated = authenticateIfRequired(); 根据请求头里面拿到的 token, 访问 auth 服务, 获取用户以及关联信息, 封装 Authentication

    3.3 accessDecisionManager.decide 结合 认证属性 和 Authentication 来判断是否有权限

 

/*** Test11TestRestClient** @author Jerry.X.He* @version 1.0* @date 2022/5/19 17:44*/
public class Test11TestRestClient {/// Test11TestRestClientpublic static void main(String[] args) {String username = "xxx";String password = "xxx";String grantType = "password";Map<String, String> queryParams = new LinkedHashMap<>();queryParams.put("username", username);queryParams.put("password", password);queryParams.put("grant_type", grantType);queryParams.put("scope", "server");queryParams.put("client_id", "xxx");queryParams.put("client_secret", "xxx");RestTemplate restTemplate = new RestTemplate();String queryString = Tools.encapQueryString(queryParams);//        String loginUrl = "http://10.60.50.50:9999/auth/oauth/token?" + queryString;
//        Map result = restTemplate.postForObject(loginUrl, null, Map.class);
//        Map result = restTemplate.getForObject(loginUrl, Map.class);String loginUrl = "http://10.60.50.50:9999/auth/oauth/token";Map result = restTemplate.postForObject(loginUrl, null, Map.class);// todo, getForObject 的时候 查询字符串 未拼接 到 uri 中
//        Map result = restTemplate.getForObject(loginUrl, Map.class, queryParams);int x = 0;}}

 

 

问题的调试

来到认证被拒绝的地方, 可以看到的是 /oauth/token  请求是需要 fullyAuthenticated 的 

cffec01b9c7a41cca0c410587e6d16ca.png

 

 

fullyAuthenticated  的条件是 不是匿名用户 并且 xxx, 这里我们的 authentication 因为没有携带参数信息, 然后 匿名过滤器填充了一个 匿名的 authentication 

83da41b181384cc08113d50beeb5a87a.png

 

 

假设是在客户端正常携带了参数的场景下面, 是怎么过验证的呢? 

这里走的是 ClientCredentialsTokenEndpointFilter 走的一个 自定义的处理, 获取认证信息, 这里能够正常的拿到认证信息, 因此能够正常认证通过 

0c75731afd854139befc922551783293.png

 

 

我准备临时处理一下, 去掉 /oauth/token 的需要认证的处理, 于是在 WebSecurityConfigurer 中增加了 antMatchers("/oauth/token").permitAll() 但是, 从 this.obtainSecurityMetadataSource().getAttributes(object) 拿到的 attributes 还是 fullyAuthenticated, 而不是我这里配置的 permitAll 

我们这里会出现几点疑问 

  1. 为什么 /oauth/token 配置的是 fullyAuthenticated 
  2. 为什么 我配置了 antMatchers("/oauth/token").permitAll(), 但是没有生效 

 

 

为什么 /oauth/token 配置的是 fullyAuthenticated 

AuthorizationServerSecurityConfiguration 中 init 的时候初始化的时候配置 /oauth/token 为 fullyAuthenticated 

AuthorizationServerSecurityConfiguration  是继承自 SecurityConfigurer, 是 spring-security 中自带的 XXConfiguration, 业务上面的限定 一般还存在一个 WebSecurityConfigurer 

b296d8244a68429aaab8d4d73616d200.png

 

 

为什么 我配置了 antMatchers("/oauth/token").permitAll(), 但是没有生效 

这里 AuthorizationServerSecurityConfiguration 的限定的是 "/oauth/token", "/oauth/token_key", "/oauth/check_token" 
WebSecurityConfigurer 限定的是 其他的业务上的大部分的请求, 我这里添加了一个 /oauth/token -> permitAll 但是没有生效 

这里需要 回溯一下 FilterSecurityInterceptor 的这一系列的整个流程 

这里 正向剖析一下这个流程, 反向回溯的过程 着实开销不小 

 

WebSecurityConfiguration.springSecurityFilterChain 创建了一个 FilterChainProxy, 注册于 spring 容器中 

并且添加到了 tomcat 的 applicationFilterChain, 因此和具体的 http 请求处理 的 TokenEndpoint 处理的过程关联起来 

向 tomcat 的 applicationFilterChain 添加 filterDef 的地方来自于 SecurityFilterAutoConfiguration.securityFilterChainRegistration, 增加了一个 DelegatingFilterProxyRegistrationBean 

 

 

这里 FilterChainProxy 内部组合了三个 filterChain, 然后根据 request 获取实际处理的 filterChain, 构建 filter 责任链来处理 请求, 构建了一个 VirtualFilterChain, 在 tomcat 的 filter 责任链的基础上, 又嵌套了一层 filter责任链 

这里 我们的 /oauth/token, 匹配到了 第二个 filterChain, oauth/token 对应的策略为 fullyAuthenticated 

第一个 filterChain 对应的是 WebSecurityConfigurer 中 构建的一个 ignoring 的策略 

第二个 filterChain 对应的是 AuthorizationServerSecurityConfiguration 构建的一个 filterChain , 其中 /oauth/token 对应的策略为 fullyAuthenticated 

第三个 filterChain 对应的是 业务系统中自定义的 WebSecurityConfigurer 构建的一个 filterChain, 其中 /oauth/token 对应的策略为 permitAll 

构建这个 FilterChainProxy 的过程是在 WebSecurity. performBuild 的过程中, 先添加的 ignoring, 然后再添加的 各个 WebSecurityConfigurer 构建的 securityFilterChainBuilder 

55a57b15ef0044649d9feefb3b532e9b.png

 

 

梳理一下构建 FilterChainProxy 的整个流程 

  1. WebSecurityConfiguration.springSecurityFilterChain 中创建了 WebSecurity, WebSecurity.build 创建了 tomcat 这边接入的 FilterChainProxy 
  2. 这个 WebSecurity 组合了 applicationContext 中的 SecurityConfigurer, SecurityConfigurer 生成一个 HttpSecurity, HttpSecurity.build 会创建一个 DefaultFilterChain, 对应于 FilterChainProxy 组合的多个 DefaultFilterChain

 

  1. 在 WebWecurity init 的过程中, 各个 SecurityConfigurer 构建了相应的 HttpSecurity 然后注册到 WebWecurity 中, 这个过程会回调 SecurityConfigurer.configure(HttpSecurity http) 
  2. 在 WebWecurity configure 的过程中, 会回调各个 SecurityConfigurer 的 configure(WebSecurity web) 
  3. 在 WebWecurity performBuild 的过程中, 会构建 FilterChainProxy, 它组合了一系列的 DefaultFilterChain, 这个过程参见 WebWecurity.performBuild, 具体的方式是 组合 WebWecurity 下面的 HttpSecurity 来构建 DefaultFilterChain

            3.1 HttpSecurity performBuild 的过程中, 是直接根据已经采集的 filter 列表, 构建 DefaultFilterChain

            3.2 HttpSecurity  的已经采集的 filter 是来自于 HttpSecurity 下面的 configurers 在 init, configure 的过程中处理进去的 

            3.3 HttpSecurity 下面的各个 configurers 来自于 SecurityConfigurer 的 configure(HttpSecurity http) 的过程中的相关业务处理, 诸如 '.authorizeRequests().antMatchers("/token/**", "/actuator/**", "/sso/**", "/test/**", "/oauth/token").permitAll()', '.formLogin().loginPage("/token/login").loginProcessingUrl("/token/form")', ".authorizeRequests()" 的相关是配置的就是请求认证的相关配置 

 

 

以上一系列流程主要的目的是 为 FilterSecurityInterceptor 中的 this.obtainSecurityMetadataSource().getAttributes(object) 服务 

  1. 请求来了之后, 走 FilterChainProxy, FilterChainProxy  选择当前 request 需要使用的 DefaultFilterChain 
  2. DefaultFilterChain 中组合了一个责任链, 其中 FilterSecurityInterceptor 包含了如下认证步骤, 也是认证的相关问题核心需要关注的地方 

引用自 "20220321_01 配置了 security.oauth2.client.ignore-urls 但是访问配置的服务依然 "用户信息已过期或已更新,请重新登录" 问题"

3. 具体的 ignore-urls 的业务层面的使用

主要是在 FilterSecurityInterceptor, 这里面我们核心需要关注几个部分

    3.1 this.obtainSecurityMetadataSource().getAttributes(object) 根据当前 request 获取认证属性

    3.2 Authentication authenticated = authenticateIfRequired(); 根据请求头里面拿到的 token, 访问 auth 服务, 获取用户以及关联信息, 封装 Authentication

    3.3 accessDecisionManager.decide 结合 认证属性 和 Authentication 来判断是否有权限  

 

另外还有一个细节是 可以看到拿到的 filterChain, 相比于我们 SecurityConfigurer 的 configure(HttpSecurity http) 中配置的处理会多一些

这部分 filter 来自于 SecurityConfigurer.getHttp 的过程中初始化的一部分 Configurer, 诸如 AnonymousAuthenticationFilter, ExceptionTranslationFilter 等等 

 

 

getForObject 的时候 查询字符串 未拼接 到 uri 中

这个是 对于 getForObject 的 uriVariables 的理解存在问题 

这里的 uriVariables 对应于 uri 中的一部分模板, 而不是 我们常规理解的 get 请求中交互的参数 

e6c36ff20673464d8a4d0ed2d6fdff3f.png

 

 

完 

 

 

 

 

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

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

相关文章

HashMap 源码解读

文章目录 一、什么是HashMap HashMap 是一种快速的查找并且插入、删除性能都良好的一种 K/V键值对的数据结构&#xff0c;key唯一&#xff0c;value允许重复它基于哈希表的 Map 接口实现&#xff0c;是常用的 Java 集合之一&#xff0c;是非线程安全的。 二、HashMap的数据结…

rtt的io设备框架面向对象学习-触摸设备

目录 1.触摸设备基类2.触摸设备基类的子类3.初始化/构造流程3.1设备驱动层3.2 设备驱动框架层3.3 io设备管理层 4.总结5.使用5.1实例 1.触摸设备基类 此层处于设备驱动框架层。此层的类是抽象类。 在/ components / drivers / include / drivers /touch.h定义了如下touch设备…

C语言----冒泡排序进阶

冒泡排序大家应该到写过吧。但大家可能知道到的冒泡排序有两种方法。而我呢&#xff0c;最近学习到了另外一种方法&#xff0c;现在知道三种方法了。所以想与大家分享一下。但是缺点是第三种是第二种的自实现版。第一种就是我们平常写的普通冒泡排序。第二种就是qsort。第三种就…

Matlab梁单元有限元编程 | 铁木辛柯梁 | 欧拉梁 | Matlab源码 | 理论文本

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

如何恢复edge的自动翻译功能

介绍&#xff1a;对于英文不好的小伙伴&#xff0c;把英语翻译成中文是有帮助的&#xff0c;而edge可以直接对英文页面翻译这一功能更是受人喜爱&#xff0c;但是&#xff0c;最近发现这一项功能消失了。 原始界面&#xff1a; 下面展示如何恢复该功能。 1.打开edge&#xff…

day06-网路编程

#include <myhead.h>int do_add(sqlite3 *ppDb) {int numb;char name[20];int age;int salary;printf("请输入要插入的信息:");scanf("%d %s %d %d", &numb, name, &age, &salary);char sql[128] "";sprintf(sql, "INSE…

Flutter(四):SingleChildScrollView、GridView

SingleChildScrollView、GridView 遇到的问题 以下代码会报错: class GridViewPage extends StatefulWidget {const GridViewPage({super.key});overrideState<GridViewPage> createState() > _GridViewPage(); }class _GridViewPage extends State<GridViewPage&g…

vscode 通义灵码 插件自动写代码

安装插件 通义灵码安装教程-阿里云 点击立即安装 我是已经安装成功了&#xff0c;所以如下图&#xff0c;没安装的会显示安装&#xff0c;点击安装即可 安装成功之后 侧边栏会出现图标 登录 使用 在编辑框中输入 问题 &#xff0c;会自动生成代码和对应的说明

Cloud+Consul

Cloud整合Zookeeper代替Eureka-CSDN博客 Consul简介 Consul是一套开源的分布式服务发现和配置管理系统 What is Consul? | Consul | HashiCorp DeveloperConsul is a service networking solution that delivers service discovery, service mesh, and network security ca…

Redis中的RDB和AOF持久化机制(一)

Redis持久化 RDB快照(snapshot). 在默认情况下&#xff0c;Redis将内存数据库快照保存在名字为dump.rdb的二进制文件中.Redis可以进行设置,让它在"N秒内数据集至少有M个改动"这一条件被满足时&#xff0c;自动保存一次数据集。比如说&#xff0c;以下设置会让Redis…

机器视觉 /从bottle.hdev示例程序开启HalconHDevelop征程

文章目录 概述示例程序bottle.hdev源码Step 0: PreparationsStep 1: Segmentation - 读取并显示图片Step 1: Segmentation - 创建并设置OCR模型Step 1: Segmentation - 文本分割与识别计算结果显示内存释放 导出为C代码导出为C代码配置 VS Halcon 环境VS程序执行结果HTuple hv…

LeetCode刷题---填充每个节点的下一个右侧节点指针

官方题解:LeetCode官方题解 解题思想: 因为是一棵满二叉树&#xff0c;所以除了叶子节点外的其他节点都有两个子节点。 可以根据每一层来依次遍历 从根节点开始&#xff0c;根节点的左子节点的next节点就指向根节点的右子节点 因为根节点的next节点为NULL&#xff0c;开始从根…

DR模式下LVS负载均衡聚集部署实验

目录 1、实验准备 2、配置负载调度器&#xff08;ens33&#xff1a;192.168.80.9 VIP:192.168.80.188&#xff09; 2.1 配置虚拟ip地址&#xff08;VIP&#xff1a;192.168.80.188&#xff09; 2.2 调整proc响应参数 2.3 设置负载分配策略 3、部署共享存储&#xff08;NF…

static详解

前言 大家好我是jiantaoyab&#xff0c;这篇文章来谈一谈c中的static&#xff0c;根据对static的使用&#xff0c;我分为类内和类外2种情况 static简介 static是c常用的修饰符&#xff0c;它用来控制变量的存储方式和可见性&#xff0c;在变量前面加上一个static&#xff0c…

ECMAScript 语法

ECMAScript 语法 一、ECMAScript1.ECMAScript简介2.ECMAScript历史 二、ECMAScript 语法区分大小写变量是弱类型的每行结尾的分号可有可无注释与 Java、C 和 PHP 语言的注释相同括号表示代码块 一、ECMAScript ECMAScript是一种由Ecma国际&#xff08;前身为欧洲计算机制造商协…

大唐杯学习笔记:Day6

1.1小区选择 一、概述 1.UE在RRC_IDLE和RRC——INACTIVATE状态下进行的过程&#xff1b; 2.UE首先需要完成PLMN的选择,在已选择的PLMN上寻找合适的小区,获取合适的服务,监听控制信道,这个过程即小区选择过程&#xff1b; 3.根据小区重选准则,UE寻找其他更适合的小区进行小区…

论文《Exploring CLIP for Assessing the Look and Feel of Images》阅读

论文《Exploring CLIP for Assessing the Look and Feel of Images》阅读 论文概述Preliminary方法论Experiments结论 论文概述 今天带来的是论文《Exploring CLIP for Assessing the Look and Feel of Images》&#xff0c;论文主要通过 CLIP 模型来完成图像的质量&#xff0…

js五星评价的制作方法

方法有两种&#xff0c;1、jquer插件&#xff1b;2、图片循环&#xff1b; 第一种、效果图 代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"…

一文了解 ArrayList 的扩容机制

了解 ArrayList 在 Java 中常用集合类之间的关系如下图所示&#xff1a; 从图中可以看出 ArrayList 是实现了 List 接口&#xff0c;并是一个可扩容数组&#xff08;动态数组&#xff09;&#xff0c;它的内部是基于数组实现的。它的源码定义如下&#xff1a; public class A…

通过hyperbeam创建梁单元截面属性

1、为模型中标准的圆柱形创建梁单元和赋予属性&#xff1b; 2、为模型中不标准的对称性实体创建梁单元和赋予属性&#xff1b; 3、为模型中壳体部分创建梁单元和赋予属性&#xff1b;