从0开始构建一个Oauth2Server服务 2 访问 OAuth2 服务器中的数据

从0开始构建一个Oauth2Server服务 2

访问 OAuth 服务器中的数据

本节中我们将介绍如何在现有的 OAuth 2.0 服务器上访问您的数据。对于此示例,我们将使用 GitHub API 并构建一个简单的应用程序,该应用程序将列出登录用户创建的所有存储库。

创建一个应用程序

在我们开始之前, 我们需要在github上面创建一个Application, 获取到ClientID 和Secret

在github上面找到设置页面, 点击Developer Settings, 会打开网页 https://github.com/settings/developers , 在这儿我们点击 New OAuth App您将看到一个简短的表格,如下所示

填写必填信息,包括回调 URL。如果您在本地开发应用程序,则必须使用本地地址作为回调 URL。由于 GitHub 只允许每个应用程序注册一个回调 URL,因此创建两个应用程序很有用,一个用于开发,另一个用于生产。

img

完成此表格后,您将被带到一个页面,您可以在其中查看颁发给您的应用程序的客户端 ID 和密码,如下所示。

客户端 ID 被视为公共信息,用于构建授权 URL,或者可以包含在网页的 JavaScript 源代码中。客户端机密必须保密。不要将其提交到您的 git 存储库或将其包含在任何 JavaScript 文件中!

img

环境配置

此示例代码是用 Golang 编写的,不需要外部包,也不需要框架。希望这可以在需要时轻松翻译成其他语言。要跟随此示例代码,您可以将其全部放在一个 main.go 文件中。

创建一个新文件夹并在该文件夹中创建一个名为main.go. 在命令行中,go run main.go从该文件夹内运行,您将能够在浏览器中访问http://localhost:8080以运行您的代码。以下示例中的所有代码都应添加到此main.go文件中。

首先我们需要定义几个变量

var (
	clientID    = "567bcc7f346c8ce22e1893cee0f43a3a" // 修改为自己的 clientID
	secret      = "a4a2d532e29a262a8fc67bc5e4db01be" // 修改为自己的 clientID
	serverURL   = "https://github.com/login/oauth/authorize" // github server url
	redirectURL = "http://127.0.0.1:8080/oauth/callback" // 本地服务地址
	scope       = "user read:user" // 定义授权的范围
	state       = "" 
)

定义主函数main

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8080", nil)
}

http.HandleFunc("/", handler) 主要作用是浏览器打开http://localhost:8080 后回执行handler 函数

我们定义 handler 函数的实现

func handler(w http.ResponseWriter, r *http.Request) {
	githubClient := oauth.NewOauth2Client(serverURL, clientID, oauth.WithRedirectURI(redirectURL), oauth.WithState(state), oauth.WithScope(scope))
	authURL, err := githubClient.AuthorizeURL()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}
	http.Redirect(w, r, authURL, http.StatusFound)
	return
}

这个函数首先需要实例化一个client ,并传入参数

  • serverURL: github服务地址

  • clientID: github 创建应用的clientID

  • oauth.WithRedirectURI(redirectURL) 配置回调地址, github授权成功后会携带code参数回调 http://localhost:8080/callback?code=xxxx

  • oauth.WithState(state) 配置state参数,statestate参数将与我们在初始授权请求中设置的参数相同,用于我们的应用程序在继续之前检查它是否匹配。这有助于我们的应用程序避免被诱骗将攻击者的授权代码发送到 GitHub,并防止 CSRF 攻击。

  • oauth.WithScope(scope) 配置授权范围. 具体作用可参考我前面的文章介绍

执行 go run main.go 启动服务, 打开浏览器 http://localhost:8080, 浏览器会执行函数 handler 函数, 并将地址重定向到 https://github.com/login/oauth/authorize 地址, 授权成功后浏览器跳转到我们本地地址并携带code参数 http://localhost:8080/callback?code=xxx,

现在我们需要给callback的路由设置一个处理函数, 那就是需要在main 函数中添加回调执行代码

func main() {
	http.HandleFunc("/", handler)
	http.HandleFunc("/oauth/callback", callback)

	http.ListenAndServe(":8080", nil)
}

定义callback回调执行函数

func callback(w http.ResponseWriter, r *http.Request) {
	var serverURL = "https://github.com/login/oauth/access_token"
	u, _ := url.ParseRequestURI(r.RequestURI)
	var code = u.Query().Get("code")
	log.Println("code = ", code)
	// get access token by code
	accessToken := oauth.NewAccessToken(serverURL, clientID, secret, code, oauth.AccessTokenWithContentType("application/json"))
	data, err := accessToken.DoRequest()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}
	getUserinfo(w, string(data))
}

这段代码接受到code参数, 并实例化 oauth.NewAccessToken()

参数说明:

  • serverURL: 获取github Access Token的服务器地址

  • clientID: github分配的ClientID

  • secret: github分配的Secret

  • code: 第一步query参数重获取到的code值, 这个是必须的

  • oauth.AccessTokenWithContentType("application/json"): 配置响应的数据格式

如果一切正常,GitHub 会生成一个访问令牌并在响应中返回它。我们将访问令牌存储在会话中并重定向到主页,用户已登录。

GitHub 的响应如下所示。

{
  "access_token": "e2f8c8e136c73b1e909bb1021b3b4c29",
  "token_type": "Bearer",
  "scope": "public_repo,user"
}

请求到accessToken后就可以执行 getUserinfo 获取github授权的用户信息

我们再来实现函数 getUserinfo


func getUserinfo(w http.ResponseWriter, requestURI string) {
	values, _ := url.ParseQuery(requestURI)
	var accessToken = values.Get("access_token")
	var serverURL = "https://api.github.com/user"
	user := oauth.NewUserInfo(serverURL, accessToken)
	data, err := user.DoRequest()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}
	w.Write(data)
}

这个函数我们获取到Github重返回的access_token

实例化 oauth.NewUserInfo(serverURL, accessToken) 这个函数需要两个参数

data 就是我们获取到的数据, 在本代码中就是一个 response.Body []byte类型数据

要想代码正常运行需要在文件顶部导入包:

import (
	"log"
	"net/http"
	"net/url"

	"github.com/demo007x/oauth2-client/oauth"
)

代码中用到的包地址 github.com/demo007x/oauth2-client/oauth

完整代码:

package main

import (
	"log"
	"net/http"
	"net/url"

	"github.com/demo007x/oauth2-client/oauth"
)

// This Is GitHub.com Oauth Restfull Demo

var (
	clientID    = "567bcc7f346c8ce22e1893cee0f43a3a" // change youself clientID
	secret      = "a4a2d532e29a262a8fc67bc5e4db01be"
	serverURL   = "https://github.com/login/oauth/authorize"
	redirectURL = "http://127.0.0.1:8080/oauth/callback"
	scope       = "user read:user"
	state       = "xxxx"
)

func handler(w http.ResponseWriter, r *http.Request) {
	githubClient := oauth.NewOauth2Client(serverURL, clientID, oauth.WithRedirectURI(redirectURL), oauth.WithState(state), oauth.WithScope(scope))
	authURL, err := githubClient.AuthorizeURL()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}
	http.Redirect(w, r, authURL, http.StatusFound)
	return
}

func callback(w http.ResponseWriter, r *http.Request) {
	var serverURL = "https://github.com/login/oauth/access_token"
	u, _ := url.ParseRequestURI(r.RequestURI)
	var code = u.Query().Get("code")
	log.Println("code = ", code)
	// get access token by code
	accessToken := oauth.NewAccessToken(serverURL, clientID, secret, code, oauth.AccessTokenWithContentType("application/json"))
	data, err := accessToken.DoRequest()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}
	getUserinfo(w, string(data))
}

func getUserinfo(w http.ResponseWriter, requestURI string) {
	values, _ := url.ParseQuery(requestURI)
	var accessToken = values.Get("access_token")
	var serverURL = "https://api.github.com/user"
	user := oauth.NewUserInfo(serverURL, accessToken)
	data, err := user.DoRequest()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}
	w.Write(data)
}

func main() {
	http.HandleFunc("/", handler)
	http.HandleFunc("/oauth/callback", callback)

	http.ListenAndServe(":8080", nil)
}

详细代码可以访问 github.com/demo007x/oauth2-client/oauth 查看

posted @ 2023-04-21 08:12  demo007x  阅读(61)  评论(0编辑  收藏  举报