Spring Security OAuth2授权原理、流程与源码解读

news2024/11/18 21:30:15

文章目录

  • 前言
  • AuthorizationServerConfigurerAdapter(身份认证服务配置适配器)
    • OAuth2AuthorizationServerConfiguration(OAuth2授权服务配置)
  • EnableAuthorizationServer(开启身份认证服务)
    • AuthorizationServerEndpointsConfigurations身份认证服务站点配置类
      • AuthorizationEndpoint (授权码类型端点)
      • TokenEndpoint (Token端点)
      • CheckTokenEndpoint (检查Token端点)
  • AuthorizationServerSecurityConfigurer(授权服务安全配置)
    • ClientCredentialsTokenEndpointFilter (客户端认证Token端点过滤器)
  • ClientDetailsServiceConfigurer(客户端详情服务配置)
  • AuthorizationServerEndpointsConfigurer(授权服务端点配置)
  • TokenGranter(Token授权)
    • CompositeTokenGranter(综合Token授权)
    • AbstractTokenGranter(抽象Token授权)
      • RefreshTokenGranter(刷新Token授权)
      • AuthorizationCodeTokenGranter(授权码Token授权)
      • ResourceOwnerPasswordTokenGranter(密码Token授权)
      • ClientCredentialsTokenGranter(客户端凭证Token授权)
      • ImplicitTokenGranter(隐式Token授权)


前言

Spring Security OAuth2,认证原理与流程。

  1. 客户端认证由ClientCredentialsTokenEndpointFilter 完成客户端身份认证
  2. 用户授权由:TokenEndpointAuthorizationEndpoint 完成。
  3. Token创建、刷新、移除等。

执行流程如下图:
在这里插入图片描述

AuthorizationServerConfigurerAdapter(身份认证服务配置适配器)

授权服务安全配置

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
}

客户端详情配置

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
}

授权服务端点配置

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}

OAuth2AuthorizationServerConfiguration(OAuth2授权服务配置)

默认OAuth2授权服务配置,可以作为参考

EnableAuthorizationServer(开启身份认证服务)

在这个注解中引入了两个配置类AuthorizationServerEndpointsConfiguration和AuthorizationServerSecurityConfiguration


@Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class})

AuthorizationServerEndpointsConfigurations身份认证服务站点配置类

AuthorizationEndpoint (授权码类型端点)

@Bean
public AuthorizationEndpoint authorizationEndpoint() throws Exception {
	AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();
	FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
	//用户审批页面
	authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
	//异常处理程序提供者
	authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator());
	//异常页面
	authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
	//token授权
	authorizationEndpoint.setTokenGranter(tokenGranter());
	//配置客户端详情
	authorizationEndpoint.setClientDetailsService(clientDetailsService);
	//身份认证授权码服务
	authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices());
	//OAuth2请求工厂
	authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
	//OAuth2请求校验
	authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
	//用户审批处理程序
	authorizationEndpoint.setUserApprovalHandler(userApprovalHandler());
	//重定向解析器
	authorizationEndpoint.setRedirectResolver(redirectResolver());
	return authorizationEndpoint;
}

TokenEndpoint (Token端点)

@Bean
public TokenEndpoint tokenEndpoint() throws Exception {
	TokenEndpoint tokenEndpoint = new TokenEndpoint();
	//配置客户端详情
	tokenEndpoint.setClientDetailsService(clientDetailsService);
	//异常处理Handler
	tokenEndpoint.setProviderExceptionHandler(exceptionTranslator());
	//token授权
	tokenEndpoint.setTokenGranter(tokenGranter());
	//OAuth2请求工厂
	tokenEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
	//OAuth2请求校验
	tokenEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
	//设置允许请求方法
	tokenEndpoint.setAllowedRequestMethods(allowedTokenEndpointRequestMethods());
	return tokenEndpoint;
}
  1. ClientCredentialsTokenEndpointFilter 完成客户端身份认证
  2. 从ClientDetailsService中获取客户端详情信息
  3. 通过OAuth2RequestFactory将请求参数和客户端详情转为TokenRequest
  4. 如果client不为空,且判断clientId和获取TokenRequest的clientId是否相等
  5. 获取的客户端详情信息通过OAuth2RequestValidator校验请求域
  6. 不支持implicit授权模式
  7. 判断是否授权码类型请求,是需要设置对应的请求域
  8. 判断是否刷新Token类型请求,是需要设置对应的请求域
  9. 通过TokenGranter完成授权,返回OAuth2AccessToken
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
	//由ClientCredentialsTokenEndpointFilter 完成客户端身份认证
	if (!(principal instanceof Authentication)) {
		throw new InsufficientAuthenticationException(
					"There is no client authentication. Try adding an appropriate authentication filter.");
	}
	//从ClientDetailsService中根据clientId获取客户端详情信息
	String clientId = getClientId(principal);
	ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);

    //通过OAuth2RequestFactory将请求参数和客户端详情转为TokenRequest 
	TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
	//如果client不为空,且判断clientId和获取客户端详情的clientId是否相等
	if (clientId != null && !clientId.equals("")) {
		// Only validate the client details if a client authenticated during this
		// request.
		if (!clientId.equals(tokenRequest.getClientId())) {
			// double check to make sure that the client ID in the token request is the same as that in the
			// authenticated client
			throw new InvalidClientException("Given client ID does not match authenticated client");
		}
	}
	//获取的客户端详情信息通过OAuth2RequestValidator校验请求域
	if (authenticatedClient != null) {
		oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
	}
	if (!StringUtils.hasText(tokenRequest.getGrantType())) {
		throw new InvalidRequestException("Missing grant type");
	}
	// 不支持implicit授权模式
	if (tokenRequest.getGrantType().equals("implicit")) {
		throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
	}
	//是否授权码类型请求
	if (isAuthCodeRequest(parameters)) {
		// The scope was requested or determined during the authorization step
		if (!tokenRequest.getScope().isEmpty()) {
			logger.debug("Clearing scope of incoming token request");
			tokenRequest.setScope(Collections.<String> emptySet());
		}
	}
	//是否刷新Token类型请求
	if (isRefreshTokenRequest(parameters)) {
		// A refresh token has its own default scopes, so we should ignore any added by the factory here.
		tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
	}
	//通过Token授权,返回OAuth2访问Token
	OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
	if (token == null) {
		throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
	}

	return getResponse(token);

}

CheckTokenEndpoint (检查Token端点)

@Bean
public CheckTokenEndpoint checkTokenEndpoint() {
	CheckTokenEndpoint endpoint = new CheckTokenEndpoint(getEndpointsConfigurer().getResourceServerTokenServices());
	endpoint.setAccessTokenConverter(getEndpointsConfigurer().getAccessTokenConverter());
	endpoint.setExceptionTranslator(exceptionTranslator());
	return endpoint;
}

AuthorizationServerSecurityConfigurer(授权服务安全配置)

主要完成功能是安全配置
主要功能:ClientDetailsService转换为UserDetailsService并注入到AuthenticationManager

public void init(HttpSecurity http) throws Exception {

	registerDefaultAuthenticationEntryPoint(http);
	//将ClientDetailsService转换为UserDetailsService并注入到AuthenticationManager
	if (passwordEncoder != null) {
		ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(clientDetailsService());
		clientDetailsUserDetailsService.setPasswordEncoder(passwordEncoder());
		http.getSharedObject(AuthenticationManagerBuilder.class)
					.userDetailsService(clientDetailsUserDetailsService)
					.passwordEncoder(passwordEncoder());
	}
	else {
		http.userDetailsService(new ClientDetailsUserDetailsService(clientDetailsService()));
	}
	http.securityContext().securityContextRepository(new NullSecurityContextRepository()).and().csrf().disable()
				.httpBasic().realmName(realm);
	if (sslOnly) {
			http.requiresChannel().anyRequest().requiresSecure();
	}
}

在配置类中完成ClientCredentialsTokenEndpointFilter 加入到FilterChainProxy中完成客户端身份认证。

@Override
public void configure(HttpSecurity http) throws Exception {	
	// ensure this is initialized
	frameworkEndpointHandlerMapping();
	if (allowFormAuthenticationForClients) {
		clientCredentialsTokenEndpointFilter(http);
	}
	for (Filter filter : tokenEndpointAuthenticationFilters) {
		http.addFilterBefore(filter, BasicAuthenticationFilter.class);
	}
	http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
/** 
 * 完成客户端身份认证
*/
private ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter(HttpSecurity http) {
	//创建过滤器,并设置匹配地址,默认/oauth/token
	ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter = new ClientCredentialsTokenEndpointFilter(
			frameworkEndpointHandlerMapping().getServletPath("/oauth/token"));
	// 设置身份认证管理器,由init()方法中获取的值
	clientCredentialsTokenEndpointFilter
				.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
	//OAuth2身份认证进入点
	OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
	authenticationEntryPoint.setTypeName("Form");
	authenticationEntryPoint.setRealmName(realm);
	clientCredentialsTokenEndpointFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
	clientCredentialsTokenEndpointFilter = postProcess(clientCredentialsTokenEndpointFilter);
	//将过滤器加入到HttpSecurity 中
	http.addFilterBefore(clientCredentialsTokenEndpointFilter, BasicAuthenticationFilter.class);
	return clientCredentialsTokenEndpointFilter;
}

ClientCredentialsTokenEndpointFilter (客户端认证Token端点过滤器)

  1. 过滤器匹配地址默认:/oauth/token
  2. 获取请求参数中的:client_idclient_secret 的数据,转换为 UsernamePasswordAuthenticationToken
  3. 通过AuthenticationManager完成客户端身份认证

ClientDetailsServiceConfigurer(客户端详情服务配置)

AuthorizationServerEndpointsConfigurer(授权服务端点配置)

TokenGranter(Token授权)

CompositeTokenGranter(综合Token授权)

 List<TokenGranter> tokenGranters

通过代理方式、循环tokenGranters,根据对应的授权模式,找到指定的TokenGranter完成Token授权模式的选择。在执行方法如下:

OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest)

AbstractTokenGranter(抽象Token授权)

/*
 * 授权服务Token服务
 * 创建Token:OAuth2AccessToken createAccessToken(OAuth2Authentication authentication)
 * 刷新Token: OAuth2AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest)
 * 获取Token:OAuth2AccessToken getAccessToken(OAuth2Authentication authentication)
 */
AuthorizationServerTokenServices tokenServices;
/*
 * 客户端详情服务
 * 获取客户端详情信息:ClientDetails loadClientByClientId(String clientId) 
 */
ClientDetailsService clientDetailsService;
/*
 * OAuth2请求工厂
 * 创建OAuth2请求: OAuth2Request createOAuth2Request(ClientDetails client, TokenRequest tokenRequest)
 * 	
 */
OAuth2RequestFactory requestFactory;
/*
 * 授权码类型:authorization_code、password、refresh_token、implicit、client_credentials
 */
String grantType

RefreshTokenGranter(刷新Token授权)

AuthorizationCodeTokenGranter(授权码Token授权)

  AuthorizationCodeServices

ResourceOwnerPasswordTokenGranter(密码Token授权)

  /* 
   * 授权类型:password
   *  身份认证管理
   * /
  AuthenticationManager

ClientCredentialsTokenGranter(客户端凭证Token授权)

ImplicitTokenGranter(隐式Token授权)

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

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

相关文章

HITSZ嵌入式计算(研)23年Keil模拟器项目解决方案

HITSZ嵌入式计算&#xff08;研&#xff09;23年Keil模拟器项目解决方案 1. 项目介绍2. Keil安装3. 创建新项目3.1 参考博文3.2 流程 4. 发送串口数据4.1 参考博文4.2 串口收发流程 5. 产生波形5.1 头文件封装5.2 初始化GPIO口5.3 产生并观察方波 6. Keil信号函数和中断6.1 中断…

佩戴舒适的蓝牙耳机有哪些品牌?不伤耳的蓝牙耳机推荐

​真无线蓝牙耳机逐渐成为大家日常必不可少的数码产品&#xff0c;也随着耳机的发展&#xff0c;人们对蓝牙耳机的要求也越来越高&#xff0c;不仅音质要好&#xff0c;长时间佩戴也要舒适&#xff0c;更是能够应用于多种场景中使用&#xff0c;但挑选蓝牙耳机也是一门学问&…

kettle开发-Day38-超好用自定义数据处理组件

目录 前言&#xff1a; 一、半斤八两&#xff0c;都不太行 1、表输入&#xff0c;速度快&#xff0c;但不稳妥 2、稳的一批&#xff0c;但是慢的像蜗牛 二、各诉衷肠&#xff0c;合作共赢 1、表输入&#xff0c;高效数据插入 2、插入更新&#xff0c;一个都不能少 三、表输…

## flink- mysql同步数据至starrocks-2.5.0之数据同步

flink- mysql同步数据至starrocks-2.5.0之数据同步 mysql 创建 表 CREATE TABLE t_user (id bigint NOT NULL AUTO_INCREMENT,user_name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,age int DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB…

手机app测试杂谈

手机上的 app 分为基于 HTML5 的 app(类似于 pc 上的 b/S 应用)和本地 app(类似于 C/S 结构)。 所以测试上我们也可以充分吸收 web 的 b/s 和 c/s 测试经验。但是不同于 pc 上的应用 测试&#xff0c;手机上的测试有其独特性 测试前的思考:我们这个产品主要是做什么的?为什么我…

03. 青龙面板配置B站快速升级任务天选时刻脚本(保姆级图文)

目录 功能介绍与环境要求1. 修改配置文件拉取.sh脚本2. 拉取库脚本3. 安装 dotnet 环境4.1 扫码登录方式4.2 b站cookie方式登录&#xff08;如果你扫码成功了就不用看这个了&#xff09;获取cookie新建cookie的环境变量 5. 配置任务设置变量6. 运行每日任务测试一下总结 欢迎关…

搭建cloud项目以及各个依赖和配置说明

文章目录 背景步骤配置父pom文件spring-cloud和spring-cloud-alibaba的区别 添加网关模块配置网关的application.yml文件网关入口 普通模块普通模块的配置文件&#xff1a;普通模块的pom文件启动类&#xff1a;application.yml文件和bootstrap.yml文件的区别 总结 背景 最近在…

chatgpt赋能python:Python描述符:更加灵活的属性管理方式

Python描述符&#xff1a;更加灵活的属性管理方式 Python是一种高级编程语言&#xff0c;它的简单易用、高效性和灵活性使得它成为了现代企业和开发者的首选开发语言之一。然而&#xff0c;在使用Python编写代码时&#xff0c;很多时候需要对属性进行访问和修改&#xff0c;而…

C++单目运算符和特殊运算符的重载(9)

运算符的重载 原理和机制 C中运算符只支持基本数据类型运算&#xff0c;如果需要运算符支持类类型的运算&#xff0c;需要使用C提供的新语法 ------- 运算符的重载 运算符的重载本质上是通过函数来实现的&#xff0c;将类类型数据的运算过程写成一个特殊的函数&#xff0c;当…

YOLOv8 图像分割

一、背景 二、环境配置 官网&#xff1a;Previous PyTorch Versions | PyTorch cuda 11.7 pytorch 1.13.0 torchvision 0.14.0 pytorch-cuda 11.7 三、安装yolov8 官网&#xff1a;GitHub - ultralytics/ultralytics: NEW - YOLOv8 &#x1f680; in PyTorch > ONNX &…

Mendix 10 树形组件分析及应用

一、前言 产品研发目标是服务于业务&#xff0c;解决具体业务问题&#xff0c;带来业务价值。 因此&#xff0c;任何产品功能的推出&#xff0c;都应该秉承“从实践中来&#xff0c;到实践中去”的原则&#xff0c;在实战中发现问题&#xff0c;通过新功能设计和功能改进解决…

chatgpt科普

引言 chatgpt没有国内开放&#xff0c;为什么如此重要。抛开技术细节&#xff0c;少用专业名词&#xff0c;在整体功能上讲解 ChatGPT 的工作原理、制造过程、涌现的能力、未来的影响以及如何应对。让大家明白&#xff1a; ChatGPT是如何回答问题的。 它是怎么被制造的&…

Go语言实现单链表

博主最近在学习Go语言&#xff0c;所以打算更新一期Go语言版本的数据结构。这篇文章将的是Go语言如何实现单链表。 文章目录 前言一、个人见解&#xff0c;为什么学GO&#xff1f;二、Go语言实现单链表1.创建节点2.通过数组创建一个单链表3.遍历单链表4.单链表插入操作4.1 伪代…

HOOPS Visualize SDK 2023 Crack

桌面和移动工程应用程序的图形引擎 HOOPS Visualize 是 3D 图形 SDK&#xff0c;支持来自市场领导者 Hexagon、Trimble、Ansys、SOLIDWORKS、™ Autodesk 等的数百个工程应用程序。 用于 3D CAD 渲染的图形 SDK HOOPS Visualize 是一个以工程为中心的高性能图形库&#xff0c…

安卓蓝牙ATT协议介绍

介绍 ATT&#xff0c;Attribute Protocol&#xff0c;用于发现、读、写对端设备的协议(针对BLE设备) ATT允许蓝牙远程设备&#xff08;比如遥控器&#xff09;作为服务端提供拥有关联值的属性集&#xff0c;让作为客户端的设备&#xff08;比如手机、电视&#xff09;来发现、…

通用能力及AI核心能力表现优异!合合信息智能文档处理系统(IDP)高评级通过中国信通院评估

数字经济快速发展的背后&#xff0c;全球数据总量呈现出爆发式增长趋势。智能文档处理&#xff08;IDP&#xff09;技术能够高效地从多格式文档中捕捉、提取和处理数据&#xff0c;帮助机构和企业大幅提升文档处理效率&#xff0c;节约时间和人力成本。近期&#xff0c;合合信息…

C语言进阶--字符函数与内存函数

目录 一.字符函数 1.strlen函数 模拟实现strlen 2.strcpy函数 模拟实现strcpy 3.strcat函数 模拟实现strcat strcat能否用于自己追加自己&#xff1f; 4.strcmp函数 模拟实现strcmp 5.strncpy函数 6.strncat函数 7.strncmp函数 模拟实现strncmp 8.strstr函数 模…

哨兵2号数据下载与利用Python处理(波段融合、降采样、可视化、裁剪等)

简单介绍 网址:https://scihub.copernicus.eu/dhus/#/home 哨兵2号(Sentinel-2)是欧洲空间局(European Space Agency,简称ESA)推出的一组遥感卫星,旨在为地球观测和环境监测提供高质量的光学图像数据。 S2MSI2A是哨兵2号卫星的一种传感器。 S2MSI2A是哨兵2号卫星搭载…

huggingface - PEFT.参数效率微调

GitHub - huggingface/peft: &#x1f917; PEFT: State-of-the-art Parameter-Efficient Fine-Tuning. 最先进的参数高效微调 (PEFT) 方法 Parameter-Efficient Fine-Tuning (PEFT) 方法可以使预训练语言模型 (PLM) 高效适应各种下游应用程序&#xff0c;而无需微调模型的所有…

记录--Vue3 封装 ECharts 通用组件

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 按需导入的配置文件 配置文件这里就不再赘述&#xff0c;内容都是一样的&#xff0c;主打一个随用随取&#xff0c;按需导入。 import * as echarts from "echarts/core"; // 引入用到的图表…