给本网站加了一个第三方github登录的功能
在github上新建oauth app
- 登录github,在用户菜单中选择Settings->Deleloper settings->OAuth Apps->New OAuth App
- 记住Client ID,Client Secret,Authorization callback URL
点击github登录
html标签
<a href="/githublogin">github</a>
路由
router.GET("/githublogin", controller.GithubLogin)
处理登录,向github发起login请求
func GithubLogin(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "https://github.com/login/oauth/authorize?client_id=531ad8e4517595748d97&state=123456789")
}
此时会打开github授权登录的页面,输入用户名和密码授权登录
github登录成功后回调
上文中设置的Authorization callback URL就是回调到本网站的地址,如我的是:
http://ningto.com/github_oauth_callback_comment
给它增加路由
router.GET("/github_oauth_callback_comment", controller.GithubOAuthCallbackComment)
处理github回调后的路由
func GithubOAuthCallbackComment(c *gin.Context) {
errmsg := ""
for {
// 第一步,获取github回调的code和state
code := c.Query("code")
state := c.Query("state")
// 第二步,POST获取access token
payload := make(map[string]interface{})
payload["client_id"] = "531ad8e4517595xxxxxx"
payload["client_secret"] = "bf123fc9fe25a30e3e33d7a07daf825b73xxxxxx"
payload["code"] = code
payload["state"] = state
data, err := json.Marshal(payload)
if err != nil {
errmsg = "json marsha1 error"
break
}
reader := bytes.NewReader(data)
url := "https://github.com/login/oauth/access_token"
request, err := http.NewRequest("POST", url, reader)
if err != nil {
errmsg = "http new post request error"
break
}
request.Header.Set("Accept", "application/json;charset=UTF-8")
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
client := http.Client{}
rsp, err := client.Do(request)
if err != nil {
errmsg = "client do request error"
break
}
var jsonAccessToken AccessTokenResponse
err = json.NewDecoder(rsp.Body).Decode(&jsonAccessToken)
if err != nil {
errmsg = "json decoder access token response error"
break
}
token := jsonAccessToken.AccessToken
// 第三步,使用access token获取用户基本信息
url = "https://api.github.com/user?access_token=" + token
rsp, err = http.Get(url)
if err != nil {
errmsg = "http get access token error"
break
}
var jsonLogin GithubLoginResponse
err = json.NewDecoder(rsp.Body).Decode(&jsonLogin)
if err != nil {
errmsg = "json decode error"
break
}
// 第四步,判断数据库中是否已存在此用户
user, err := model.GetUser("github", jsonLogin.Login)
if err != nil {
newUser := model.User {
Id: bson.NewObjectId(),
Provider: "github",
Login: jsonLogin.Login,
Password: "",
Token: token,
}
model.InsertUser(newUser)
} else {
model.UpdateUserToken(user, token)
}
// 第五步,将token保存在session中,浏览器端会将token加密后放在cookie中,
// 当前用户再次进行http请求时判断浏览器传过来的token是否在session中,
// 如果存在表示已登录
session := sessions.Default(c)
session.Set("token", token)
if session.Save() != nil {
errmsg = "session save token failed"
}
break
}
// 完成
if len(errmsg) > 0 {
c.HTML(http.StatusOK, "login.tmpl", gin.H{
"errmsg": errmsg,
})
} else {
c.Redirect(http.StatusFound, "/")
}
}
下面定义的两个结构体是github返回的json应答,由于我只需要用到部分数据,所以没有全部定义
type (
AccessTokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
}
GithubLoginResponse struct {
Login string `json:"login"`
Id int `json:"id"`
AvatarUrl string `json:"avatar_url"`
}
)