zoukankan      html  css  js  c++  java
  • github 授权登录教程与如何设计第三方授权登录的用户表

    效果图

    需求:在网站上想评论一篇文章,而评论文章是要用户注册与登录的,那么怎么免去这麻烦的步骤呢?答案是通过第三方授权登录。本文讲解的就是 github 授权登录的教程。

    效果体验地址: http://biaochenxuying.cn

    1. github 第三方授权登录教程

    先来看下 github 授权的完整流程图 1:

    github 1

    或者看下 github 授权的完整流程图 2:

    github 2

    1.1 申请一个 OAuth App

    首先我们必须登录上 github 申请一个 OAuth App,步骤如下:

    1. 登录 github
    2. 点击头像下的 Settings -> Developer settings 右侧 New OAuth App
    3. 填写申请 app 的相关配置,重点配置项有2个
    4. Homepage URL 这是后续需要使用授权的 URL ,你可以理解为就是你的项目根目录地址
    5. Authorization callback URL 授权成功后的回调地址,这个至关重要,这是拿到授权 code 时给你的回调地址。

    具体实践如下:

      1. 首先登录你的 GitHub 账号,然后点击进入Settings。

      1. 点击 OAuth Apps , Register a new application 或者 New OAuth App 。

      1. 输入信息。

    image.png

      1. 应用信息说明。

    流程也可看 GitHub 设置的官方文档-Registering OAuth Apps

    1.2 授权登录

    github 文档:building-oauth-apps/authorizing-oauth-apps

    授权登录的主要 3 个步骤:

    笔者这次实践中,项目是采用前后端分离的,所以第 1 步在前端实现,而第 2 步和第 3 步是在后端实现的,因为第 2 个接口里面需要Client_secret 这个参数,而且第 3 步获取的用户信息在后端保存到数据库。

    1.3 代码实现

    1.3.1 前端

    笔者项目的技术是 react。

    // 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; 
    

    代码参考 config.js

    redirect_uri 回调地址是分环境的,所以我是新建了两个 OAuth App 的,一个用于线上生产环境,一个用于本地开发环境。

    一般来说,登录的页面应该是独立的,对应相应的路由 /login , 但是本项目的登录 login 组件是 nav 组件的子组件,nav 是个全局用的组件, 所以回调地址就写了 http://biaochenxuying.cn/。

    • 所以点击跳转是写在 login.js 里面;
    • 授权完拿到 code 后,是写在 nav.js 里面
    • nav.js 拿到 code 值后去请求后端接口,后端接口返回用户信息。
    • 其中后端拿到 code 还要去 github 取 access_token ,再根据 access_token 去取 github 取用户的信息。
    // login.js
    
    // html
    <Button
        style={{  '100%' }}
        onClick={this.handleOAuth} >
          github 授权登录
    </Button>
    
    // js
    handleOAuth(){
        // 保存授权前的页面链接
        window.localStorage.preventHref = window.location.href
        // window.location.href = 'https://github.com/login/oauth/authorize?client_id=***&redirect_uri=http://biaochenxuying.cn/'
        window.location.href = `${config.oauth_uri}?client_id=${config.client_id}&redirect_uri=${config.redirect_uri}`
    }
    

    代码参考 login.js

    // nav.js
    
    componentDidMount() {
        // console.log('code :', getQueryStringByName('code'));
        const code = getQueryStringByName('code')
        if (code) {
          this.setState(
            {
              code
            },
            () => {
              if (!this.state.code) {
                return;
              }
              this.getUser(this.state.code);
            },
          );
        }
      }
    
    componentWillReceiveProps(nextProps) {
        const code = getQueryStringByName('code')
        if (code) {
          this.setState(
            {
              code
            },
            () => {
              if (!this.state.code) {
                return;
              }
              this.getUser(this.state.code);
            },
          );
        }
      }
      getUser(code) {
        https
          .post(
            urls.getUser,
            {
              code,
            },
            { withCredentials: true },
          )
          .then(res => {
            // console.log('res :', res.data);
            if (res.status === 200 && 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 => {
            console.log(err);
          });
      }
    

    参考 nav.js

    1.3.2 后端

    笔者项目的后端采用的技术是 node.js 和 express。

    • 后端拿到前端传来的 code 后,还要去 github 取 access_token ,再根据 access_token 去取 github 取用户的信息。
    • 然后把要用到的用户信息通过 注册 的方式保存到数据库,然后返回用户信息给前端。
    // 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=******&scope=&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: "*****",
    };
    

    代码参考 app.config.js

    // 路由文件  user.js
    
    const fetch = require('node-fetch');
    const CONFIG = require('../app.config.js');
    const User = require('../models/user');
    
    // 第三方授权登录的用户信息
    exports.getUser = (req, res) => {
      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 => {
          return res1.text();
        })
        .then(body => {
          const args = body.split('&');
          let arg = args[0].split('=');
          const access_token = arg[1];
          // console.log("body:",body);
          console.log('access_token:', access_token);
          return access_token;
        })
        .then(async token => {
          const url = CONFIG.GITHUB.user_url + '?access_token=' + token;
          console.log('url:', url);
          await fetch(url)
            .then(res2 => {
              console.log('res2 :', res2);
              return res2.json();
            })
            .then(response => {
              console.log('response ', response);
              if (response.id) {
                //验证用户是否已经在数据库中
                User.findOne({ github_id: response.id })
                  .then(userInfo => {
                    // 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 => {
                        // console.log('data :', data);
                        req.session.userInfo = data;
                        responseClient(res, 200, 0, '授权登录成功', data);
                      });
                    }
                  })
                  .catch(err => {
                    responseClient(res);
                    return;
                  });
              } else {
                responseClient(res, 400, 1, '授权登录失败', response);
              }
            });
        })
        .catch(e => {
          console.log('e:', e);
        });
    };
    

    代码参考 user.js

    至于拿到 github 的用户信息后,是注册到 user 表,还是保存到另外一张 oauth 映射表,这个得看自己项目的情况。

    从 github 拿到的用户信息如下图:

    github-login.png

    最终效果:

    github-logining.gif

    参与文章:

    1. https://www.jianshu.com/p/a9c0b277a3b3

    2. https://blog.csdn.net/zhuming3834/article/details/77649960

    2. 如何设计第三方授权登录的用户表

    第三方授权登录的时候,第三方的用户信息是存数据库原有的 user 表还是新建一张表呢 ?

    答案:这得看具体项目了,做法多种,请看下文。

    第三方授权登录之后,第三方用户信息一般都会返回用户唯一的标志 openid 或者 unionid 或者 id,具体是什么得看第三方,比如 github 的是 id

    • 1. 直接通过 注册 的方式保存到数据库

    第一种:如果网站 没有 注册功能的,直接通过第三方授权登录,授权成功之后,可以直接把第三的用户信息 注册 保存到自己数据库的 user 表里面。典型的例子就是 微信公众号的授权登录。

    第二种:如果网站 注册功能的,也可以通过第三方授权登录,授权成功之后,也可以直接把第三的用户信息 注册 保存到自己数据库的 user 表里面(但是密码是后端自动生成的,用户也不知道,只能用第三方授权登录),这样子的第三方的用户和原生注册的用户信息都在同一张表了,这种情况得看自己项目的具体情况。笔者的博客网站暂时就采用了这种方式。

    • 2. 增加映射表

    现实中很多网站都有多种账户登录方式,比如可以用网站的注册 id 登录,还可以用手机号登录,可以用 QQ 登录等等。数据库中都是有映射关系,QQ、手机号等都是映射在网站的注册 id 上。保证不管用什么方式登录,只要去查映射关系,发现是映射在网站注册的哪个 id 上,就让哪个 id 登录成功。

    • 3. 建立一个 oauth 表,一个 id 列,记录对应的用户注册表的 id

    建立一个 oauth 表,一个 id 列,记录对应的用户注册表的 id,然后你有多少个第三方登陆功能,你就建立多少列,记录第三方登陆接口返回的 openid;第三方登陆的时候,通过这个表的记录的 openid 获取 id 信息,如果存在通过 id 读取注册表然后用 session 记录相关信息。不存在就转向用户登陆/注册界面要用户输入本站注册的账户进行 openid 绑定或者新注册账户信息进行绑定。

    具体代码实践请参考文章:

    1. 第三方登录用户信息表设计

    2. 浅谈数据库用户表结构设计,第三方登录

    4. 最后

    笔者的 github 博客地址:github

    如果您觉得这篇文章不错或者对你有所帮助,请给个赞或者星呗,你的点赞就是我继续创作的最大动力。

  • 相关阅读:
    ssh 免密
    SCALA XML pattern attrbute(属性)
    数据库分区的概念
    Intellij IDEA 快捷键整理
    笔记--Linux
    netstat
    笔记--MySQL相关操作
    ip地址
    使用ASMCMD管理Oracle ASM
    使用RMAN执行Oracle ASM数据迁移
  • 原文地址:https://www.cnblogs.com/biaochenxuying/p/11484350.html
Copyright © 2011-2022 走看看