首页 > 其他分享 >前后端分离OAuth授权登录实现方式

前后端分离OAuth授权登录实现方式

时间:2023-02-17 14:33:40浏览次数:43  
标签:code const 登录 access oauth token OAuth 授权

以码云作为 OAuth 的演示,码云 OAuth 的验证流程如下图:

为了演示,需要先在码云上申请一个应用。

  1. 在 修改资料 -> 第三方应用,创建要接入码云的应用。

  2. 填写应用相关信息,勾选应用所需要的权限。其中: 回调地址是用户授权后,码云回调到应用,并且回传授权码的地址。

  3. 创建成功后,会生成 Cliend ID 和 Client Secret。他们将会在上述 OAuth2 认证基本流程用到。

应用创建成功后,就可以进入编码了。以 egg.js 作为后端的实现。

1、安装

$ mkdir oauth && cd oauth
$ npm init egg --type=ts
$ npm i
$ npm run dev

2、全局配置 gitee 参数

修改 oauth/config/config.default.ts,在其中添加一个 gitee 的配置,并扩展到 bizConfig。

const giteeOauthConfig = {
  client_id: "510c2a54e0c942bc13b5ca8e88d4048f46af9a5535f",
  client_secret: "2c64bcefcd37f10ff99f5cc84bc9e6739",
  redirect_uri: "http://localhost:7001/api/users/passport/gitee/callback",
  authURL: "https://gitee.com/oauth/token?grant_type=authorization_code",
  giteeUserAPI: "https://gitee.com/api/v5/user",
};

const bizConfig = {
  giteeOauthConfig,
};

3、编写 controller 方法

在 oauth/app/controller 目录下添加一个 user.ts,在其中实现两个方法 oauth,oauthByGitee,分别处理授权链接的跳转与 code 换取 token 的操作。

import { Controller } from 'egg';

export default class UserController extends Controller {
  public async oauth() {

  }
  public async oauthByGitee() {

  }
}

同时在 oauth/app/router.ts 中添加对应对控制器路由

import { Application } from "egg";

export default (app: Application) => {
  const { controller, router } = app;

  router.get("/", controller.home.index);

  // 用户模块
  router.get("/api/users/passport/gitee", controller.user.oauth);
  router.get(
    "/api/users/passport/gitee/callback",
    controller.user.oauthByGitee
  );
};

4、oauth 方法实现

oauth 方法中主要处理应用授权链接的跳转,跳转时需要传递应用 ID 与回调地址。

public async oauth() {
  const { ctx, app } = this;
  const { client_id, redirect_uri } = app.config.giteeOauthConfig;
  const url = `https://gitee.com/oauth/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}&response_type=code`;
  ctx.redirect(url);
}

从 app.config.giteeOauthConfig 获取对应的配置,然后拼接成授权链接,并使用 egg 提供的 ctx.redirect 方法进行重定向跳转。当前端访问授权接口时,服务端重定向到第三方授权服务器页面

用户点击同意授权,第三方授权服务器会根据传过去的 redirect_uri 参数进行重定向跳转,并携带一个 code。在前面配置了 redirect_uri,根据路由配置重定向后就进入到了 oauthByGitee 方法中。添加 oauthByGitee 的实现:

public async oauthByGitee() {
  const { ctx } = this;
  const { code } = ctx.query;
  try {
    const result = await ctx.service.user.loginByGitee(code);
    ctx.body = result;
  } catch (error) {
    console.log(error);
  }
}

oauthByGitee 中通过 code,获取了访问 token,服务端根据获取到的 token 向码云获取了对应的用户信息,对应的 service 代码如下:

import { Service } from 'egg';

/**
 * Test Service
 */
export default class User extends Service {


  /**
     * 通过用户授权码从码云获取对应用户信息
     * @param code 用户授权码
     */
  public async getAccessToken(code:string) {
    const { ctx, app } = this;
    const { client_id, redirect_uri, client_secret, authURL } = app.config.giteeOauthConfig;
    const access_token = await ctx.curl(authURL, {
      method: 'POST',
      contentType: 'json',
      dataType: 'json',
      data: {
        code,
        client_id,
        redirect_uri,
        client_secret,
      },
    });
    return access_token;
  }

  public async getGiteeUserData(access_token: string) {
    const { ctx, app } = this;
    const { giteeUserAPI } = app.config.giteeOauthConfig;
    const { data } = await ctx.curl(`${giteeUserAPI}?access_token=${access_token}`, {
      dataType: 'json',
    });
    return data;
  }

  public async loginByGitee(code: string) {
    // 获取access_token
    const access_token = await this.getAccessToken(code);
    // 获取用户信息
    const user = await this.getGiteeUserData(access_token.data.access_token);
    return user;
  }
}

在 loginByGitee,获取了 access_token,并根据 access_token 获取了用户信息。此时 oauth 授权登录就完成了一半,也就是只完成了授权,登录还没实现。从第三方拿到了用户信息,此时就可以根据这些信息来完成登录的操作。

5、前后端分离授权方式

目前市面上的应用基本是前后端分离的,通常前后端是分开部署的,此时就会有一个跨源通信的问题。前面已经根据第三方提供的信息生成了自己的用户 token,那么在前后端分离的情况下怎么将 token 传给前端呢?其实浏览器已经给提供了方法,可以通过 postMessage 来实现跨源通信。

前端通过 window.open 打开授权链接,用户同意授权后,跳转到回调页面。后端拿到 token 后自己渲染一个页面提示授权成功,渲染的页面逻辑:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>授权成功</title>
  </head>
  <body>
    <h1>授权 成功</h1>
    <h2>两秒后关闭</h2>
  </body>
  <script>
    window.onload = function () {
      setTimeout(() => {
        window.opener.postMessage("{{token}}", "http://127.0.0.1:5173/");
        window.close();
      }, 2000);
    };
  </script>
</html>

这时候前端通过 addEventListener 就可以监听到对应事件,并接收对应参数了,这样就授权成功了。

标签:code,const,登录,access,oauth,token,OAuth,授权
From: https://www.cnblogs.com/wp-leonard/p/17130006.html

相关文章

  • mercadolibre 美卡多 本土授权(PKCE)
    API接口文档: 身份验证和授权(mercadolibre.com.mx) https://developers.mercadolibre.com.mx/es_ar/autenticacion-y-autorizacion//***生成授权链接*......
  • 7. 联合登录和信任登录
    7.1什么是联合登陆联合登录指同时包含多种凭证校验的登录服务,同时,也可以理解为使用第三方凭证进行校验的登录服务。通俗点讲:对于两个网站A和B,在登录A网站的时候......
  • 6. OAuth 2.0
    在实际浏览网站的时候,当登录的时候除了输入当前网站的账号密码外,还发现可以通过第三方的QQ或者微信登录,那么这又是如何做到了呢,这就要谈到OAuth了。OAuth协议又有1......
  • 5. 单点登录(Single Sign On)
    前面已经知道了,在同域下的客户端/服务端认证系统中,通过客户端携带凭证,可以维持一段时间内的登录状态。但随着企业的发展,一个大型系统里可能包含n多子系统,用户在操作不同......
  • MySQL数据库的几种登录方式
    1234567891011前提:连接进入mysql数据库本机安装的myslq基础信息:host="localhost",数据库主机地址:127.0.0.1port=3306,端口号user="root",myslql数据库......
  • 退出登录操作
    asynclogout(){try{awaitthis.$confirm("确定退出吗?");}catch(error){returnconsole.log(error);}//要退出......
  • 2- 用户登录表单拦截 UsernamePasswordAuthenticationFilter
    /**Copyright2004,2005,2006AcegiTechnologyPtyLimited**LicensedundertheApacheLicense,Version2.0(the"License");*youmaynotusethisf......
  • Docker客户端登录启用了HTTPS的Harbor要注意的事项
    首先在Harbor将要部署的主机上创建Harbor专用的证书目录:mkdir-p/data/harbor/certs/cd/data/harbor/certs生成CA证书的私钥opensslgenrsa-outca.key409......
  • ssh-copy-id三步实现SSH无密码登录和ssh常用命令
    原文地址https://blog.csdn.net/liu_qingbo/article/details/78383892ssh-copy-id-p22-i~/.ssh/id_rsa.pubroot@远程ipssh-p'22''root@远程ip'产生如下代码,......
  • 配置ssh登录远程机子
    本地准备ssh公钥查看本地是否有sshls-al~/.ssh本地生成sshssh-keygen-trsa-C自己的Email地址查看ssh公钥cat.ssh/id_rsa.pub远程安装ssh服务查看......