Просмотр исходного кода

feat(bx-mobile): add portal HTTP client and RSA login API

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 3 месяцев назад
Родитель
Сommit
c7a8930ad6
3 измененных файлов с 90 добавлено и 0 удалено
  1. 14 0
      bx-mobile/src/api/portalAuth.ts
  2. 50 0
      bx-mobile/src/api/portalClient.ts
  3. 26 0
      bx-mobile/src/utils/loginRsa.ts

+ 14 - 0
bx-mobile/src/api/portalAuth.ts

@@ -0,0 +1,14 @@
+import portalClient from './portalClient'
+import { buildWebEncryptedLoginBody } from '@/utils/loginRsa'
+
+export const portalAuthApi = {
+  login: async (username: string, password: string) => {
+    const body = await buildWebEncryptedLoginBody(username, password)
+    return portalClient.post('/web/auth/login', body)
+  },
+  currentUser: () => portalClient.get('/web/auth/current-user'),
+  menuCodes: () => portalClient.get('/web/auth/menu-codes'),
+  refreshToken: (refreshToken: string) =>
+    portalClient.post('/web/auth/refresh-token', { refreshToken }),
+  logout: () => portalClient.post('/web/auth/logout'),
+}

+ 50 - 0
bx-mobile/src/api/portalClient.ts

@@ -0,0 +1,50 @@
+import axios from 'axios'
+import { showToast } from 'vant'
+
+export class PortalError extends Error {
+  code?: number
+  constructor(message: string, code?: number) {
+    super(message)
+    this.code = code
+    this.name = 'PortalError'
+  }
+}
+
+const portalClient = axios.create({
+  baseURL: import.meta.env.VITE_API_URL || '/api',
+  timeout: 60000,
+  headers: { 'Content-Type': 'application/json' },
+})
+
+portalClient.interceptors.request.use((config) => {
+  const token = localStorage.getItem('accessToken')
+  if (token) {
+    config.headers.Authorization = `Bearer ${token}`
+  }
+  return config
+})
+
+portalClient.interceptors.response.use(
+  (res) => {
+    const body = res.data
+    if (body && typeof body.code === 'number' && body.code !== 200) {
+      const msg = body.message || body.msg || '请求失败'
+      showToast(msg)
+      return Promise.reject(new PortalError(msg, body.code))
+    }
+    return body
+  },
+  (err) => {
+    if (err.response?.status !== 401) {
+      const msg =
+        err.response?.data?.message ||
+        err.response?.data?.msg ||
+        err.message ||
+        '网络异常'
+      showToast(msg)
+    }
+    return Promise.reject(err)
+  },
+)
+
+export default portalClient

+ 26 - 0
bx-mobile/src/utils/loginRsa.ts

@@ -0,0 +1,26 @@
+import JSEncrypt from 'jsencrypt'
+import portalClient from '@/api/portalClient'
+
+/**
+ * Web 端登录:拉取 RSA 公钥后加密 { username, password, ts },与门户 PKCS#1 v1.5 解密一致。
+ */
+export async function buildWebEncryptedLoginBody(username: string, password: string) {
+  const res: any = await portalClient.get('/web/auth/login-rsa-public-key')
+  const publicKeyPem = res?.data?.publicKeyPem
+  if (!publicKeyPem) {
+    throw new Error('无法获取登录公钥')
+  }
+  const crypt = new JSEncrypt()
+  crypt.setPublicKey(publicKeyPem)
+  const encryptedLogin = crypt.encrypt(
+    JSON.stringify({
+      username,
+      password,
+      ts: Date.now(),
+    }),
+  )
+  if (!encryptedLogin) {
+    throw new Error('登录加密失败,请重试')
+  }
+  return { encryptedLogin }
+}