1. Oauth2s/

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

·522 字·3 分钟· loading
Oauth2 Oauth2 HTTP
demo007x
作者
demo007x

从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 查看

Related

从0开始构建一个Oauth2Server服务1
Oauth2 Oauth2 HTTP
我们将介绍在构建与现有 OAuth 2.0 API 对话的应用程序时需要了解的事项。无论您是构建 Web 应用程序还是移动应用程序,在我们开始时都需要牢记一些事项。
Golang 如何实现一个 Oauth2 客户端程序 (1)
Oauth2 Oauth2 HTTP golang
Golang 如何实现一个 Oauth2 客户端程序 (1)
OAuth2.0 OpenID Connect 三
Oauth2 Oauth2 HTTP
JWT 的好处是能够在其中携带信息。有了可用于您的应用程序的此信息,您可以轻松强制执行令牌过期并减少 API 调用次数。此外,由于它们经过加密签名,您可以验证它们是否未被篡改。