了解oauth2.0

news2024/9/28 23:28:51

1 什么是 OAuth2.0

开发授权(OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片、视频、联系人列表等),而无需将用户名和密码提供给第三方应用。

OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下里的2小时内)访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站他们存储在另外服务提供者的某些特定信息,而非所有内容。

OAuth 是 OpenID 的一个补充,但是完全不同的服务。

OAuth 2.0

OAuth 2.0 是 OAuth 协议的下一版本,但不向下兼容 OAuth 1.0。OAuth 2.0 关注客户端开发者的简易性,同时为Web应用、桌面应用、手机和智慧设备提供专门的认证流程。

2 应用场景

参考自阮一峰的理解OAuth的适用场景的例子。

有一个“云冲印”的网站,支持将用户存在各大服务商(包括但不限于百度网盘,Google网盘等)存储的图片打印出来,现在用户想利用“云冲印”来打印他存储在百度网盘的图片。那么问题来了:“云冲印”如何拿到用户存储在百度网盘的图片呢?

答:“云冲印”需要得到用户的授权(即授权“云冲印”读取存储在百度网盘的图片)。传统的方法可能是,用户将自己百度网盘的用户名和密码告诉“云冲印”,后者就可以读取用户的照片了。但这样的做法有以下几个严重的缺点:

  1. “云冲印”为了后续的服务,“云冲印”需要保存该用户的百度网盘的用户名和密码,这会对“云冲印”公司带来额外的存储开销。
  2. “云冲印”拥有了用户存储在百度网盘所有自资料的权利,用户没法限制“云冲印”获得授权的范围和有效期。
  3. 用户修改了百度网盘的密码后,“云冲印”必须也要同步修改密码才能正常使用服务,这很显然是不合理的。

OAuth就是为了解决上述问题而诞生的。

3 名词定义

在深入了解OAuth 2.0之前,需要了解几个专用名词。

  • Third-party application:第三方应用程序,即上一节例子中的“云冲印”。
  • HTTP service:HTTP 服务提供商,即上一节例子中的“百度网盘”。
  • Resource Owner:资源所有者,又称“用户”。
  • User Agent:用户代理,一般是浏览器。
  • Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
  • Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器可以同在一台服务器也可以不同在一台服务器。

了解了上述名词,就不难理解,OAuth的作用就是让“客户端”安全可控地获取“用户”的授权,与“服务提供商”进行互动。

4 OAuth 思路

OAuth 使得“第三方应用程序”与“服务提供商”之间多了一个授权层(authorization layer)。“第三方应用程序”不能直接登录“服务提供商”,只能访问授权层,以此将用户与“第三方应用程序”区分来。“第三方应用程序”登录授权层所用的令牌,与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。

“第三方应用程序”登录授权层后,“服务提供商”根据令牌的权限范围和有效期,向“第三方应用程序”开放用户存储的资料。

5 运行流程

OAuth 2.0 的运行流程如下图

在这里插入图片描述

A) 用户打开第三方程序后,第三方程序要求用户给予授权。

B) 用户同意给予第三方程序授权。

C) 第三方程序使用上一步获得的授权,向认证服务器申请令牌。

D) 认证服务器对第三方程序进行认证后,确认无误,同意发放令牌。

E) 第三方程序使用令牌,向资源服务器申请获取资源。

F) 资源服务器确认令牌无误,同意向客户端开放资源。

6 第三方程序的授权模式

第三方程序必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0 定义了四种授权方式:

  • 授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与“服务提供商”的认证服务器进行互动。
  • 简化模式(implicit)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了“授权码”这个步骤。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
  • 密码模式(resource owner password credentials)用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。
  • 客户端模式(client credentials)指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。

7 OAuth 接入第三方程序示例

基本上遵循 OAuth 协议的第三方程序都是以下流程,举例 A 网站允许 Github 登录,差不多就是以下流程:

  1. A 网站引导用户跳转到 Github。
  2. Github 要求用户登录,并询问“ A 网站要求获得 XX 权限,是否同意?”
  3. 用户登录并同意后 Github 就会重定向回 A 网站,同时发回一个授权码。
  4. A 网站使用授权码,向 Github 请求令牌。
  5. Github 返回令牌。
  6. A 网站使用令牌,访问用户存储在 Github 的数据。

以下以gitee为例,集成OAuth:

a) 应用登记

访问这个网址完成App登记:https://gitee.com/oauth/applications/new

应用名称随便填,主页URL填http://localhost:8090,应用回调地址填http://localhost:8090/gitee/oauth/callback,再随便上传一个图片作为Logo。填写完成后 gitee 会返回供 gitee 识别的该第三方应用的 ClientID 和 ClientSecret 身份识别码。

在这里插入图片描述

b) 第三方应用程序请求授权码

运行示例代码,然后浏览器输入

http://localhost:8090/gitee/authorize

此时浏览器会重定向到(如果此时你没有登录gitee,则会要求先登录gitee)

https://gitee.com/oauth/authorize?client_id=894c85cea5b36cd629a3cf0867778692acbad55877487e111f4683312bc512cb&redirect_uri=http%3A%2F%2Flocalhost%3A8090%2Fgitee%2Foauth%2Fcallback&response_type=code

该 url 中包含了client_id:在gitee申请的第三方程序的识别的ID,和redirect_uri:同意授权后gitee会回调的地址(会携带一个授权码code)以及response_type指定授权模式,这里是授权码模式即code。

此时你确认是否同意授权以及第三方程序访问的范围

在这里插入图片描述

点击同意后,浏览器则会回调刚填入的redirect_uri,这里则是

http://localhost:8090/gitee/oauth/callback?code=18ca69350dfb2f139829944845a017cef6bfddec01c2d71e507cbdbf51962b1c

会看到这里会携带一个授权码code,接着就需要利用这个code向gitee申请令牌。

C) 获得token

可以看到已经拿到token了。这是因为后端在拿到code后直接向gitee发起申请得到token了并且返回。

在这里插入图片描述

此时已经完成了用授权码code申请到了token,接下来就可以使用token获取用户存储在gitee的资源了。

D) 获取资源

拿到token后具体可以使用哪些API可以参阅文档,文档中有API的method以及可选参数:https://gitee.com/api/v5/swagger,这里演示:获取授权用户的资料(即当前用户的资料):https://gitee.com/api/v5/user 浏览器输入

https://gitee.com/api/v5/user?access_token=5d3b7a6d1602934d12709xxxxx

即可以获取到用户的资料了

在这里插入图片描述

恭喜你,你已经可以在第三方程序发起请求使用gitee登录了(表面上的)

接下来将带你了解后端代码具体如何与gitee交互的。

E) 后端实现

这里以gitee的实现为例,github或者gitlab可以自行参考代码。

1 第三方App向服务提供商发起获得授权的申请

// curl -X GET http://localhost:8090/gitee/authorize

// Authorize
// 向服务提供商gitee发起获得授权码的申请
func Authorize(c *gin.Context) {
	u, _ := url.Parse("https://gitee.com/oauth/authorize")
	values := u.Query()
	values.Set("client_id", ClientId)
	values.Set("redirect_uri", "http://localhost:8090/gitee/oauth/callback")
	values.Set("response_type", "code")
	u.RawQuery = values.Encode()

	// redirect to -> https://gitee.com/oauth/authorize?client_id={CLIENT_ID}&redirect_uri=http://localhost:8090/gitee/oauth/callback&response_type=code
	c.Redirect(http.StatusMovedPermanently, u.String())
	return
}

使用GET请求该App的/gitee/authorize资源,服务端就会将clientIdredirect_uriresponse_type拼凑到向gitee发起请求的url上,并重定向到该url。此时浏览器会转到服务提供商(这里是gitee),gitee就会向登录的用户发起提示:是否允许该第三方App访问你的gitee资源,并且还会提示资源访问的范围。

2 第三方App获取到授权码code,并使用code获取token

// Callback
// 用户同意授权后
func Callback(c *gin.Context) {
	code := c.Query("code")
	u, _ := url.Parse("https://gitee.com/oauth/token")
	values := u.Query()
	values.Set("client_id", ClientId)
	values.Set("client_secret", ClientSecret)
	values.Set("code", code)
	values.Set("grant_type", "authorization_code")
	values.Set("redirect_uri", "http://localhost:8090/gitee/oauth/callback")
	u.RawQuery = values.Encode()

	client := http.DefaultClient
	req, _ := http.NewRequest(http.MethodPost, u.String(), nil)
	req.Header.Set("accept", "application/json")
	res, _ := client.Do(req)
	// HTTP POST -> https://gitee.com/oauth/token?client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&code={CODE}&grant_type=authorization_code&redirect_uri=http://localhost:8090/gitee/oauth/callback
	defer res.Body.Close()
	bytes, _ := ioutil.ReadAll(res.Body)
	fmt.Println(string(bytes))

	obj := make(map[string]interface{})
	json.Unmarshal(bytes, &obj)
	c.JSON(http.StatusOK, obj)
	return
}

待上述第1步完成后,gitee会携带授权码code回调服务端redirect_uri的地址/gitee/oauth/callback。此时服务端会将client_idclient_secretcodegrant_typeredirect_uri拼凑到向gitee发起请求的url上,指定接受json格式的返回值,此时返回的json就是一个包含token的对象了。

3 使用token获取gitee资源

func Userinfo(c *gin.Context) {
	token := c.Query("token")

	u, _ := url.Parse("https://gitee.com/api/v5/user")
	values := u.Query()
	values.Set("access_token", token)
	u.RawQuery = values.Encode()

	client := http.DefaultClient
	req, _ := http.NewRequest(http.MethodGet, u.String(), nil)
	req.Header.Set("accept", "application/json")
	res, _ := client.Do(req)
	// HTTP GET -> https://gitee.com/api/v5/user?access_token={TOKEN}
	defer res.Body.Close()
	bytes, _ := ioutil.ReadAll(res.Body)
	fmt.Println(string(bytes))

	info := new(UserInfo)
	json.Unmarshal(bytes, &info)
	c.JSON(http.StatusOK, info)
	return
}

待第2步获取到token后,我们就可以使用该token访问用户存储在gitee的资源了。使用GET请求将token作为query访问/gitee/userinfo,此时服务端就会将此token拼凑到向gitee请求资源的url上,发起GET请求,制定接受json格式的返回值,此时gitee就会返回该token对应的userinfo了。

示例仓库

示例仓库

https://github.com/FanGaoXS/oauth_demo.git

代码用Go集成了giteegithubgitlab的OAuth实现,记得阅读README然后根据实际情况修改.env文件中的配置信息。

参考自

理解OAuth 2.0:https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

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

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

相关文章

我的软件测试故事:成长、迷茫、奋斗

前言 测试工作6年,经历过不同产品、共事过不同专业背景、能力的同事,踩过测试各种坑、遇到过各种bug。测试职场生涯积极努力上进业务和技术能力快速进步过、也有努力付出却一无所得过、有对测试生涯前景充满希望认为一片朝气蓬勃过、也有对中年危机思考不…

OpenAI Embedding:快速实现聊天机器人(二)

theme: orange 本文正在参加「金石计划」 接上文OpenAI Embedding:快速实现聊天机器人(一)有讲到聊天机器人的一些概念,这篇开始讲讲这个聊天机器人的架构和流程。 总架构图 这里我参照一个现成的架构图来讲如何实现,其是基于Azure多个相关云…

香港进出口报关指南

由于香港优越的地理位置和政策支持,大部分外贸货物通过香港出口到世界各地。与内地海关相比,香港报关更简单快捷。司机可根据内地舱单填写一份香港进/出口舱单(俗称司机纸)进出香港海关,并在货物抵港后14天内申报。 香港进出口报关需要提供哪…

单机/集群/热备/磁盘阵列(RAID)的区别详解

一、单机部署(stand-alone) 单个服务器,只有一个饮水机提供服务,服务只部署一份。 二、集群部署(cluster) 服务器集群就是指将很多服务器集中起来一起进行同一种服务,在客户端看来就像是只有…

MySQL连接空闲时间超过8小时报错原因与延伸知识

1 错误原因 1.1 两个参数 MySQL服务端两个参数控制连接超时时间: wait_timeoutinteractive_timeout1.1.1 如何查看 show global variables like interactive_timeout show global variables like wait_timeout 复制代码 1.1.2 含义与区别 wait_timeout&#xf…

JAVA System.nanoTime()与GO time.Now().UnixNano()区别

JAVA System.nanoTime() public static void main(String[] args) {long nano System.nanoTime();System.out.println(nano);} 输出:701863191254000,这个值有点奇怪,System.currentTimeMillis()不是System.nanoTime()的1000000倍。 这个值…

【springcloud开发教程】路由网关——zuul

官方资料:https://github.com/Netflix/zuul/ 什么是Zuul? Zuul包含了两个主要的功能:路由和过滤 路由功能将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预&#…

Ubuntu环境安装指定版本的docker 和一键卸载dockerdocker-compose

一、环境 我的linux系统是Ubuntu 22.04 二、开始安装 卸载旧版 由于apt官方库里的docker版本可能比较旧,所以先卸载可能存在的旧版本: $ sudo apt-get remove docker docker-engine docker-ce docker.io更新apt包索引 $ sudo apt-get update安装以下…

基础工业工程(易树平、郭伏)——第四章 程序分析

第四章 程序分析 第一节 程序分析概述 一、程序分析的概念、特点及目的 1.程序分析的概念 程序分析是一种重要的生产管理工具,它是依照工作流程,从第一个工作地到最后一个工作地,全面地分析有无多余、重复、不合理的作业&…

【单片机/普中A2】学习笔记3-数码管

数码管主要概念 参考文献:点击前往 数码管分类 字形重叠数码管:将不同数字、字母与符号重叠,需要用到那个就显示那个 分段式数码管:最常见的数码管,按照笔画来显示,主要有七段式和八段式数码管 点矩阵式…

JavaScript 代码整洁之道

文章目录概述篇变量篇函数篇注释篇异常处理篇复杂判断函数篇重构篇参考资料概述篇 书写能让人读懂的代码使用英语编写代码团队协作 制定通用的规则,依靠工具让团队的代码风格保持统一,要让代码看起来是由一个人编写的,消除个人的代码风格。…

这么好看的客服组件,还是觉得接入ChatGPT,把它放在博客中那就完美了

我们在使用ChatGPT时,它的返回方式是采用流式回复。感觉这个效果不错。之前做的全都是的等全部结果请求完成,再一次性返回给用户。今天就通过流式回复的效果重写之前的程序。 前端 前端采用一个网页版的客服组件,整体的效果如下:…

小白学Pytorch系列- -torch.distributions API Transforms (2)

小白学Pytorch系列- -torch.distributions API Transforms (2) AbsTransform AffineTransform 通过逐点仿射映射进行转换yloc⁡scale xy\operatorname{loc}\text { scale } \times xyloc scale x loc (Tensor or float) : 位置参数scale (Tensor or float) : 尺度参数event…

k8s Deployment

一个Deployment控制器会创建一个新的ReplicaSet控制器,通过ReplicaSet创建pod rs v1控制三个pod,删除一个pod,在rs v2上重新建立一个,依次类推,直到全部都是由rs v2控制,如果rs v2有问题,还可以…

Spark on Yarn(client和cluster模式,spark-shell 和 spark-submit 的区别,WorldCount实现与理解)

文章目录Spark on Yarn两种模式clientclusterspark-shell 和 spark-submit 的区别的理解spark-shellspark-submitWorldCount实现IDEA本地实现On Yarn 实现WorldCount图解Spark on Yarn spark on yarn 的两种模式是指 spark 应用程序的 driver 进程(负责控制和协调整…

JAVA8的Optional类,还用 != null 判空?

在文章的开头,先说下NPE问题,NPE问题就是,我们在开发中经常碰到的NullPointerException.假设我们有两个类,他们的UML类图如下图所示 在这种情况下,有如下代码 user.getAddress().getProvince(); 这种写法,在…

springboot整合mybatis(idea)

从idea新建项目 选择spring启动* help、mvnw 文件可以删除 springBoot3.0需要的最小JDK是JDK17,当低于17的时候会报错。 改成2.7.6 新建控制层Controller、Mapper层和Model文件夹 必须在springBoot启动项下面新建,不然无法识别。 允许XML进入target…

Java - 泛型

一、什么是泛型? 一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的 代码,这种刻板的限制对代码的束缚就会很大。----- 来源《Java编程思想》对泛型的介绍。 泛型是在JDK1.5…

初次使用yolov8遇到的问题

记录第一次使用yolo8跑自己的数据; 首先将官方文档看一下,大概捉摸了2个小时,地址:GitHub - ultralytics/ultralytics: NEW - YOLOv8 🚀 in PyTorch > ONNX > CoreML > TFLite 获得了基本的一些了解&#x…

$\Beta$分布推导与可视化

$\Gamma$函数 $\Gamma$函数(Gamma函数)是阶乘函数在实数和复数域的扩展。对于正整数$n$,阶乘函数表示为$n! 1 \times 2 \times ... \times n$。然而,这个定义仅适用于正整数。Gamma函数的目的是将阶乘扩展到实数和复数域,从而计算实数和复数…