【OAuth2框架】理解和实战 OAuth2 认证授权

news/2025/1/16 12:33:11/文章来源:https://www.cnblogs.com/o-O-oO/p/18674764

你知道互联网大厂最怕的是什么吗?但凡有点这样的风吹草动,我们就要花费大量的时间进行修复和上线。一点都不敢耽误,对于紧急类型的,基本当天发现,当天就要升级上线。那是什么问题呢?🤔

其实最怕的就是各类组件漏洞!

有这么一个东西,13scan - 安全漏洞扫描 它可以扫描出系统组件的各项存在的漏洞,给出整改建议。因为这些漏洞的存在,就可能让不法用户通过接口调用到系统数据。比如,随意输入个订单号,就知道是谁,什么时间、购买的什么、地址在哪。这是非常可怕的。

所以,在互联网大厂中,会有统一的安全授权认证服务 OAuth2。这样即使有外部对接的系统确实需要授权获得用户的数据,也可以在可靠的范围内进行授权和使用。

那么,OAuth2 是个啥呢?🤔 本节我们来分享下并做个代码案例运行验证。

一、OAuth2 是啥?

OAuth 2.0 的标准 RFC 6749,解释了 OAuth 是什么。

官网:https://datatracker.ietf.org/doc/html/rfc6749

OAuth 2.0 本身是一种开放标准,不是一个具体的服务类组件,而是一种标准。旨在为用户提供授权,允许第三方应用程序访问用户在某个服务提供者(如社交网络或云服务)上的信息,而无需将用户的凭证(如用户名和密码)透露给这些应用程序。OAuth 2.0 主要用于授权,而不是身份验证。

而 Spring 中 OAuth2 就是对这套标准的具体实现,但这不是唯一实现,你甚至可以通过这套标准做一套自己的 OAuth2 授权框架。

二、举个例子

大家在日常的生活中使用互联网类的产品,包括;购物、视频、出行等,都可能收到活动类的短信,问你是否要参与一个这样的活动,如果参与则需要点击授权允许。那么这个过程就有 OAuth2 的授权使用。
如图;

这是一套用户参与小傅哥分享的星球用户活动页面。用户点击参与后,会引导进入授权验证。显示进入微信登录,之后跳转到用户数据授权使用页。
用户允许授权后,小傅哥的这套活动页就可以拿到用户个人的数据,通过个人的数据为唯一标识,允许用户参与本次活动。这些活动可以是一些抽奖、礼包领取、代码仓库授权等。这些场景的使用,就是 OAuth2 的授权框架作用。

三、授权方式

在看 OAuth2 之前,可以代入的思考下,如果是你做一个认证授权框架,你会怎么做。其实你在最开始学习编程使用账号密码在数据库里匹配验证,完成后生成一个 Token 让前端保存到 Cookie 里,之后每次请求后端都携带上这个 Cookie 进行校验。

其实这个模型就是认证授权框架。认证;使用账密证明你是你,授权,则通过账密分配一个Token,让使用放通过 Token 进行数据访问。

那么,OAuth2 作为认证授权框架,提供了四种授权访问,包括;

  1. 授权模型(authorization-code)
    
  2. 隐藏模式(简单授权)(implicit)
    
  3. 密码模式(password)
    
  4. 客户端凭证模式(client credentials)
    

这四种授权方式,逐渐减弱。不过,无论那种授权方式,在第三方应用申请可调用数据的令牌前,都需要先完成系统备案,验明自身身份。包括客户端 ID、客户端秘钥 Client Secret。

3.1 授权模型

授权模式:指第三方应用先申请一个授权码,之后再使用该码获得令牌。授权码模式通常用于具有浏览器界面的应用程序,尤其是在需要用户交互的场景下,例如传统的Web应用。由于使用了重定向和授权码,维护了更高的安全性。

工作流程:

1、用户在客户端(第三方应用)上点击登录。
2、客户端将用户重定向到授权服务器,携带其注册的客户端ID、重定向URI和请求的权限范围。
3、用户在授权服务器上验证身份,并同意授权后,授权服务器将用户重定向回客户端,附带一个授权码。
4、客户端使用该授权码向授权服务器请求访问令牌,同时发送客户端ID、客户端密钥和重定向URI。
5、授权服务器验证请求,并返回访问令牌(和可选的刷新令牌)。
6、客户端使用访问令牌访问受保护的资源。

3.2 隐藏模式

隐式模式主要适用于在Web浏览器中运行的单页应用(SPA)等不安全的客户端环境,因为不需要后台服务器交换授权码,简化了流程。然而,隐式模式由于直接暴露令牌,安全性较低,不建议用于敏感操作。

工作流程:

1、用户在客户端上点击登录。
2、客户端将用户重定向到授权服务器,携带客户端ID、重定向URI及请求的权限范围。
3、用户在授权服务器进行身份验证,并同意授权后,授权服务器立即将访问令牌作为URI片段重定向回客户端。
4、客户端在接收到重定向后,解析URI以获取访问令牌,随后可直接使用该令牌访问受保护的资源。

3.3 密码模式

密码模式适用于用户信任客户端的情况,如用户通过原生应用(移动应用)访问服务。在此情况下,客户端直接处理用户的凭据,使用时要确保应用的安全性。

工作流程:

1、用户在客户端直接输入其用户名和密码。
2、客户端将用户的凭据(用户名和密码)发送到授权服务器,请求访问令牌。
3、授权服务器验证凭据并返回访问令牌(和可选的刷新令牌)。
4、客户端使用访问令牌访问受保护的资源。

3.4 客户端凭证模式

客户端凭证模式主要用于服务器与服务器之间的通信,如后台服务相互访问API,或者服务自身需要访问其资源。适用于没有用户上下文的场景,更多用于机器对机器(M2M)通信。

工作流程

1、客户端向授权服务器发送包含其客户端ID和客户端密钥的请求,请求访问令牌。
2、授权服务器验证客户端身份,并返回访问令牌。
3、客户端使用访问令牌访问受保护的资源,通常是与服务器本身相关的资源。

四、授权代码

有了上面的概念,我们再来看个实际的案例工程,验证四种授权模式。
环境信息如下;

JDK 1.8
Maven 3.8.*
MySQL 5.x ~ 8.x,案例使用的是 8.x
工程:https://github.com/fuzhengwei/xfg-dev-tech-oauth2

4.1 工程结构

首先,案例工程提供了 OAuth2 的授权框架 + 数据库配置实现。
docs 下提供了 docker compose 安装 MySQL 的脚本和导入库表的操作,这套库表就是授权框架的库表。
验证功能的时候需要使用到 ApiPost,你可以下载使用,并到 api-json 导入。

4.2 核心实现

除了 OAuth2 关于 Spring Security 部分已经在前面的课程讲解过,可以补充学习。

https://bugstack.cn/md/road-map/spring-security.html

1、 账户认证

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();String clientId;if (authentication != null) {Object principal = authentication.getPrincipal();if (principal instanceof User) {User clientUser = (User) principal;clientId = clientUser.getUsername();} else if (principal instanceof OauthAccountUserDetails) {getClientIdByRequest();return (OauthAccountUserDetails) principal;} else {throw new UnsupportedOperationException();}} else {clientId = getClientIdByRequest();}// 校验用户 - 直接从数据库查询OauthAccount account = oauthAccountDao.loadUserByUsername(clientId, username);if (account == null || !account.getAccountNonDeleted()) {throw new UsernameNotFoundException("err user is not found!");}return new OauthAccountUserDetails(account, new ArrayList<>());
}

2、 刷新授权

@Bean
public TokenEnhancer additionalInformationTokenEnhancer() {return (accessToken, authentication) -> {Map<String, Object> information = new HashMap<>(8);Authentication userAuthentication = authentication.getUserAuthentication();if (userAuthentication instanceof UsernamePasswordAuthenticationToken) {UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) userAuthentication;Object principal = token.getPrincipal();if (principal instanceof OauthAccountUserDetails) {OauthAccountUserDetails userDetails = (OauthAccountUserDetails) token.getPrincipal();OauthAccount oauthAccount = userDetails.getOauthAccount();information.put("account_info", UserAccountVO.builder().id(oauthAccount.getId()).clientId(oauthAccount.getClientId()).username(oauthAccount.getUsername()).mobile(oauthAccount.getMobile()).email(oauthAccount.getEmail()).build());((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(information);}}return accessToken;};
}

3、 添加账户

@Resource
private PasswordEncoder passwordEncoder;
@Test
public void test_passwordEncoder() {log.info("测试结果:{}", passwordEncoder.encode("123456"));
}

这里测试可以生成一个需要的密码,账户填写到数据库中使用。

4.3 测试验证

在测试之前,你要启动服务,确保运行没问题。启动前配置数据库连接。

spring:datasource:username: rootpassword: 123456url: jdbc:mysql://192.168.1.109:13306/xfg-dev-tech-oauth2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&useSSL=truedriver-class-name: com.mysql.cj.jdbc.Driver

关于 ApiPost 的测试,你可以直接从工程中的 json 导入的你的 ApiPost 就可以使用了。

1、 客户端凭证

{"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYmlnLW1hcmtldC1hcHAiXSwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTczNjY3OTA4MCwiYXV0aG9yaXRpZXMiOlsidXNlciJdLCJqdGkiOiI4NWY0YjY2Ni1mNDliLTRiNGEtOTM1Ni0xYjRiMTVmZmI5MWEiLCJjbGllbnRfaWQiOiJ4Zmctc3R1ZGlvIn0.CqMOMbBkHMnQicpkBEeqMyJEp9HbSiGgXoYUke_PWtI","token_type": "bearer","expires_in": 7198,"scope": "read write","jti": "85f4b666-f49b-4b4a-9356-1b4b15ffb91a"
}

请求:http://127.0.0.1:8091/oauth/token?grant_type=client_credentials
认证:xfg-studio/123456

2、 用户密码模式

{"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYmlnLW1hcmtldC1hcHAiXSwiZXhwIjoxNzM2Njc5MTQxLCJ1c2VyX25hbWUiOiJ4aWFvZnVnZSIsImp0aSI6ImVhZWMzZmQ0LTViOTAtNGRhNy1hODQ1LTA2MDFmMjJiNDc2ZCIsImNsaWVudF9pZCI6InhmZy1zdHVkaW8iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.JgUxx6_aHqCBxuvYXvekw-ZW5pPnSw5LEKlfsd4qVyI","token_type": "bearer","refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYmlnLW1hcmtldC1hcHAiXSwidXNlcl9uYW1lIjoieGlhb2Z1Z2UiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiZWFlYzNmZDQtNWI5MC00ZGE3LWE4NDUtMDYwMWYyMmI0NzZkIiwiZXhwIjoxNzM5MjYzOTQyLCJqdGkiOiI5ZDc4ZjVjZS0xZTMwLTRiZTYtYWUyNi01NjY1NWQ4YjYzZjIiLCJjbGllbnRfaWQiOiJ4Zmctc3R1ZGlvIn0.8gMfqhBnc4wI9BsRENu_16RmZFqeCWVSyWcF4B9nA1I","expires_in": 7198,"scope": "read write","account_info": {"id": null,"clientId": "xfg-studio","username": "xiaofuge","mobile": "13500002222","email": "523088136@qq.com"}
}

请求:http://127.0.0.1:8091/oauth/token
认证:xfg-studio/123456
参数:grant_type = password、username = xiaofuge、password = 123456

3、 授权模式

(1)登录认证


{"status": 200,"message": "hi login success!"
}

请求:http://127.0.0.1:8091/login
认证:xfg-studio/123456
参数:username = xiaofuge、password = 123456
说明:你会拿到一个 Cookie JSESSIONID=9000E64733AA6E947054AC4326C91AF8 这个 cookie 用于获取授权码

(2) 获取授权码&跳转

请求:http://127.0.0.1:8091/oauth/authorize?client_id=xfg-studio&response_type=code&grant_type=authorization_code
认证:无
参数:client_id = xfg-studio、response_type = code、grant_type=authorization_code
注意:如果 oauth_client_details 表字段配置 autoapprove = false 则不会直接跳转页面,会进行让用户确认。

之后刷新令牌、检查令牌,就可以单独测试了。如果部署到云服务器,那么还可以走浏览器访问,单独有一个获取令牌的操作,之后再跳转地址。

作者:小傅哥
博客:https://bugstack.cn

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

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

相关文章

2025.1.15——1200

2025.1.15——1200Q1. 1200 简单来说就是给定3个数组,每个数组选择一个数,三者下标不同,问三者和的最大值。 Winter holidays are coming up. They are going to last for \(n\) days. During the holidays, Monocarp wants to try all of these activities exactly once wi…

【附源码】JAVA大学生竞赛管理系统源码+SpringBoot+VUE+前后端分离

学弟,学妹好,我是爱学习的学姐,今天带来一款优秀的项目:大学生竞赛管理系统 。 本文介绍了系统功能与部署安装步骤,如果您有任何问题,也请联系学姐,偶现在是经验丰富的程序员! 一. 系统演示 系统测试截图系统视频演示https://githubs.xyz/show/343.mp4二. 系统概述【 系…

Ubuntu升级Linux内核教程

本文作者CVE-柠檬i:https://www.cnblogs.com/CVE-Lemon 本文使用的方法是dpkg安装,目前版本为5.4.0-204,要升级成5.8.5版本下载 下载网站:https://kernel.ubuntu.com/mainline/ 在该网站下载deb包,选择自己想要升级的版本,这里是5.8.5https://kernel.ubuntu.com/mainline/…

psSign、random推导

入口:传递的参数值:sign函数 function() {var _0x36c5d3 = _0x79c1ce;let _0x479298 = arguments[_0x36c5d3(0x5a9, PHXL)] > 0x0 && void 0x0 !== arguments[0x0] ? arguments[0x0] : {};try {var _0x1a6f66;if (!_0x32d6a8[_0x36c5d3(0x4d7, l!Uo)][_0x36c5d3(…

渗透测试中如何反编译JAR

反编译是渗透测试中的重要环节之一。 通过反编译,我们可以得到程序的项目结构、相关资源以及配置的数据库等信息。 本文以常见SpringBoot项目为例,对其进行反编译。 Spring Boot 是一个基于 Spring 的框架,旨在简化 Spring 应用的配置和开发过程,通过自动配置和约定大于配置…

专项训练2

贪心专题 1. [NOIP2015 普及组] 推销员 link:https://www.luogu.com.cn/problem/P2672 思路跟正解大差不差,但想的有点复杂了。先把所有的按疲劳值排个序,(这样省却了找最大疲劳值的过程),然后只用考虑第x大的和后面距离+疲劳值最大值的比较即可(累了,不想写了) 2. Tw…

vue2子组件获取父组件的实例以及数据,vue2子组件获取父组件的数据

多个组件引入同一个js文件,实例化对象,数据不会错乱,再引入相同的组件,例如每个页面都需要引入到一个分页组件,然后分页组件需要获取各自父组件中的实例对象 通过 this.$parent 即可获取到父组件中的数据 所以在使用子组件时可以不用在组件上传入数据 公共js文件functi…

专利申请审批流程

依据专利法,发明专利申请的审批程序包括受理、初审、公布、实审以及授权五个阶段。实用新型或者外观设计专利申请在审批中不进行早期公布和实质审查,只有受理、初审和授权三个阶段。 发明、实用新型和外观设计专利的申请、审查流程图如下:

深入理解第二范式(2NF):提升数据库设计的有效性与灵活性

title: 深入理解第二范式(2NF):提升数据库设计的有效性与灵活性 date: 2025/1/16 updated: 2025/1/16 author: cmdragon excerpt: 数据库的规范化是确保数据完整性和消除数据冗余的关键过程。第二范式(2NF)是关系数据库设计中的重要概念,进一步建立在第一范式的基础之上…

【附源码】JAVA课程管理系统源码+SpringBoot+VUE+前后端分离

学弟,学妹好,我是爱学习的学姐,今天带来一款优秀的项目:课程管理系统 。 本文介绍了系统功能与部署安装步骤,如果您有任何问题,也请联系学姐,偶现在是经验丰富的程序员! 一. 系统演示 系统测试截图系统视频演示https://githubs.xyz/show/342.mp4二. 系统概述【 系统功能…

【YashanDB知识库】解压安装包时报错tar:Error is not recoverable

本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7919233.html?templateId=1718516 现象 解压安装时报如下错误:、分析过程 1、检查解包命令是否正确 tar --help 2、查询CPU类型(arm/x86,32bit/64bit)是否与包匹配 lscpu3、检查MD5值, md5sum 再下…

多校区管理难题:管理工具的高效解决方案

一、连锁培训机构的管理挑战 多校区数据同步难题连锁机构需要实时掌握各校区的学员、教师、财务等数据,而传统的手工记录或独立系统难以满足这一需求。 跨校区资源协作障碍教室、师资和课程安排在不同校区间分配时,容易产生冲突或资源浪费。 信息孤岛现象各校区独立运作的模式…