mirror of
https://github.com/certd/certd.git
synced 2026-04-24 04:17:25 +08:00
perf: 支持微信扫码登录
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { addonRegistry, BaseController, Constants, SysInstallInfo, SysSettingsService } from "@certd/lib-server";
|
||||
import { addonRegistry, AddonService, BaseController, Constants, SysInstallInfo, SysSettingsService } from "@certd/lib-server";
|
||||
import { ALL, Body, Controller, Get, Inject, Param, Post, Provide, Query } from "@midwayjs/core";
|
||||
import { AddonGetterService } from "../../../modules/pipeline/service/addon-getter-service.js";
|
||||
import { IOauthProvider } from "../../../plugins/plugin-oauth/api.js";
|
||||
@@ -31,6 +31,9 @@ export class ConnectController extends BaseController {
|
||||
@Inject()
|
||||
oauthBoundService: OauthBoundService;
|
||||
|
||||
@Inject()
|
||||
addonService: AddonService;
|
||||
|
||||
|
||||
|
||||
private async getOauthProvider(type: string) {
|
||||
@@ -51,14 +54,14 @@ export class ConnectController extends BaseController {
|
||||
}
|
||||
|
||||
@Post('/login', { summary: Constants.per.guest })
|
||||
public async login(@Body(ALL) body: { type: string, forType?:string }) {
|
||||
public async login(@Body(ALL) body: { type: string, forType?:string ,from?:string }) {
|
||||
|
||||
const addon = await this.getOauthProvider(body.type);
|
||||
const installInfo = await this.sysSettingsService.getSetting<SysInstallInfo>(SysInstallInfo);
|
||||
const bindUrl = installInfo?.bindUrl || "";
|
||||
//构造登录url
|
||||
const redirectUrl = `${bindUrl}api/oauth/callback/${body.type}`;
|
||||
const { loginUrl, ticketValue } = await addon.buildLoginUrl({ redirectUri: redirectUrl, forType: body.forType });
|
||||
const { loginUrl, ticketValue } = await addon.buildLoginUrl({ redirectUri: redirectUrl, forType: body.forType ,from: body.from || "web" });
|
||||
const ticket = this.codeService.setValidationValue(ticketValue)
|
||||
this.ctx.cookies.set("oauth_ticket", ticket, {
|
||||
httpOnly: true,
|
||||
@@ -217,10 +220,32 @@ export class ConnectController extends BaseController {
|
||||
return this.ok(bounds);
|
||||
}
|
||||
|
||||
@Post('/providers', { summary: Constants.per.guest })
|
||||
|
||||
@Post('/providers', { summary: Constants.per.guest })
|
||||
public async providers() {
|
||||
const list = addonRegistry.getDefineList("oauth");
|
||||
const defineList = addonRegistry.getDefineList("oauth");
|
||||
|
||||
const publicSetting = await this.sysSettingsService.getPublicSettings();
|
||||
const oauthProviders = publicSetting.oauthProviders || {};
|
||||
const list = [];
|
||||
|
||||
for (const item of defineList) {
|
||||
const type = item.name
|
||||
const conf = oauthProviders[type];
|
||||
const provider:any = {
|
||||
...item,
|
||||
}
|
||||
delete provider.input
|
||||
if (conf && conf.addonId) {
|
||||
const addonEntity = await this.addonService.info(conf.addonId);
|
||||
if (addonEntity) {
|
||||
provider.addonId = conf.addonId;
|
||||
provider.addonTitle = addonEntity.name;
|
||||
}
|
||||
}
|
||||
list.push(provider);
|
||||
}
|
||||
|
||||
return this.ok(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,7 +37,13 @@ export type LoginUrlReply = {
|
||||
ticketValue: any;
|
||||
}
|
||||
|
||||
export type BuildLoginUrlReq = {
|
||||
redirectUri: string;
|
||||
forType?: string;
|
||||
from?:string;
|
||||
}
|
||||
|
||||
export interface IOauthProvider {
|
||||
buildLoginUrl: (params: { redirectUri: string, forType?: string }) => Promise<LoginUrlReply>;
|
||||
buildLoginUrl: (params: BuildLoginUrlReq) => Promise<LoginUrlReply>;
|
||||
onCallback: (params: OnCallbackReq) => Promise<OauthToken>;
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './api.js'
|
||||
export * from './oidc/plugin-oidc.js'
|
||||
export * from './wx/plugin-wx.js'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server";
|
||||
import { IOauthProvider, OnCallbackReq } from "../api.js";
|
||||
import { BuildLoginUrlReq, IOauthProvider, OnCallbackReq } from "../api.js";
|
||||
|
||||
@IsAddon({
|
||||
addonType: "oauth",
|
||||
@@ -56,7 +56,7 @@ export class OidcOauthProvider extends BaseAddon implements IOauthProvider {
|
||||
}
|
||||
}
|
||||
|
||||
async buildLoginUrl(params: { redirectUri: string, forType?: string }) {
|
||||
async buildLoginUrl(params: BuildLoginUrlReq) {
|
||||
const { config, client } = await this.getClient()
|
||||
|
||||
let redirect_uri = new URL(params.redirectUri)
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server";
|
||||
import { BuildLoginUrlReq, IOauthProvider, OnCallbackReq } from "../api.js";
|
||||
|
||||
@IsAddon({
|
||||
addonType: "oauth",
|
||||
name: 'wx',
|
||||
title: '微信登录',
|
||||
desc: '微信网站应用登录',
|
||||
icon: "mdi:wechat",
|
||||
showTest: false,
|
||||
})
|
||||
export class WxOauthProvider extends BaseAddon implements IOauthProvider {
|
||||
|
||||
@AddonInput({
|
||||
title: "AppId",
|
||||
required: true,
|
||||
helper: "在[微信开放平台](https://open.weixin.qq.com/cgi-bin/index)注册网站应用后获取",
|
||||
})
|
||||
appId = "";
|
||||
|
||||
@AddonInput({
|
||||
title: "AppSecretKey",
|
||||
component: {
|
||||
placeholder: "AppSecretKey",
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
appSecretKey = "";
|
||||
|
||||
|
||||
wxAccessToken?: { access_token: string, expires_at: number }
|
||||
|
||||
async buildLoginUrl(params: BuildLoginUrlReq) {
|
||||
|
||||
const from = params.from || "web";
|
||||
const appId = this.appId;
|
||||
const redirect_uri = encodeURIComponent(params.redirectUri);
|
||||
let state: any = {
|
||||
forType: params.forType,
|
||||
from
|
||||
}
|
||||
state = this.ctx.utils.hash.base64(JSON.stringify(state))
|
||||
let scope = "snsapi_userinfo";
|
||||
let loginUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirect_uri}&response_type=code&scope=${scope}&state=${state}#wechat_redirect`
|
||||
if (from === "web") {
|
||||
scope = "snsapi_login";
|
||||
loginUrl = `https://open.weixin.qq.com/connect/qrconnect?appid=${appId}&redirect_uri=${redirect_uri}&response_type=code&scope=${scope}&state=${state}#wechat_redirect`
|
||||
}
|
||||
|
||||
return {
|
||||
loginUrl,
|
||||
ticketValue: {
|
||||
state,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// async getWxAccessToken() {
|
||||
// if (this.wxAccessToken && this.wxAccessToken.expires_at > Date.now()) {
|
||||
// return this.wxAccessToken
|
||||
// }
|
||||
// const res = await this.http.request({
|
||||
// url: "https://api.weixin.qq.com/cgi-bin/token",
|
||||
// method: "GET",
|
||||
// params: {
|
||||
// appid: this.appId,
|
||||
// secret: this.appSecretKey,
|
||||
// grant_type: "client_credential",
|
||||
// },
|
||||
// })
|
||||
// this.checkRet(res)
|
||||
// this.wxAccessToken = {
|
||||
// access_token: res.access_token,
|
||||
// expires_at: Date.now() + res.expires_in * 1000,
|
||||
// }
|
||||
// return this.wxAccessToken
|
||||
// }
|
||||
|
||||
checkRet(res: any) {
|
||||
if (res.errcode) {
|
||||
throw new Error(res.errmsg)
|
||||
}
|
||||
}
|
||||
|
||||
async onCallback(req: OnCallbackReq) {
|
||||
|
||||
|
||||
// GET https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx520c15f417810387&secret=SECRET&code=CODE&grant_type=authorization_code
|
||||
const res = await this.http.request({
|
||||
url: "https://api.weixin.qq.com/sns/oauth2/access_token",
|
||||
method: "GET",
|
||||
params: {
|
||||
appid: this.appId,
|
||||
secret: this.appSecretKey,
|
||||
code: req.code,
|
||||
grant_type: "authorization_code",
|
||||
},
|
||||
})
|
||||
this.checkRet(res)
|
||||
const accessToken = res.access_token
|
||||
|
||||
|
||||
// GET https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
|
||||
const userInfoRes = await this.http.request({
|
||||
url: "https://api.weixin.qq.com/sns/userinfo",
|
||||
method: "GET",
|
||||
params: {
|
||||
access_token:accessToken,
|
||||
openid: res.openid,
|
||||
lang: "zh_CN",
|
||||
},
|
||||
})
|
||||
this.checkRet(userInfoRes)
|
||||
|
||||
return {
|
||||
token: {
|
||||
accessToken: res.access_token,
|
||||
refreshToken: res.refresh_token,
|
||||
expiresIn: res.expires_in,
|
||||
},
|
||||
userInfo: {
|
||||
openId: res.unionid || res.openid,
|
||||
nickName: userInfoRes.nickname || "",
|
||||
avatar: userInfoRes.headimgurl,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user