Github OAuth2.0 的使用

原文:
最近想为自己的博客增加一个评论功能 。多说已经倒下了 , 畅言需要网站备案,isso 看起来也不是很好用,算了,自己折腾一个吧 。
评论系统其实还是又很多复杂的地方的,不能简单的画一个表单完事,会有很多垃圾评论的 →_→ 。我的博客系统暂时没有用户系统,一次需要一个第三方登录 。
第三方登录有 OAuth 2.0 标准,什么是 OAuth 2.0 请参考阮一峰老师的文章。
为什么使用? 因为 QQ、微信的第三方登录都需要你的域名有备案,而且需要企业用户才能接入,也就是说你的现有个公司 →→ 。考虑到技术博客会看的都是码农们,还是用吧 , 毕竟 。。。也是全球最大的搞基网站了 →→ 。
OAth2.0 流程
为了方便描述 , 我们这边画一个大致的 .0 的流程图 。
前端后端后端后端-—------------------------------------------------------| Client ID | -->| code + Client Secret |-->|access_token |-->| user info |-------------------------------------------------------
具体也可以查阅官方文档
注册
使用第三方登录,首先需要注册一个.

Github OAuth2.0 的使用

文章插图

Github OAuth2.0 的使用

文章插图

Github OAuth2.0 的使用

文章插图

Github OAuth2.0 的使用

文章插图

Github OAuth2.0 的使用

文章插图
按照步骤注册后可以得到和。
最后需要设置一下回调地址 。
Github OAuth2.0 的使用

文章插图
前端获取 code & state
根据 .0 规范 , 第一步是根据获取到 code(后面需要这个 code +换取) 。
获取 code 的步骤很简单,只要拼接好一个 URL 访问 , 带用户操作完成后 ,  会重定向会第一步中设置的回调地址 。
https://github.com/login/oauth/authorize?client_id=your_client_id&redirect_uri=your_callback_url&scope=user&state=random_string
具体 URL 参数如下:
第一步中注册得到的
第一步中设置的回调地址
loin
推荐登录的账户 , 一般不填
scope
这个参数指定了最后能获取到的信息,取值范围有 user 和 repo 等等,默认同时取 user 和 repo 的信息 , 详细取值范围见 文档
state
你设定的一个随机值,用来防止 cross-sit 攻击
这个参数指定是否允许用户在认证的时候注册账号,默认是 true
如下是我的博客的前端代码示例:
methods: {...mapMutations(['setUser']),loginWithGithub() {this.spinning = trueconst oauthUri = GITHUB.OAUTH_URIconst redirectUri = GITHUB.REDIRECT_URIconst clientId = GITHUB.CLIENT_IDconst state = new Date().getTime()const url = `${oauthUri}?client_id=${clientId}&redirect_uri=${redirectUri}&scope=user&state=${state}`window.loginWithGithub = (code, vertifyState) => {this.$api.LoginWithGithub(code, vertifyState).then(res => {const user = res.datathis.setUser(user)this.spinning = false})}const myWindow = window.open(url,'aqcoder.com-login-github','modal=yes,toolbar=no,titlebar=no,menuba=no,location=no,top=200,left=500,width=600,height=400')myWindow.focus()setTimeout(() => {if (!this.isAuthenticated) {this.$message.error('登录超时')this.spinning = false}}, 60000)},}
这里我们没有使用 标签,因为我的博客前端是用 Vue 构建的单页应用,如果使用标签会存在跳转问题,所以我这里使用了.open 弹出一个网页,待回调后,在会用..调用回来 。这样不会跳转网页,会比较平滑 。
回调地址也的代码如下:
login-.vue
>import isNil from 'lodash.isnil'export default {layout: 'empty',beforeRouteEnter(to, from, next) {next(vm => {const code = to.query.codeconst state = to.query.stateif (!isNil(code)) {window.opener.loginWithGithub(code, state)window.close()}})},mounted() {window.onblur = () => {window.focus()}}}
详细代码可以见我的博客前端代码:
拿着 code 传给后端,从后端获取
第二步就是用 code +获取了 。那这一步为什么要在后端做呢 , 可能有的同学是存 Vue 应用,没有后端会有这个疑问 。其实这一不要挪到后端做的理由很简单 。
不能暴露给在前端网页,否则会有人拿着你的干一些非法勾当,所以这一步只能放到后端 。放在前端人家一个 F12 啥都看到了 。
获取 的接口同样是 http 协议, 这样各个语言不需要 SDK 都可以直接使用 。
POST https://github.com/login/oauth/access_token
具体参数如下:
第一步中获取到的
第一步中获取到的
【Github OAuth2.0 的使用】code
第二步中前端获取到的 code
第一步中设置的回调地址
state
第一步中设置的随机值
接口的返回值会根据你的 http 头部的的值返回不同的格式
/x-www-form-
access_token=e72e16c7e42f292c6912e7710c838347ae178b4a&token_type=bearer
/json
{"access_token": "e72e16c7e42f292c6912e7710c838347ae178b4a","scope": "repo,gist","token_type": "bearer"}
/xml
bearer>repo,giste72e16c7e42f292c6912e7710c838347ae178b4a
下面是我的博客祸端代码,使用编写,详细见我的博客后端项目
var v map[string]stringjson.Unmarshal(c.Ctx.Input.RequestBody, &v)code := v["code"]state := v["state"]clientID := beego.AppConfig.String("GITHUB_CLIENT_ID")clientSecret := beego.AppConfig.String("GITHUB_CLIENT_SECRET")req := httplib.Post("https://github.com/login/oauth/access_token")req.Param("client_id", clientID)req.Param("client_secret", clientSecret)req.Param("code", code)req.Param("state", state)req.Header("Content-Type", "application/json")req.Header("Accept", "application/json")var accRet GithubAccessResulterr := req.ToJSON(&accRet)if err != nil {c.Data["json"] = errors.New(fmt.Sprintf("Error: %v", err))c.ServeJSON()return}
用获取用户信息
这一步也是在后端做的,API 如下:
GET https://api.github.com/user
我的博客后端代码片段如下:
// 获取 user inforeqUser := httplib.Get(fmt.Sprintf("https://api.github.com/user?access_token=%s", accRet.AccessToken))var githubUser GithubUsererrGetUser := reqUser.ToJSON(&githubUser)if err != nil {c.Data["json"] = errors.New(fmt.Sprintf("Error: %v", errGetUser))c.ServeJSON()return}
因为我的博客没有做完整的用户系统 , 只是在评论模块想加入一个用户认证过程 , 防止垃圾评论 。所以后面两步就直接在后端做了 , 直接使用从获取的信息注册了用户 。
完整的后端代码如下:
func (c *UserController) LoginWithGithub() {// 获取access_tokenvar v map[string]stringjson.Unmarshal(c.Ctx.Input.RequestBody, &v)code := v["code"]state := v["state"]clientID := beego.AppConfig.String("GITHUB_CLIENT_ID")clientSecret := beego.AppConfig.String("GITHUB_CLIENT_SECRET")req := httplib.Post("https://github.com/login/oauth/access_token")req.Param("client_id", clientID)req.Param("client_secret", clientSecret)req.Param("code", code)req.Param("state", state)req.Header("Content-Type", "application/json")req.Header("Accept", "application/json")var accRet GithubAccessResulterr := req.ToJSON(&accRet)if err != nil {c.Data["json"] = errors.New(fmt.Sprintf("Error: %v", err))c.ServeJSON()return}// 获取 user inforeqUser := httplib.Get(fmt.Sprintf("https://api.github.com/user?access_token=%s", accRet.AccessToken))var githubUser GithubUsererrGetUser := reqUser.ToJSON(&githubUser)if err != nil {c.Data["json"] = errors.New(fmt.Sprintf("Error: %v", errGetUser))c.ServeJSON()return}// 注册用户foreignId := fmt.Sprintf("github-%d", githubUser.ID)user, errGetUser := models.GetUserByForeignId(foreignId)if errGetUser != nil || user == nil {user = &models.User{}user.Name = githubUser.Nameuser.Nick = githubUser.Nameuser.AvatarUrl = githubUser.AvatarURLuser.Email = githubUser.Emailuser.UserType = models.UserType_GITHUBuser.ForeignId = foreignIduser.IsAdmin = falsemodels.AddUser(user)}// 设置登录 sessionc.SetSession(utils.TOKEN, user)user.Token = c.CruSession.SessionID()c.Data["json"] = utils.NewResult(user, nil)c.ServeJSON()}
最后
这样整个使用OAuth 登录就这样完成了,欢迎到我的博客体验:
Github OAuth2.0 的使用

文章插图