以码云作为 OAuth 的演示,码云 OAuth 的验证流程如下图:
为了演示,需要先在码云上申请一个应用。
-
在 修改资料 -> 第三方应用,创建要接入码云的应用。
-
填写应用相关信息,勾选应用所需要的权限。其中: 回调地址是用户授权后,码云回调到应用,并且回传授权码的地址。
-
创建成功后,会生成 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