From 73325aaefb0e750a22aaac40929e7bf3f5864996 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sun, 30 Nov 2025 01:13:55 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=89=AB=E7=A0=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/locales/langs/en-US/certd.ts | 5 +- .../src/locales/langs/zh-CN/certd.ts | 5 +- .../certd/addon/addon-selector/index.vue | 6 +- .../src/views/certd/addon/common.tsx | 9 +- .../src/views/certd/addon/crud.tsx | 6 + .../src/views/framework/oauth/api.ts | 3 +- .../views/framework/oauth/oauth-footer.vue | 19 +-- .../src/views/sys/settings/api.ts | 2 +- .../src/views/sys/settings/tabs/register.vue | 45 +++--- .../basic/login/oauth-controller.ts | 37 ++++- .../src/plugins/plugin-oauth/api.ts | 8 +- .../src/plugins/plugin-oauth/index.ts | 1 + .../plugins/plugin-oauth/oidc/plugin-oidc.ts | 4 +- .../src/plugins/plugin-oauth/wx/plugin-wx.ts | 128 ++++++++++++++++++ 14 files changed, 237 insertions(+), 41 deletions(-) create mode 100644 packages/ui/certd-server/src/plugins/plugin-oauth/wx/plugin-wx.ts diff --git a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts index cc48067ce..00019b232 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts @@ -766,7 +766,10 @@ export default { oauthProviders: "OAuth2 Login Providers", oauthType: "OAuth2 Login Type", oauthConfig: "OAuth2 Login Config", - oauthProviderSelectorPlaceholder: "Please select OAuth2 login provider", + oauthProviderSelectorPlaceholder: "Not Configured", + oauthCallback: "Callback URL", + oauthCallbackHelper: "Copy this URL to the callback address of the OAuth2 login provider", + oauthCallbackCopy: "Copy Callback URL", }, }, modal: { diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts index ae7a70a76..3e368a21d 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts @@ -767,7 +767,10 @@ export default { oauthProviders: "第三方登录提供商", oauthType: "第三方登录类型", oauthConfig: "第三方登录配置", - oauthProviderSelectorPlaceholder: "请选择第三方登录提供商", + oauthProviderSelectorPlaceholder: "未配置", + oauthCallback: "回调地址", + oauthCallbackHelper: "复制回调地址,配置到对应提供商的回调地址中", + oauthCallbackCopy: "复制回调地址", }, }, modal: { diff --git a/packages/ui/certd-client/src/views/certd/addon/addon-selector/index.vue b/packages/ui/certd-client/src/views/certd/addon/addon-selector/index.vue index 104035363..dfd36ac8b 100644 --- a/packages/ui/certd-client/src/views/certd/addon/addon-selector/index.vue +++ b/packages/ui/certd-client/src/views/certd/addon/addon-selector/index.vue @@ -1,6 +1,6 @@ @@ -15,15 +17,16 @@ import { onMounted, ref } from "vue"; import * as api from "./api"; -const oauthList = ref([]); +const oauthProviderList = ref([]); onMounted(async () => { - oauthList.value = await api.GetOauthProviders(); + oauthProviderList.value = await api.GetOauthProviders(); }); async function goOauthLogin(type: string) { //获取第三方登录URL - const res = await api.OauthLogin(type); + const from = "web"; + const res = await api.OauthLogin(type, from); const loginUrl = res.loginUrl; window.location.href = loginUrl; } diff --git a/packages/ui/certd-client/src/views/sys/settings/api.ts b/packages/ui/certd-client/src/views/sys/settings/api.ts index a252ad3a4..c758e4e04 100644 --- a/packages/ui/certd-client/src/views/sys/settings/api.ts +++ b/packages/ui/certd-client/src/views/sys/settings/api.ts @@ -114,7 +114,7 @@ export async function GetSmsTypeDefine(type: string) { export async function GetOauthProviders() { return await request({ - url: apiPrefix + "/oauth/providers", + url: "/oauth/providers", method: "post", }); } diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue index 1a40f3d8b..ab8c1c420 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue @@ -67,8 +67,9 @@ - - + + + @@ -79,6 +80,11 @@ {{ item.title }} + @@ -96,14 +102,14 @@ diff --git a/packages/ui/certd-server/src/controller/basic/login/oauth-controller.ts b/packages/ui/certd-server/src/controller/basic/login/oauth-controller.ts index ca938d8ed..4bcac06a7 100644 --- a/packages/ui/certd-server/src/controller/basic/login/oauth-controller.ts +++ b/packages/ui/certd-server/src/controller/basic/login/oauth-controller.ts @@ -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); 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); } - } diff --git a/packages/ui/certd-server/src/plugins/plugin-oauth/api.ts b/packages/ui/certd-server/src/plugins/plugin-oauth/api.ts index 4c1d16f29..9ca0391cc 100644 --- a/packages/ui/certd-server/src/plugins/plugin-oauth/api.ts +++ b/packages/ui/certd-server/src/plugins/plugin-oauth/api.ts @@ -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; + buildLoginUrl: (params: BuildLoginUrlReq) => Promise; onCallback: (params: OnCallbackReq) => Promise; } \ No newline at end of file diff --git a/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts b/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts index 7f230f570..0925eb093 100644 --- a/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts @@ -1,2 +1,3 @@ export * from './api.js' export * from './oidc/plugin-oidc.js' +export * from './wx/plugin-wx.js' diff --git a/packages/ui/certd-server/src/plugins/plugin-oauth/oidc/plugin-oidc.ts b/packages/ui/certd-server/src/plugins/plugin-oauth/oidc/plugin-oidc.ts index 749861491..99a78c18a 100644 --- a/packages/ui/certd-server/src/plugins/plugin-oauth/oidc/plugin-oidc.ts +++ b/packages/ui/certd-server/src/plugins/plugin-oauth/oidc/plugin-oidc.ts @@ -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) diff --git a/packages/ui/certd-server/src/plugins/plugin-oauth/wx/plugin-wx.ts b/packages/ui/certd-server/src/plugins/plugin-oauth/wx/plugin-wx.ts new file mode 100644 index 000000000..061c554fd --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-oauth/wx/plugin-wx.ts @@ -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, + }, + } + }; +}
{{ t("certd.sys.setting.oauthType") }}{{ t("certd.sys.setting.oauthConfig") }}{{ t("certd.sys.setting.oauthType") }}{{ t("certd.sys.setting.oauthCallback") }}{{ t("certd.sys.setting.oauthConfig") }}
+ + {{ t("certd.sys.setting.oauthCallbackCopy") }} + +