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 {

    /// Test11TestRestClient
    public 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

 

 

完 

 

 

 

 

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

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

相关文章

Linux 之三:CentOS7 目录结构 和 日期及时区设置

Linux 目录 以下是对这些目录的解释&#xff1a; /bin&#xff1a;bin是Binary的缩写, 这个目录存放着最经常使用的命令。/boot&#xff1a; 这里存放的是启动Linux时使用的一些核心文件&#xff0c;包括一些连接文件以及镜像文件。/dev &#xff1a; dev是Device(设备)的缩写…

[Java安全入门]二.序列化与反序列化

一.概念 Serialization&#xff08;序列化&#xff09;是一种将对象以一连串的字节描述的过程&#xff1b;反序列化deserialization是一种将这些字节重建成一个对象的过程。将程序中的对象&#xff0c;放入文件中保存就是序列化&#xff0c;将文件中的字节码重新转成对象就是反…

【广度优先搜索】【堆】【C++算法】407. 接雨水 II

作者推荐 【二分查找】【C算法】378. 有序矩阵中第 K 小的元素 本文涉及知识点 广度优先搜索 堆 LeetCoce407. 接雨水 II 给你一个 m x n 的矩阵&#xff0c;其中的值均为非负整数&#xff0c;代表二维高度图每个单元的高度&#xff0c;请计算图中形状最多能接多少体积的雨…

【C语言】还有柔性数组?

前言 也许你从来没有听说过柔性数组&#xff08;flexible array&#xff09;这个概念&#xff0c;但是它确实是存在的。C99中&#xff0c;结构中的最后⼀个元素允许是未知⼤⼩的数组&#xff0c;这就叫做『柔性数组』成员。 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xf…

二级水平导航菜单栏的实现

1. 这个是本人设计的一带一路的二级水平导航栏HTML代码&#xff1b; 这里最后实现的效果是鼠标悬停在导航栏上面&#xff0c;就会显示下面的4个部分页面&#xff0c;这里只是以评论热 点作为例子&#xff0c;其他的类似&#xff1b; 2.首先要设计DIV&#xff0c;然后利用无…

GO语言环境安装---VScode.2024

目录 一、下载并安装GO 二、配置环境变量 三、VScode环境安装 由于工作原因&#xff0c;需要用到go来写web后端&#xff0c;正好从零记录下环境安装 一、下载并安装GO 首先在官网根据PC系统选择对应的包下载 源地址&#xff1a;https://go.dev/dl/ 打不开的用这个也行&a…

论文阅读:Dataset Quantization

摘要 最先进的深度神经网络使用大量&#xff08;百万甚至数十亿&#xff09;数据进行训练。昂贵的计算和内存成本使得在有限的硬件资源上训练它们变得困难&#xff0c;特别是对于最近流行的大型语言模型 (LLM) 和计算机视觉模型 (CV)。因此最近流行的数据集蒸馏方法得到发展&a…

第三天 Kubernetes进阶实践

第三天 Kubernetes进阶实践 本章介绍Kubernetes的进阶内容&#xff0c;包含Kubernetes集群调度、CNI插件、认证授权安全体系、分布式存储的对接、Helm的使用等&#xff0c;让学员可以更加深入的学习Kubernetes的核心内容。 ETCD数据的访问 kube-scheduler调度策略实践 预选与…

Python内置模块

目录 什么是模块 模块分类 通过模块创建者分类 系统内置模块 第三方模块 在线安装 离线安装 模块导入 math和random模块介绍 math模块 random模块 什么是模块 在我们编写程序时&#xff0c;需要导入包。例如随机数的产生&#xff0c;需要import random。import XXX&…

【MATLAB】兔子机器人总系统_动力学模型解读(及simulink中的simscape的各模块介绍)

1、动力学模型 Rectangular Joint 控制平面上&#xff08;x&#xff0c;y轴&#xff09;的移动&#xff0c;去掉以后&#xff0c;机器人在原地翻滚不移动 Rigid Transform 坐标转换&#xff0c;B站视频已收藏 去掉&#xff0c;机体与地面贴合 此处的作用是设定机体的初…

【conda】实现conda环境迁移的4种方式

文章目录 方案1: 使用conda pack制作压缩包并在目标环境解压使用方案2: 使用package列表文件重新创建conda环境方案3: scp将环境文件夹拷贝到目标主机上方案4: 通过--clone先克隆一个环境再conda pack打包迁移 方案1: 使用conda pack制作压缩包并在目标环境解压使用 适合离线环…

不买后悔的阿里云服务器租用价格表_优惠活动整理_2024新版

2024阿里云服务器优惠活动政策整理&#xff0c;阿里云99计划ECS云服务器2核2G3M带宽99元一年、2核4G5M优惠价格199元一年&#xff0c;轻量应用服务器2核2G3M服务器61元一年、2核4G4M带宽165元1年&#xff0c;云服务器4核16G10M带宽26元1个月、149元半年&#xff0c;云服务器8核…

Python基于opencv的人脸识别上课签到考勤系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

JS 实现AES方式加密数据实现示例

简介&#xff1a;全称高级加密标准&#xff08;英文名称&#xff1a;Advanced Encryption Standard&#xff09;&#xff0c;在密码学中又称 Rijndael 加密法&#xff0c;由美国国家标准与技术研究院 &#xff08;NIST&#xff09;于 2001 年发布&#xff0c;并在 2002 年成为有…

最近开发中遇到的一些问题

puppeteer下载失败问题 使用的淘宝镜像&#xff0c;但执行命令npm i puppeteer之后&#xff0c;报错&#xff1a; npm ERR! code 1 npm ERR! path E:\项目-临时\test_install_puppeteer\node_modules\puppeteer npm ERR! command failed npm ERR! command C:\WINDOWS\system3…

00X集——CAD vba 填充(hatch)及挖空

首先&#xff0c;画个椭圆&#xff0c;并填充&#xff0c;直接上代码&#xff1a; Sub 画椭圆填充() 2024年3月6日21:10:22 by qq443440204 Dim hat As AcadHatch 填充 Dim ell(0) As AcadEllipse 椭圆 Dim cent(0 To 2) As Double 椭圆中心点 Dim dd(0 To 2) As Double 椭圆长…

【爬虫】单首音乐的爬取(附源码)

以某狗音乐为例 import requests import re import time import hashlibdef GetResponse(url):# 模拟浏览器headers {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0}# 发送请求…

python+django+vue房屋租赁系统 8gwmf

房屋租赁系统在设计与实施时&#xff0c;采取了模块性的设计理念&#xff0c;把相似的系统的功能整合到一个模组中&#xff0c;以增强内部的功能&#xff0c;减少各组件之间的联系&#xff0c;从而达到减少相互影响的目的。如房源信息、预约信息、求租信息模块等[12]。 管理员后…

常见排序算法解析

芝兰生于深林&#xff0c;不以无人而不芳&#xff1b;君子修道立德&#xff0c;不为穷困而改节 文章目录 插入排序直接插入排序希尔排序 选择排序直接选择排序堆排序 交换排序冒泡排序快速排序优化挖坑法前后指针法非递归版 归并排序递归非递归 总结 插入排序 插入排序&#…

STM32控制气泵和电磁阀实现

一、功能简介 使用STM32控制气泵和电磁阀的开和关&#xff0c;气泵和电磁阀的供电电压为12V。 二、实现过程 1、气泵和电磁阀的开和关均为开关量&#xff0c;实现控制方法有多种&#xff0c;比如继电器&#xff0c;但是继电器动作有噪声且体积较大&#xff0c;更好的方法为使…