博古铸魂 發表於 2019-9-8 09:21:00

github 授权登录教程与如何设计第三方授权登录的用户表

<p><img src="https://img2018.cnblogs.com/blog/1204203/201909/1204203-20190908092004501-741806291.gif"></p>
<p>需求:在网站上想评论一篇文章,而评论文章是要用户注册与登录的,那么怎么免去这麻烦的步骤呢?答案是通过第三方授权登录。本文讲解的就是 github 授权登录的教程。</p>
<p>效果体验地址: http://biaochenxuying.cn</p>
<h1 id="1-github-第三方授权登录教程">1. github 第三方授权登录教程</h1>
<p>先来看下 github 授权的完整流程图 1:</p>
<p><img src="https://img2018.cnblogs.com/blog/1204203/201909/1204203-20190908092008993-281652774.png"></p>
<p>或者看下 github 授权的完整流程图 2:</p>
<p><img src="https://img2018.cnblogs.com/blog/1204203/201909/1204203-20190908092013239-659682341.png"></p>
<h2 id="11-申请一个-oauth-app">1.1 申请一个 OAuth App</h2>
<p><strong>首先我们必须登录上 github 申请一个 OAuth App,步骤如下:</strong></p>
<ol>
<li>登录 github</li>
<li>点击头像下的 Settings -&gt; Developer settings 右侧 New OAuth App</li>
<li>填写申请 app 的相关配置,重点配置项有2个</li>
<li>Homepage URL 这是后续需要使用授权的 URL ,你可以理解为就是你的项目根目录地址</li>
<li>Authorization callback URL 授权成功后的回调地址,这个至关重要,这是拿到授权 code 时给你的回调地址。</li>
</ol>
<p><strong>具体实践如下:</strong></p>
<ul>
<li>
<ol>
<li>首先登录你的 GitHub 账号,然后点击进入Settings。</li>
</ol>
</li>
</ul>
<p><img src="https://img2018.cnblogs.com/blog/1204203/201909/1204203-20190908092014674-997188764.png"></p>
<ul>
<li>
<ol start="2">
<li>点击 OAuth Apps , Register a new application 或者 New OAuth App 。</li>
</ol>
</li>
</ul>
<p><img src="https://img2018.cnblogs.com/blog/1204203/201909/1204203-20190908092016327-1664381180.png"></p>
<ul>
<li>
<ol start="3">
<li>输入信息。</li>
</ol>
</li>
</ul>
<p><img src="https://img2018.cnblogs.com/blog/1204203/201909/1204203-20190908092019094-1463462175.png"></p>
<ul>
<li>
<ol start="4">
<li>应用信息说明。</li>
</ol>
</li>
</ul>
<p><img src="https://img2018.cnblogs.com/blog/1204203/201909/1204203-20190908092020951-1317131689.png"></p>
<p>流程也可看 GitHub 设置的官方文档-Registering OAuth Apps。</p>
<h2 id="12-授权登录">1.2 授权登录</h2>
<p>github 文档:building-oauth-apps/authorizing-oauth-apps</p>
<p>授权登录的主要 3 个步骤:</p>
<ul>
<li>
<ol>
<li>web 端重定向 http://github.com/login/oauth/authorize</li>
</ol>
</li>
<li>
<ol start="2">
<li>根据 code 获取 access_token</li>
</ol>
</li>
<li>
<ol start="3">
<li>根据 access_token 获取用户信息</li>
</ol>
</li>
</ul>
<p>笔者这次实践中,项目是采用前后端分离的,所以第 1 步在前端实现,而第 2 步和第 3 步是在后端实现的,因为第 2 个接口里面需要Client_secret 这个参数,而且第 3 步获取的用户信息在后端保存到数据库。</p>
<h2 id="13-代码实现">1.3 代码实现</h2>
<h4 id="131-前端">1.3.1 前端</h4>
<p>笔者项目的技术是 react。</p>
<pre><code>// config.js

// ***** 处请填写你申请的 OAuth App 的真实内容
const config = {
'oauth_uri': 'https://github.com/login/oauth/authorize',
'redirect_uri': 'http://biaochenxuying.cn/',
'client_id': '*****',
'client_secret': '*******',
};

// 本地开发环境下
if (process.env.NODE_ENV === 'development') {
config.redirect_uri = "http://localhost:3001/"
config.client_id = "******"
config.client_secret = "*****"
}
export default config;
</code></pre>
<p>代码参考 config.js </p>
<p>redirect_uri 回调地址是分环境的,所以我是新建了两个 OAuth App 的,一个用于线上生产环境,一个用于本地开发环境。</p>
<p>一般来说,登录的页面应该是独立的,对应相应的路由 /login , 但是本项目的登录 login 组件是 nav 组件的子组件,nav 是个全局用的组件, 所以回调地址就写了 http://biaochenxuying.cn/。</p>
<ul>
<li>所以点击跳转是写在 login.js 里面;</li>
<li>授权完拿到 code 后,是写在 nav.js 里面</li>
<li>nav.js 拿到 code 值后去请求后端接口,后端接口返回用户信息。</li>
<li>其中后端拿到 code 还要去 github 取 access_token ,再根据 access_token 去取 github 取用户的信息。</li>
</ul>
<pre><code>// login.js

// html
&lt;Button
    style={{ width: '100%' }}
    onClick={this.handleOAuth} &gt;
      github 授权登录
&lt;/Button&gt;

// js
handleOAuth(){
    // 保存授权前的页面链接
    window.localStorage.preventHref = window.location.href
    // window.location.href = 'https://github.com/login/oauth/authorize?client_id=***&amp;redirect_uri=http://biaochenxuying.cn/'
    window.location.href = `${config.oauth_uri}?client_id=${config.client_id}&amp;redirect_uri=${config.redirect_uri}`
}
</code></pre>
<p>代码参考 login.js </p>
<pre><code>// nav.js

componentDidMount() {
    // console.log('code :', getQueryStringByName('code'));
    const code = getQueryStringByName('code')
    if (code) {
      this.setState(
      {
          code
      },
      () =&gt; {
          if (!this.state.code) {
            return;
          }
          this.getUser(this.state.code);
      },
      );
    }
}

componentWillReceiveProps(nextProps) {
    const code = getQueryStringByName('code')
    if (code) {
      this.setState(
      {
          code
      },
      () =&gt; {
          if (!this.state.code) {
            return;
          }
          this.getUser(this.state.code);
      },
      );
    }
}
getUser(code) {
    https
      .post(
      urls.getUser,
      {
          code,
      },
      { withCredentials: true },
      )
      .then(res =&gt; {
      // console.log('res :', res.data);
      if (res.status === 200 &amp;&amp; res.data.code === 0) {
          this.props.loginSuccess(res.data);
          let userInfo = {
            _id: res.data.data._id,
            name: res.data.data.name,
          };
          window.sessionStorage.userInfo = JSON.stringify(userInfo);
          message.success(res.data.message, 1);
          this.handleLoginCancel();
          // 跳转到之前授权前的页面
          const href = window.localStorage.preventHref
          if(href){
            window.location.href = href
          }
      } else {
          this.props.loginFailure(res.data.message);
          message.error(res.data.message, 1);
      }
      })
      .catch(err =&gt; {
      console.log(err);
      });
}
</code></pre>
<p>参考 nav.js </p>
<h4 id="132-后端">1.3.2 后端</h4>
<p>笔者项目的后端采用的技术是 node.js 和 express。</p>
<ul>
<li><strong>后端拿到前端传来的 code 后,还要去 github 取 access_token ,再根据 access_token 去取 github 取用户的信息。</strong></li>
<li><strong>然后把要用到的用户信息通过 注册 的方式保存到数据库,然后返回用户信息给前端。</strong></li>
</ul>
<pre><code>// app.config.js

exports.GITHUB = {
        oauth_uri: 'https://github.com/login/oauth/authorize',
        access_token_url: 'https://github.com/login/oauth/access_token',
        // 获取 github 用户信息 url // eg: https://api.github.com/user?access_token=******&amp;scope=&amp;token_type=bearer
        user_url: 'https://api.github.com/user',

        // 生产环境
    redirect_uri: 'http://biaochenxuying.cn/',
    client_id: '*****',
    client_secret: '*****',

        // // 开发环境
        // redirect_uri: "http://localhost:3001/",
    // client_id: "*****",
        // client_secret: "*****",
};
</code></pre>
<p>代码参考 app.config.js </p>
<pre><code>// 路由文件user.js

const fetch = require('node-fetch');
const CONFIG = require('../app.config.js');
const User = require('../models/user');

// 第三方授权登录的用户信息
exports.getUser = (req, res) =&gt; {
let { code } = req.body;
if (!code) {
    responseClient(res, 400, 2, 'code 缺失');
    return;
}
let path = CONFIG.GITHUB.access_token_url;
const params = {
    client_id: CONFIG.GITHUB.client_id,
    client_secret: CONFIG.GITHUB.client_secret,
    code: code,
};
// console.log(code);
fetch(path, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(params),
})
    .then(res1 =&gt; {
      return res1.text();
    })
    .then(body =&gt; {
      const args = body.split('&amp;');
      let arg = args.split('=');
      const access_token = arg;
      // console.log("body:",body);
      console.log('access_token:', access_token);
      return access_token;
    })
    .then(async token =&gt; {
      const url = CONFIG.GITHUB.user_url + '?access_token=' + token;
      console.log('url:', url);
      await fetch(url)
      .then(res2 =&gt; {
          console.log('res2 :', res2);
          return res2.json();
      })
      .then(response =&gt; {
          console.log('response ', response);
          if (response.id) {
            //验证用户是否已经在数据库中
            User.findOne({ github_id: response.id })
            .then(userInfo =&gt; {
                // console.log('userInfo :', userInfo);
                if (userInfo) {
                  //登录成功后设置session
                  req.session.userInfo = userInfo;
                  responseClient(res, 200, 0, '授权登录成功', userInfo);
                } else {
                  let obj = {
                  github_id: response.id,
                  email: response.email,
                  password: response.login,
                  type: 2,
                  avatar: response.avatar_url,
                  name: response.login,
                  location: response.location,
                  };
                  //注册到数据库
                  let user = new User(obj);
                  user.save().then(data =&gt; {
                  // console.log('data :', data);
                  req.session.userInfo = data;
                  responseClient(res, 200, 0, '授权登录成功', data);
                  });
                }
            })
            .catch(err =&gt; {
                responseClient(res);
                return;
            });
          } else {
            responseClient(res, 400, 1, '授权登录失败', response);
          }
      });
    })
    .catch(e =&gt; {
      console.log('e:', e);
    });
};
</code></pre>
<p>代码参考 user.js </p>
<p>至于拿到 github 的用户信息后,是注册到 user 表,还是保存到另外一张 oauth 映射表,这个得看自己项目的情况。</p>
<p>从 github 拿到的用户信息如下图:</p>
<p><img src="https://img2018.cnblogs.com/blog/1204203/201909/1204203-20190908092027589-27107441.png"></p>
<p>最终效果:</p>
<p><img src="https://img2018.cnblogs.com/blog/1204203/201909/1204203-20190908092040442-647364222.gif"></p>
<p>参与文章:</p>
<ol>
<li>
<p>https://www.jianshu.com/p/a9c0b277a3b3</p>
</li>
<li>
<p>https://blog.csdn.net/zhuming3834/article/details/77649960</p>
</li>
</ol>
<h1 id="2-如何设计第三方授权登录的用户表">2. 如何设计第三方授权登录的用户表</h1>
<p>第三方授权登录的时候,第三方的用户信息是存数据库原有的 user 表还是新建一张表呢 ?</p>
<p>答案:这得看具体项目了,做法多种,请看下文。</p>
<p><strong>第三方授权登录之后,第三方用户信息一般都会返回用户唯一的标志 openid 或者 unionid 或者 id,具体是什么得看第三方,比如 github 的是 id</strong></p>
<ul>
<li><strong>1. 直接通过 注册 的方式保存到数据库</strong></li>
</ul>
<p>第一种:如果网站 <strong>没有</strong> 注册功能的,直接通过第三方授权登录,授权成功之后,可以直接把第三的用户信息 <strong>注册</strong> 保存到自己数据库的 user 表里面。典型的例子就是 微信公众号的授权登录。</p>
<p>第二种:如果网站 <strong>有</strong> 注册功能的,也可以通过第三方授权登录,授权成功之后,也可以直接把第三的用户信息 <strong>注册</strong> 保存到自己数据库的 user 表里面(但是密码是后端自动生成的,用户也不知道,只能用第三方授权登录),这样子的第三方的用户和原生注册的用户信息都在同一张表了,这种情况得看自己项目的具体情况。笔者的博客网站暂时就采用了这种方式。</p>
<ul>
<li><strong>2. 增加映射表</strong></li>
</ul>
<p>现实中很多网站都有多种账户登录方式,比如可以用网站的注册 id 登录,还可以用手机号登录,可以用 QQ 登录等等。数据库中都是有映射关系,QQ、手机号等都是映射在网站的注册 id 上。保证不管用什么方式登录,只要去查映射关系,发现是映射在网站注册的哪个 id 上,就让哪个 id 登录成功。</p>
<ul>
<li><strong>3. 建立一个 oauth 表,一个 id 列,记录对应的用户注册表的 id</strong></li>
</ul>
<p>建立一个 oauth 表,一个 id 列,记录对应的用户注册表的 id,然后你有多少个第三方登陆功能,你就建立多少列,记录第三方登陆接口返回的 openid;第三方登陆的时候,通过这个表的记录的 openid 获取 id 信息,如果存在通过 id 读取注册表然后用 session 记录相关信息。不存在就转向用户登陆/注册界面要用户输入本站注册的账户进行 openid 绑定或者新注册账户信息进行绑定。</p>
<p>具体代码实践请参考文章:</p>
<p>1. 第三方登录用户信息表设计</p>
<p>2.浅谈数据库用户表结构设计,第三方登录</p>
<h1 id="4-最后">4. 最后</h1>
<blockquote>
<p>笔者的 github 博客地址:github</p>
</blockquote>
<p>如果您觉得这篇文章不错或者对你有所帮助,请给个赞或者星呗,你的点赞就是我继续创作的最大动力。</p><br><br>
来源:https://www.cnblogs.com/biaochenxuying/p/11484350.html
頁: [1]
查看完整版本: github 授权登录教程与如何设计第三方授权登录的用户表