oauth2.0授权登录

149 阅读2分钟

什么是 oauth2.0

OAuth 不是一个 API 或者服务, 而是一个验证授权(Authorization)的开放标准,所有人都有基于这个标准实现自己的 OAuth。 更具体来说,OAuth 是一个标准,app 可以用来实现 secure delegated access. OAuth 基于 HTTPS,以及 APIs,Service 应用使用 access token 来进行身份验证。 OAuth 主要有 OAuth 1.0a 和 OAuth 2.0 两个版本,并且二者完全不同,且不兼容。OAuth2.0 是目前广泛使用的版本,我们多数谈论 OAuth 时,为 OAuth2.0。 更多的话我推荐 阮一峰 写的文章 oauth2.0

实现 oauth2.0 授权登录

其实 oauth 登录方式有四种,常用就是授权登录,我这里用 gitee 第三方登录举例 先要去 gitee 上创建第三方应用登录,网址 gitee-api

oauth基本流程.jpg

上边的这个图主要东西就是client_id和client_secret,这两个东西你创建完应用就会有 client_id用来获取code,code和client_secret用来获取access_token 当然还有回调地址 redirect_uri 也很重要。主要步骤如下:
  1. 调用第三方服务器接口来获取code,用get请求,参数主要是client_id和redirect_uri。client_id需要创建第三方应用登录才会有
  2. 第三方服务器调用redirect_uri并返回code
  3. 接着去调用第三方服务器接口来获取access_token,post请求,参数主要是client_id,client_secret。 client_id和client_secret需要创建第三方应用登录才会有
  4. 有了access_token后就可以调用第三方服务器接口获取用户的个人信息

要仔细地查看第三方服务器接口的文档,gitee 的话 -> gitee

代码演示(next.js)

  1. 组件调用第三方接口
const handleOAuthGitee = () => {
  const { ClientID, RedirectUri } = giteeLoginInfo
  window.open(
    `https://gitee.com/oauth/authorize?client_id=${ClientID}&redirect_uri=${RedirectUri}&response_type=code`
  )
}
  1. 自己的服务器调用接口
import type { NextApiRequest, NextApiResponse } from 'next'
import { withIronSessionApiRoute } from 'iron-session/next'
// import {getIronSession} from 'iron-session'
import { ISession, ResponseData } from 'pages/api/index'
import { ironOptions } from 'config'
import { prepareConnection } from 'db/index'
import { User, UserAuth } from 'db/entity/index'
import { Cookie } from 'next-cookie'
import { setCookie } from 'utils/index'
import { giteeLoginInfo } from 'config/index'
import request from 'service/fetch'

async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  const session: ISession = req.session
  //http://localhost:3003/api/oauth/gitee
  const cookies = Cookie.fromApiRoute(req, res)
  const { code, error } = req.query
  if (!code && error === 'access_denied') {
    res.redirect(302, '/')
    return
  }
  // 自己的git登录时需要的一些参数配置
  const { ClientID, ClientSecrets, RedirectUri } = giteeLoginInfo
  // 调用第三方接口获取access_token
  try {
    const url = `https://gitee.com/oauth/token?grant_type=authorization_code&code=${code}&client_id=${ClientID}&redirect_uri=${RedirectUri}`
    const result = await request({
      url,
      method: 'post',
      data: { client_secret: ClientSecrets },
    }).catch((err) => {
      throw new Error(err.message)
    })
    const { access_token } = result as any
    const giteeUserInfo = await request({
      url: 'https://gitee.com/api/v5/user',
      params: { access_token },
    })
    const db = await prepareConnection()
    const userAuth = await db.getRepository(UserAuth).findOne(
      {
        identity_type: 'gitee',
        identifier: ClientID,
      },
      { relations: ['user'] }
    )
    if (userAuth) {
      //之前用gitee登录过
      const user = userAuth.user
      const { id, nickname, avatar } = user
      //更新credential
      try {
        await db
          .getRepository(UserAuth)
          .update({ identifier: ClientID }, { credential: access_token })
      } catch (error) {
        console.log('error', error)
      }
      session.userId = id
      session.nickname = nickname
      session.avatar = avatar
      await session.save()
      setCookie(cookies, { userId: id, nickname, avatar })
      res.redirect(302, '/')
    } else {
      //新用户
      const { login = '', avatar_url = '' } = giteeUserInfo as any
      const user = new User()
      user.nickname = login
      user.avatar = avatar_url

      const userAuth = new UserAuth()
      userAuth.identity_type = 'gitee'
      userAuth.identifier = ClientID
      userAuth.credential = access_token
      userAuth.user = user

      const userAuthRepo = db.getRepository(UserAuth)
      const resUserAuth = await userAuthRepo.save(userAuth)

      const { id, nickname, avatar } = resUserAuth.user
      session.userId = id
      session.nickname = nickname
      session.avatar = avatar
      await session.save()

      setCookie(cookies, { userId: id, nickname, avatar })

      res.redirect(302, '/')
    }
  } catch (error) {
    console.log('error', error)
  }
}

export default withIronSessionApiRoute(handler, ironOptions)