内容简介:原文:最近想为自己的博客增加一个评论功能。多说已经倒下了,畅言需要网站备案,isso 看起来也不是很好用,算了,自己折腾一个吧。评论系统其实还是又很多复杂的地方的,不能简单的画一个表单完事,会有很多垃圾评论的 →_→。我的博客系统暂时没有用户系统,一次需要一个第三方登录。
原文: http://www.aqcoder.com/post/content?id=37
最近想为自己的博客增加一个评论功能。多说已经倒下了,畅言需要网站备案,isso 看起来也不是很好用,算了,自己折腾一个吧。
评论系统其实还是又很多复杂的地方的,不能简单的画一个表单完事,会有很多垃圾评论的 →_→。我的博客系统暂时没有用户系统,一次需要一个第三方登录。
第三方登录有 OAuth 2.0 标准,什么是 OAuth 2.0 请参考阮一峰老师的文章 http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html。
为什么使用 Github ? 因为 QQ、微信的第三方登录都需要你的域名有备案,而且需要企业用户才能接入,也就是说你的现有个公司 → →。考虑到技术博客会看的都是 码农 们,还是用 Github 吧,毕竟。。。也是全球最大的搞基网站了 → →。
OAth2.0 流程
为了方便描述,我们这边画一个大致的 OAuth2.0 的流程图。
前端 后端 后端 后端 -—--------- ---------------------- ------------- ---------- | Client ID | -->| code + Client Secret |-->|access_token |-->| user info | ----------- ---------------------- ------------ ----------
具体也可以查阅 Github 官方文档 https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/
注册 Github Application
使用 Github 第三方登录,首先需要注册一个 Github Application.
github settings
github application
github new application
github new application form
github application id & skey
按照步骤注册后可以得到 ClientId 和 Client Secret。
最后需要设置一下回调地址。
github callback url
前端获取 code & state
根据 OAuth2.0 规范,第一步是根据 ClientId 获取到 code(后面需要这个 code + SecretID 换取access_token)。
获取 code 的步骤很简单,只要拼接好一个 URL 访问,带用户操作完成后,Github 会重定向会第一步中设置的回调地址。
https://github.com/login/oauth/authorize?client_id=your_client_id&redirect_uri=your_callback_url&scope=user&state=random_string
具体 URL 参数如下:
name | type | description |
---|---|---|
client_id | string | 第一步中注册得到的ClientID |
redirect_uri | string | 第一步中设置的回调地址 |
loin | string | 推荐登录的 Github 账户,一般不填 |
scope | string | 这个参数指定了最后能获取到的信息,取值范围有 user 和 repo 等等,默认同时取 user 和 repo 的信息,详细取值范围见 Github 文档 |
state | string | 你设定的一个随机值,用来防止 cross-sit 攻击 |
allow_signup | string | 这个参数指定是否允许用户在认证的时候注册 Github 账号,默认是 true |
如下是我的博客的前端代码示例:
methods: { ...mapMutations(['setUser']), loginWithGithub() { this.spinning = true const oauthUri = GITHUB.OAUTH_URI const redirectUri = GITHUB.REDIRECT_URI const clientId = GITHUB.CLIENT_ID const 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.data this.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) }, }
这里我们没有使用 <a>
标签,因为我的博客前端是用 Vue 构建的单页应用,如果使用 <a>
标签会存在跳转问题,所以我这里使用了 window.open
弹出一个网页,待 Github 回调后,在会用 window.opener.loginGithub
调用回来。这样不会跳转网页,会比较平滑。
回调地址也的代码如下:
login-github.vue
<template> <div /> </template> <script> import isNil from 'lodash.isnil' export default { layout: 'empty', beforeRouteEnter(to, from, next) { next(vm => { const code = to.query.code const state = to.query.state if (!isNil(code)) { window.opener.loginWithGithub(code, state) window.close() } }) }, mounted() { window.onblur = () => { window.focus() } } } </script>
详细代码可以见我的博客前端代码: http://www.github.com/ravenq/gvf-client
拿着 code 传给后端,从后端获取 access_token
第二步就是用 code + Client Secret 获取 access_token 了。那这一步为什么要在后端做呢,可能有的同学是存 Vue 应用,没有后端会有这个疑问。其实这一不要挪到后端做的理由很简单。
Client Secret
不能暴露给在前端网页,否则会有人拿着你的 Client Secret
干一些非法勾当,所以这一步只能放到后端。放在前端人家一个 F12 啥都看到了。
获取access_token 的接口同样是 http 协议, 这样各个语言不需要 SDK 都可以直接使用。
POST https://github.com/login/oauth/access_token
具体参数如下:
Name | Type | Description |
---|---|---|
client_id | string | 第一步中获取到的 ClientID |
cleint_secret | string | 第一步中获取到的 ClientSecret |
code | string | 第二步中前端获取到的 code |
redirect_uri | string | 第一步中设置的回调地址 |
state | string | 第一步中设置的随机值 |
接口的返回值会根据你的 http 头部的 Accept
的值返回不同的格式
application/x-www-form-urlencoded
access_token=e72e16c7e42f292c6912e7710c838347ae178b4a&token_type=bearer
application/json
{ "access_token": "e72e16c7e42f292c6912e7710c838347ae178b4a", "scope": "repo,gist", "token_type": "bearer" }
application/xml
<OAuth> <token_type>bearer</token_type> <scope>repo,gist</scope> <access_token>e72e16c7e42f292c6912e7710c838347ae178b4a</access_token> </OAuth>
下面是我的博客祸端代码,使用 Golang 编写,详细见我的博客后端项目 http://www.github.com/ravenq/gvf-server
var v map[string]string json.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 GithubAccessResult err := req.ToJSON(&accRet) if err != nil { c.Data["json"] = errors.New(fmt.Sprintf("Error: %v", err)) c.ServeJSON() return }
用 access_token 获取用户信息
这一步也是在后端做的,API 如下:
GET https://api.github.com/user
我的博客后端代码片段如下:
// 获取 user info reqUser := httplib.Get(fmt.Sprintf("https://api.github.com/user?access_token=%s", accRet.AccessToken)) var githubUser GithubUser errGetUser := reqUser.ToJSON(&githubUser) if err != nil { c.Data["json"] = errors.New(fmt.Sprintf("Error: %v", errGetUser)) c.ServeJSON() return }
因为我的博客没有做完整的用户系统,只是在评论模块想加入一个用户认证过程,防止垃圾评论。所以后面两步就直接在后端做了,直接使用从Github获取的信息注册了用户。
完整的后端代码如下:
func (c *UserController) LoginWithGithub() { // 获取access_token var v map[string]string json.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 GithubAccessResult err := req.ToJSON(&accRet) if err != nil { c.Data["json"] = errors.New(fmt.Sprintf("Error: %v", err)) c.ServeJSON() return } // 获取 user info reqUser := httplib.Get(fmt.Sprintf("https://api.github.com/user?access_token=%s", accRet.AccessToken)) var githubUser GithubUser errGetUser := 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 = ⊧.User{} user.Name = githubUser.Name user.Nick = githubUser.Name user.AvatarUrl = githubUser.AvatarURL user.Email = githubUser.Email user.UserType = models.UserType_GITHUB user.ForeignId = foreignId user.IsAdmin = false models.AddUser(user) } // 设置登录 session c.SetSession(utils.TOKEN, user) user.Token = c.CruSession.SessionID() c.Data["json"] = utils.NewResult(user, nil) c.ServeJSON() }
最后
这样整个使用 Github OAuth 登录就这样完成了,欢迎到我的博客体验: http://www.aqcoder.com
ravenq
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- RecyclerView使用指南(一)—— 基本使用
- 如何使用Meteorjs使用URL参数
- 使用 defer 还是不使用 defer?
- 使用 Typescript 加强 Vuex 使用体验
- [译] 何时使用 Rust?何时使用 Go?
- UDP协议的正确使用场合(谨慎使用)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
旷世之战――IBM深蓝夺冠之路
纽伯 / 邵谦谦 / 清华大学出版社 / 2004-5 / 35.0
本书作者Monty Neworn是国际计算机象棋协公的主席,作者是用生动活泼的笔触描写了深蓝与卡斯帕罗夫之战这一引起全世界关注的历史事件的前前后后。由于作者的特殊身份和多年来对计算机象棋的关心,使他掌握了许多局外人不能得到的资料,记叙了很多鲜为人知的故事。全书行文流畅、文笔优美,对于棋局的描述更是跌宕起伏、险象环生,让读者好像又一次亲身经历了那场流动人心的战争。 本书作为一本科普读物......一起来看看 《旷世之战――IBM深蓝夺冠之路》 这本书的介绍吧!