OAuth 2.0 相关整理记录
status
Published
type
Post
slug
oauth-2.0-practice
date
Apr 22, 2021
tags
OAuth 2.0
Note
summary
OAuth 2.0 是一种开放标准的授权协议,用于应用程序访问其他网站上用户账户的权限管理。它提供了安全性、便利性和灵活性。OAuth 2.0 支持多种授权类型,包括授权码模式、隐式 / 简化模式、密码模式和客户端凭证模式。授权码模式是最常见的,用户会被重定向到授权服务器进行身份验证,并授权应用程序访问其账户。其他类型则不同程度上降低了安全性和交互性。开发过程中需要前端、后端和授权服务器三个模块来实现授权码模式。前端生成授权请求 URL,用户登录后会重定向到授权服务器,并返回授权码给前端。前端将授权码传递给后端,后端使用授权码交换访问令牌。授权服务器验证授权码,并返回访问令牌给后端。后端可以使用访问令牌来访问受保护的资源。这是一个简单的 OAuth 2.0 授权流程。
OAuth 2.0 是当下较为流行的一种开放标准的授权协议,使应用程序能够在不共享用户密码的情况下访问其他网站上的用户账户。
授权与登录是区别在于,授权是指用户允许第三方应用访问其账户的部分信息或功能,而登录是指用户使用其账户在第三方应用上注册或登录。OAuth 使用户可以在不泄露密码的情况下,授予第三方应用对其账户的有限访问权限。
此协议在各种应用系统中广泛应用,虽然之前有对接使用过,但没有构建起成体系的认知,于是集中整理记录一下。
为什么使用
- 安全性: 用户的密码永远不会与应用程序共享,只有在用户授权的情况下,应用程序才能访问用户的账户。
- 便利性: 用户不需要记住每个应用程序的单独密码,用户可以随时撤销应用程序的访问权限。
- 灵活性:许多热门网站和应用程序支持,并且在自己的应用程序中很容易实现。
OAuth 2.0 授权过程
概括来说, OAuth 2.0 通过委派身份验证给托管用户账户的应用程序(Web 网站)来工作。应用程序请求用户授权,如果用户授权,应用程序将获得一个可以用于访问用户账户的令牌。该令牌是临时的,并且用户可以随时撤销它。
- 客户端注册: 应用程序向授权服务器注册。注册过程包括提供应用程序的名称、网站和联系信息。
- 资源所有者授权: 用户授权应用程序访问其账户。用户将看到一个同意屏幕,说明应用程序将能够使用他们的账户进行的操作。
- 访问令牌授权: 授权服务器向应用程序颁发访问令牌。应用程序使用访问令牌来访问用户的账户。
- 访问令牌使用: 应用程序使用访问令牌来访问用户的账户。应用程序只能访问用户授权访问的资源。
OAuth 2.0 授权类型
OAuth 2.0 支持多种授权类型,可以根据不同的使用场景及现实需要来选用相应的授权类型。
- 授权码模式(Authorization Code): 最常见的授权类型。用户被重定向到授权服务器的网站,在那里他们将看到一个同意屏幕。如果用户授权,授权服务器将向应用程序颁发授权码。然后应用程序使用授权码交换访问令牌。
+--------+ +---------------+ +-----------------+ | |--(A)----- Authorization -->| | | | | | | | | | | |<-(B)----- Redirect URI ----| | | | | | | Authorization | | | | | | Server | | | | | | | | | | | +---------------+ | | | | | | | | | | | | | | | | | | | | | |<-(C)------ Authorization --| | | | | | | | | | | |--(D)----- Authorization -->| |--(E)---- Authorization ----| | | | | | | | | |<-(F)----- Redirect URI ----| | | | | | | Authorization | | | | | | Server | | | | | | | | | | | +---------------+ | | | | | | | | | | | | | | | | | | | | | |<-(G)----- Access Token ----| | | | | | | | | | | | | | | | +--------+ +---------------+ +-----------------+ User Application Authorization Server
- 隐式/简化 模式(Implicit): 相比授权码授权不太安全,但更容易实现。用户不会被重定向到授权服务器的网站。相反,授权服务器直接向应用程序颁发访问令牌。
+--------+ +---------------+ +-----------------+ | |--(A)----- Authorization -->| | | | | | | | | | | |<-(B)----- Redirect URI ----| | | | | | | Authorization | | | | | | Server | | | | | | | | | | | +---------------+ | | | | | | | | | | | | | | | | | | | | | |<-(C)----- Access Token ----| | | | | | | | | | | | | | | | +--------+ +---------------+ +-----------------+ User Application Authorization Server
- 密码模式(Password):用户将其用户名和密码直接提供给客户端应用程序,客户端应用程序使用这些凭据直接向授权服务器请求访问令牌。这种授权类型的安全性相对较低,因为用户的用户名和密码必须通过客户端应用程序进行传输和存储。
+--------+ +---------------+ +-----------------+ | |--(A)------ Request ------->| | | | | | | | | | | |<-(B)----- Access Token ----| | | | | | | Authorization | | | | | | Server | | | | | | | | | +--------+ +---------------+ +-----------------+ User Application Authorization Server
- 客户端凭证 (Client Credentials): 这种授权类型用于不需要与用户交互的应用程序。应用程序直接从授权服务器获取访问令牌。
+---------------+ +---------------+ +-----------------+ | |--(A)------ Request -->| | | | | | | | | | | |<-(B)---- Access Token-| | | | | | | Authorization | | | | | | Server | | | | | | | | | +---------------+ +---------------+ +-----------------+ Application Application Authorization Server
授权码模式 Demo
在一般的开发过程中,一般是可以分为三个模块,即当前系统
前端
、后端
,OAuth 2.0 服务端
(授权服务器)那么授权码模式下,从这三个模块看整个流程就是如下的情况:
当前系统前端视角
- 用户在当前系统的前端界面上点击登录按钮。
- 当前系统前端生成授权请求URL,并将用户重定向到该URL。
- 用户在授权服务器的登录页面上输入凭据进行身份验证。
- 用户同意授权后,授权服务器将用户重定向回当前系统前端,并附带授权码作为查询参数。
- 当前系统前端从回调URL中提取授权码。
当前系统后端视角
- 当前系统后端接收到当前系统前端传递的授权码。
- 当前系统后端使用授权码和其他必要参数构建请求,向授权服务器发送请求以交换访问令牌。
- 授权服务器验证授权码和相关参数的有效性,并颁发访问令牌和刷新令牌。
- 当前系统后端接收到访问令牌和刷新令牌。
OAuth 2.0 服务端视角
- 授权服务器接收到当前系统后端发送的交换令牌请求。
- 授权服务端验证授权码的有效性和与客户端的匹配。
- 授权服务端生成访问令牌和刷新令牌,并将其返回给当前系统后端。
前端
func handleLogin() { // 构建授权请求URL authURL := `https://example.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}` // 重定向用户到授权请求URL window.location.href = authURL; }
后端
// 回调处理函数 func handleCallback(w http.ResponseWriter, r *http.Request) { // 检查回调请求中的授权码和状态参数 code := r.FormValue("code") state := r.FormValue("state") // 使用授权码和其他参数向授权服务器发送请求以交换令牌 tokenURL := "https://example.com/oauth/token" payload := []byte(fmt.Sprintf( "grant_type=authorization_code&code=%s&redirect_uri=YOUR_REDIRECT_URI&client_id=YOUR_CLIENT_ID", code)) req, err := http.NewRequest("POST", tokenURL, bytes.NewBuffer(payload)) if err != nil { log.Println("Failed to create token request:", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") client := &http.Client{} resp, err := client.Do(req) if err != nil { log.Println("Failed to exchange authorization code:", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } defer resp.Body.Close() // 解析响应并提取访问令牌 var tokenResponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` ExpiresIn int `json:"expires_in"` } err = json.NewDecoder(resp.Body).Decode(&tokenResponse) if err != nil { log.Println("Failed to parse token response:", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } // 使用访问令牌进行受保护资源的请求 apiURL := "https://api.example.com/resource" req, err = http.NewRequest("GET", apiURL, nil) if err != nil { log.Println("Failed to create API request:", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", tokenResponse.AccessToken)) resp, err = client.Do(req) if err != nil { log.Println("Failed to make API request:", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } defer resp.Body.Close() // 处理API响应 // ... // 返回结果给用户 // ... }
相关资料
以上是个人的粗浅整理,以下为 阮一峰博客 博客中对 OAuth 2.0 的一些理解与教程
- 整体理解:
- 实例教程