Github OAuth第三方接入

认证和受权

认证(Authentication)

  所谓认证就是如何证实你是你本身的方式,通常来讲证实你是你本身的方式就是经过身份证;而互联网中的认证一般用用户名和密码来证实你是你本身。
  经常使用的身份认证的方式通常有:javascript

  1. 用户名和密码登录
  2. 邮箱发送登录连接
  3. 手机号接收验证码

受权(Authorization)

  所谓受权举个例子就是:你在安装手机应用的时候,应用会申请权限,你赞成这个权限申请的过程就叫作受权。同理,在oauth中,被受权方会收获必定的权限。一般使用oauth的厂商有:java

  1. qq
  2. 微信
  3. 微博
  4. 脸书
  5. 推特

受权和认证的关系

  受权并不必定须要认证。好比:当你有钥匙的时候,你就能够开门,你并不必定是这个屋子的主人。
  在互联网中的oauth受权一般来讲是经过token进行权限的授予的。ios

OAuth

OAuth的概念

  OAuth是互联网行业一种标准的受权方式。各个公司根据这一套标准实现本身的OAuth认证和受权流程,而第三方想要接入这个流程,就须要使用OAuth这套方案。
  OAuth目前有两个版本:git

  1. 1.x(已弃用)
  2. 2.x(流行)

角色理解

  OAuth第三方认证和受权流程中通常会有几个角色:github

  1. 客户端:浏览器端
  2. 服务端:开发这个网站对应的服务器端
  3. 受权服务器:提供OAuth第三方接入服务的提供商,好比QQ、微信等。

  另一种说法说的是客户端和服务端能够合并称为被受权方,若是你作的是个纯客户端的OAuth接入的话,那么你也能够不须要服务端,不过这个接入方式安全性不高。redis

受权方式

  目前业界的OAuth受权方式有多种,其中最主要的是如下几种:数据库

  1. Authorization Code(最经常使用的)
  2. Refresh Token:在受权Token未过时时,向OAuth接入方从新申请一个新的受权Token
  3. Device Code:主要针对智能TV中的第三方接入。
  4. Password:用OAuth提供方暴露的API,进行本网站的用户名和密码登录。(不推荐,信任和安全,通常用于公司内部的登录方式)
  5. Implicit:已废弃
  6. Client Credentials

Authorization Code

  经过认证码的认证受权方式进行OAuth的接入时,主要流程分别是如下几步:json

  1. 客户端发起请求redirect到OAuth接入方并附带上client_id
  2. 用户在redirect以后的网站上输入用户名和密码
  3. 登录成功以后,OAuth接入方会返回给服务端一个code
  4. 服务端拿到code以后,拿着client_secretcode向OAuth接入方申请得到Token
  5. 服务端拿到Token以后,进入受权窗口
  6. 受权成功,跳转到客户端网站。

Github OAuth第三方接入

注册Github OAuth

url:https://github.com/settings/appsaxios

  1. 登录github
  2. 点击头像、setting
  3. 点击developer setting
  4. 新建OAuth App

  注意:HomePage URL在项目未上线以前填本地项目根路径地址,在项目上线以后填上线以后的域名地址;同理Authorization callback URL在上线以前填写http://localhost:3000/auth,在上线以后填写域名+/auth
  注册成功以后,会出现一个client_id和client_secret,这个两个值很是重要。须要保存在项目文件之中api

OAuth字段详解

跳转字段

url: https://github.com/login/oauth/authorize
param

  1. client_id:client_id是注册时的client_id
  2. scope:但愿获得的权限范围
  3. redirect_uri:受权后重定向到项目的地址,必须和注册Github OAuth App时的redirect uri相同
  4. login:用于登陆和受权应用程序的特定账户,若是你在该网站登录过github并受权,则在此请求OAuth时免去登录步骤。
  5. state:不可猜想的随机字符串。它用于防止跨站点请求伪造攻击。
  6. allow_signup:在OAuth流程中,是否向未认证的用户提供注册GitHub的选项。默认值为true。false在策略禁止注册的状况下使用。

请求token

url: https://github.com/login/oauth/access_token
param

  1. client_id:必需,标明token使用方
  2. client_secret:必需,标明token使用方
  3. code:必需,表明用户已受权
  4. redirect_uri:同上
  5. state:同上

请求用户信息

url: https://api.github.com/user
header

  1. Authorization:即请求access_token时返回的access_token,该字段统一是token +access_token

Github OAuth受权如何保证安全

策略

  1. 一次性code:即受权成功时返回的code是一次性的,请求access_token后,上一次的code已失效。
  2. id + secret验证方式:请求access_token时,不只须要code,还须要client_id和client_secret,就算code已泄露,只要对方没有这两个字段那他就没法获取我方的access_token。
  3. redirect_uri:这个字段是位于请求时返回的uri,若是这个字段与github的Authorization callback URL不一样时,就会报错。

使用cookie和session存储token

  当用户进行完了OAuth流程以后,将token保存于cookie和session中,这样用户就不须要每次切换页面时都去进行一次OAuth认证和受权。

存储cookie

koa框架存储cookie的方式:

server.use(async (ctx, next) => {
  ctx.cookies.set('id', id, {
    httpOnly: true
  });
  await next()
})
复制代码

存储session

const session = require('koa-session');
// 给cookie加密,加密以后的密文是jwt
server.keys = ['ainuo develop github apps'];
const SESSION_CONFIG = {
  // key为存储的字段名
  key: 'jid',
   // 当没有配置store时,session是以jwt存储在cookie中的
   // store:
};
// 使用session中间件
server.use(session(SESSION_CONFIG, server));
// 通常来讲咱们会单独暴露一个接口用于设置session。
router.get('/set/user', async ctx => {
    // 设置session
  ctx.session.user = {
    name: 'ainuo',
    age: 18
  };
  ctx.body = 'set session success'
});
复制代码

将token存储到redis

建立Store对象

  建立这个对象的目的是集成redis到kos-session,并简化redis的相关操做

function getRedisSessionId(sessionId) {
  return `ssid:${sessionId}`
}

module.exports = class RedisSessionStore {
  constructor(client) {
    this.client = client
  }

  // 根据sessionId获取redis中存储的session数据
  async get(sessionId) {
    console.log('get session', sessionId);
    const id = getRedisSessionId(sessionId);
    const data = await this.client.get(id);
    if (!data) {
      return null
    }
    try {
      return JSON.parse(data);
    } catch (e) {
      console.log(e)
    }
  }

  // 存储session数据到redis
  async set(sessionId, sessionValue, lifetime) {
    console.log('set session', sessionId);
    const id = getRedisSessionId(sessionId);
    if (typeof lifetime === "number") {
      lifetime = Math.ceil(lifetime / 1000);
    }
    try {
      const sessionStr = JSON.stringify(sessionValue);
      if (lifetime) {
        await this.client.setex(id, lifetime, sessionStr)
      } else {
        await this.client.set(id, sessionStr)
      }
    } catch (e) {
      console.log(e)
    }
  }

  // 从redis中删除某个session
  async destroy(sessionId) {
    console.log('destroy session', sessionId);
    const id = getRedisSessionId(sessionId);
    await this.client.del(id)
  }
}
复制代码

koa-session链接redis数据库

const Redis = require('ioredis');
// 能够传入一些配置
const client = new Redis({});
server.keys = ['ainuo develop github apps'];
const SESSION_CONFIG = {
  key: 'jid',
    // 当没有配置store时,session是以jwt存储在cookie中的
   store: new RedisSessionStore(client),
   // maxAge: 10 * 1000,
};
// 对服务端的session进行加密
server.use(session(SESSION_CONFIG, server));
// 删除session时删除redis数据库中的ssid:xxx的数据
router.get('/del/user', async ctx => {
    // 设置session
    ctx.session = null;
    ctx.body = 'del session success'
});
// 设置session时添加session到redis数据库
router.get('/set/user', async ctx => {
  // 设置session
  ctx.session.user = {
    name: 'ainuo',
    age: 18
  };
  ctx.body = 'set session success'
});
复制代码

koa接入Github OAuth

//config,js
module.exports = {
  github: {
    client_id: 'xxxx',
    client_secret: 'xxxx',
    request_token_url: 'https://github.com/login/oauth/access_token',
    auth_url: 'https://github.com/login/oauth/authorize',
    scope: 'user',
    user_info_url: 'https://api.github.com/user'
  }
};


// auth.js
const axios = require('axios');
const {github} = require('../config');
const {client_id, client_secret, request_token_url} = github;
module.exports = function (server) {
  server.use(async (ctx, next) => {
    if (ctx.path === '/auth') {
      const code = ctx.query.code;
      if (code) {
        // 发起对access_token的请求
        const res = await axios({
          method: 'POST',
          url: request_token_url,
          data: {
            client_id,
            client_secret,
            code
          },
          headers: {
            Accept: 'application/json',
          }
        });
        if (res.status === 200 && !(res.data && res.data.error)) {
          ctx.session.githubAuth = res.data;
          const {access_token, token_type} = res.data;
          // 请求获得用户信息
          const userInfoRes = await axios({
            method: 'GET',
            url: github.user_info_url,
            headers: {
              Authorization: `token ${access_token}`
            }
          });
          // 存储用户信息到session和redis
          ctx.session.userInfo = userInfoRes.data;
          // 受权等流程结束以后,重定向到首页
          ctx.redirect('/');
        } else {
          const errorMsg = res.data && res.data.error;
          ctx.body = `request token failed ${errorMsg}`
        }
      } else {
        ctx.body = 'code not exist';
      }
    } else {
      await next()
    }
  })
};

// server.js
// 对服务端的session进行加密
server.use(session(SESSION_CONFIG, server));
// 配置处理github oauth登录
auth(server);
复制代码

项目地址

相关文章
相关标签/搜索