西藏巴青项目

screenAutoLogin.js 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
  2. import { getScreenPublicKey, screenAutoLogin } from '@/api/login'
  3. import { getToken, setToken } from '@/utils/auth'
  4. /** URL 密文登录失败时展示给用户的提示 */
  5. export const SCREEN_LOGIN_CIPHER_ERROR = '公钥不对'
  6. /** URL uuid 免密登录失败时展示给用户的提示 */
  7. export const SCREEN_LOGIN_UUID_ERROR = '免密失败,请联系管理员获取免登ID'
  8. /** 同一会话内仅尝试一次免密登录,避免反复请求 */
  9. let autoLoginState = null
  10. let autoLoginPromise = null
  11. let autoLoginUuid = null
  12. export function isScreenAutoLoginEnabled() {
  13. return import.meta.env.VITE_SCREEN_AUTO_LOGIN === 'true'
  14. }
  15. export function resetScreenAutoLoginState() {
  16. autoLoginState = null
  17. autoLoginPromise = null
  18. autoLoginUuid = null
  19. }
  20. /** 从路由 query 读取 RSA 密文(?token=xxxx) */
  21. export function getUrlScreenLoginCipher(query) {
  22. const raw = query?.token
  23. if (typeof raw !== 'string' || !raw.trim()) {
  24. return ''
  25. }
  26. try {
  27. return decodeURIComponent(raw.trim())
  28. } catch {
  29. return raw.trim()
  30. }
  31. }
  32. /** 从路由 query 读取免登 UUID(?uuid=xxxx) */
  33. export function getUrlScreenLoginUuid(query) {
  34. const raw = query?.uuid
  35. if (typeof raw !== 'string' || !raw.trim()) {
  36. return ''
  37. }
  38. try {
  39. return decodeURIComponent(raw.trim())
  40. } catch {
  41. return raw.trim()
  42. }
  43. }
  44. /** 去掉 URL 中的 token,避免密文残留在地址栏 */
  45. export function stripScreenLoginTokenQuery(query = {}) {
  46. const next = { ...query }
  47. delete next.token
  48. return next
  49. }
  50. /** 去掉 URL 中的 uuid */
  51. export function stripScreenLoginUuidQuery(query = {}) {
  52. const next = { ...query }
  53. delete next.uuid
  54. return next
  55. }
  56. /**
  57. * 使用 URL 传入的 RSA 密文换取 JWT
  58. * @param {string} cipher
  59. * @returns {Promise<string>} JWT
  60. */
  61. export async function tryScreenLoginWithCipher(cipher) {
  62. if (!cipher) {
  63. throw new Error(SCREEN_LOGIN_CIPHER_ERROR)
  64. }
  65. try {
  66. const loginRes = await screenAutoLogin(cipher)
  67. if (!loginRes?.token) {
  68. throw new Error(SCREEN_LOGIN_CIPHER_ERROR)
  69. }
  70. setToken(loginRes.token)
  71. autoLoginState = 'ok'
  72. return loginRes.token
  73. } catch {
  74. throw new Error(SCREEN_LOGIN_CIPHER_ERROR)
  75. }
  76. }
  77. /**
  78. * 获取公钥 → RSA 加密 URL 中的 UUID → 换取 JWT
  79. * @param {string} uuid
  80. * @returns {Promise<string>} JWT
  81. */
  82. export async function tryScreenAutoLogin(uuid) {
  83. if (!uuid) {
  84. throw new Error(SCREEN_LOGIN_UUID_ERROR)
  85. }
  86. try {
  87. const keyRes = await getScreenPublicKey()
  88. const publicKey = keyRes.data?.publicKey
  89. if (!publicKey) {
  90. throw new Error(SCREEN_LOGIN_UUID_ERROR)
  91. }
  92. const encryptor = new JSEncrypt()
  93. encryptor.setPublicKey(publicKey)
  94. const cipher = encryptor.encrypt(uuid)
  95. if (!cipher) {
  96. throw new Error(SCREEN_LOGIN_UUID_ERROR)
  97. }
  98. const loginRes = await screenAutoLogin(cipher)
  99. if (!loginRes?.token) {
  100. throw new Error(SCREEN_LOGIN_UUID_ERROR)
  101. }
  102. setToken(loginRes.token)
  103. return loginRes.token
  104. } catch {
  105. throw new Error(SCREEN_LOGIN_UUID_ERROR)
  106. }
  107. }
  108. /**
  109. * 路由守卫用:URL 含 uuid 时尝试免密登录
  110. * @returns {Promise<{ attempted: boolean, ok: boolean }>}
  111. */
  112. export async function ensureScreenAutoLogin(query) {
  113. if (getToken()) {
  114. return { attempted: false, ok: true }
  115. }
  116. if (!isScreenAutoLoginEnabled()) {
  117. return { attempted: false, ok: false }
  118. }
  119. const uuid = getUrlScreenLoginUuid(query)
  120. if (!uuid) {
  121. return { attempted: false, ok: false }
  122. }
  123. if (autoLoginState === 'fail' && autoLoginUuid === uuid) {
  124. return { attempted: true, ok: false }
  125. }
  126. if (autoLoginState === 'ok' && getToken()) {
  127. return { attempted: false, ok: true }
  128. }
  129. if (autoLoginState === 'pending' && autoLoginPromise && autoLoginUuid === uuid) {
  130. const ok = await autoLoginPromise
  131. return { attempted: true, ok }
  132. }
  133. autoLoginUuid = uuid
  134. autoLoginState = 'pending'
  135. autoLoginPromise = tryScreenAutoLogin(uuid)
  136. .then(() => {
  137. autoLoginState = 'ok'
  138. return true
  139. })
  140. .catch(() => {
  141. autoLoginState = 'fail'
  142. return false
  143. })
  144. const ok = await autoLoginPromise
  145. return { attempted: true, ok }
  146. }