本篇展示了使用golang的gin框架来实现github第三方登录的一个过程。
创建OAuth应用程序
首先按照网上的教程在github的settings/Developer settings中新建一个OAuth Apps。填写信息如下:
需要注意的是,callback URL必须和你开发的程序的回调程序相同,否则无法成功回调。创建完OAuth Apps可以获得第三方登录所需要的client_id以及client_secret供下一步使用。
基于Golang Gin框架的Demo 展示
这里按照github官方文档所提供的步骤一步一步的获得github三方登录的信息。
第一步请求获得github身份信息,附加参数有client_id,redirect_uri和state。state的作用用于防止跨站请求伪造攻击。在成功请求后会github会对上一步的回调url进行请求,同时在query中返回code参数。
func HandlerGithubLogin(c *gin.Context) {
// GET https://github.com/login/oauth/authorize
conf := lib.LoadServerConfig()
// state string:An unguessable random string.
// It is used to protect against cross-site request forgery attacks.
state := "xxxxxxx"
url := "https://github.com/login/oauth/authorize?client_id=" + conf.AppId + "&redirect_uri=" + conf.RedirectURI + "&state=" + state
c.Redirect(http.StatusMovedPermanently, url)
}
在获得code参数后第二步就是根据code去申请access_token,然后再去获取用户信息。这里是采用POST的方法去请求,body的参数具体见下图以及示例代码。与第一步不同的是POST的body参数还需要client_secret和第一步获得的code。
// get access_token
func GetGithubToken(c *gin.Context) {
// POST https://github.com/login/oauth/access_token
conf := lib.LoadServerConfig()
code := c.Query("code")
loginUrl := "https://github.com/login/oauth/access_token?client_id=" + conf.AppId + "&client_secret=" + conf.AppKey + "&redirect_uri=" + conf.RedirectURI + "&code=" + code
response, err := http.PostForm(loginUrl, url.Values{
"client_id": {conf.AppId},
"client_secret": {conf.AppKey},
"redirect_uri": {conf.RedirectURI},
"code": {code},
})
if err != nil {
fmt.Println("post error!", err.Error())
return
}
defer response.Body.Close()
bs, _ := ioutil.ReadAll(response.Body)
body := string(bs)
resultMap := lib.ConvertToMap(body)
accessToken := resultMap["access_token"]
GetGithubUserMessage(accessToken, c)
}
在获得access_token后,最后一步就是获得用户信息。token参数是附带在head中进行get请求的。
// get user data
func GetGithubUserMessage(access_token string, c *gin.Context) {
// Authorization: Bearer OAUTH-TOKEN
// GET https://api.github.com/user
client := &http.Client{}
reqest, err := http.NewRequest("GET", "https://api.github.com/user", nil)
if err != nil {
panic(err)
}
reqest.Header.Add("Authorization", "token "+access_token)
resp, _ := client.Do(reqest)
if err != nil {
fmt.Println("GetMessage Err", err.Error())
return
}
defer resp.Body.Close()
// use for your website
message, _ := lib.ParseResponse(resp)
c.JSON(http.StatusOK,message)
}
最后用户结果示例如下:
{
"login": "TomaChen513",
"id": 000000,
"node_id": "AAAAAAAAAA",
"avatar_url": "https://avatars.githubusercontent.com",
"gravatar_id": "",
"url": "https://api.github.com/users",
"html_url": "https://github.com",
"followers_url": "https://api.github.com/users",
"following_url": "https://api.github.com/users",
"gists_url": "https://api.github.com/users",
"starred_url": "https://api.github.com/users",
"subscriptions_url": "https://api.github.com/users",
"type": "User",
"site_admin": false,
"name": null,
"company": null,
"blog": "",
"location": null,
"email": null,
"hireable": null,
"bio": null,
"twitter_username": null,
"public_repos": 3,
"public_gists": 0,
"followers": 1000000,
"following": 1000000,
"created_at": "2019",
"updated_at": "2023"
}