登录验证码接入
This commit is contained in:
1 parent
ae075bda6d
commit
f489a3553c
15 files changed
+471
-19
No files matched your search
+12
-15
@@ -20,14 +20,16 @@ const http = axios.create({
|
||||
timeout: 15000,
|
||||
})
|
||||
|
||||
function isAuthenticationRequest(url?: string) {
|
||||
return url?.startsWith('/auth/login') ||
|
||||
url?.endsWith('/auth/refresh') ||
|
||||
url?.endsWith('/auth/logout') ||
|
||||
url?.endsWith('/auth/sso/exchange') ||
|
||||
url?.endsWith('/auth/sso/bind')
|
||||
}
|
||||
|
||||
http.interceptors.request.use(async (config) => {
|
||||
const isAuthenticationRequest =
|
||||
config.url?.endsWith('/auth/login') ||
|
||||
config.url?.endsWith('/auth/refresh') ||
|
||||
config.url?.endsWith('/auth/logout') ||
|
||||
config.url?.endsWith('/auth/sso/exchange') ||
|
||||
config.url?.endsWith('/auth/sso/bind')
|
||||
if (!isAuthenticationRequest && !config.skipAuthSession) {
|
||||
if (!isAuthenticationRequest(config.url) && !config.skipAuthSession) {
|
||||
const activeToken = await refreshIfNeeded(true)
|
||||
if (activeToken) markActivity()
|
||||
}
|
||||
@@ -39,15 +41,10 @@ http.interceptors.request.use(async (config) => {
|
||||
http.interceptors.response.use(
|
||||
(response) => response,
|
||||
async (error) => {
|
||||
const isAuthenticationRequest =
|
||||
error.config?.url?.endsWith('/auth/login') ||
|
||||
error.config?.url?.endsWith('/auth/refresh') ||
|
||||
error.config?.url?.endsWith('/auth/logout') ||
|
||||
error.config?.url?.endsWith('/auth/sso/exchange') ||
|
||||
error.config?.url?.endsWith('/auth/sso/bind')
|
||||
const authenticationRequest = isAuthenticationRequest(error.config?.url)
|
||||
const retryableConfig = error.config as
|
||||
(typeof error.config & { _jiaowuRetried?: boolean }) | undefined
|
||||
if (error.response?.status === 401 && !isAuthenticationRequest && !error.config?.skipAuthSession &&
|
||||
if (error.response?.status === 401 && !authenticationRequest && !error.config?.skipAuthSession &&
|
||||
!retryableConfig?._jiaowuRetried) {
|
||||
const token = await refreshAuthSession()
|
||||
if (token && retryableConfig) {
|
||||
@@ -56,7 +53,7 @@ http.interceptors.response.use(
|
||||
return http.request(retryableConfig)
|
||||
}
|
||||
}
|
||||
if (error.response?.status === 401 && !isAuthenticationRequest && !error.config?.skipAuthSession) {
|
||||
if (error.response?.status === 401 && !authenticationRequest && !error.config?.skipAuthSession) {
|
||||
clearAuthSession()
|
||||
goLogin(location.pathname + location.search + location.hash)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import FingerprintJS from '@fingerprintjs/fingerprintjs'
|
||||
|
||||
const fingerprint = FingerprintJS.load()
|
||||
|
||||
/** A pseudonymous, client-generated identifier used only to bind short-lived login challenges. */
|
||||
export async function getLoginDeviceId(): Promise<string> {
|
||||
try {
|
||||
return (await fingerprint).get().then((result) => result.visitorId)
|
||||
} catch {
|
||||
// Privacy extensions may block fingerprinting. The captcha remains usable with IP/session binding.
|
||||
return ''
|
||||
}
|
||||
}
|
||||
Vendored
+1
@@ -61,6 +61,7 @@ declare module 'vue' {
|
||||
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
|
||||
ElTimeSelect: typeof import('element-plus/es')['ElTimeSelect']
|
||||
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||
LoginCaptcha: typeof import('./components/LoginCaptcha.vue')['default']
|
||||
NativePlaceCascader: typeof import('./components/NativePlaceCascader.vue')['default']
|
||||
NotificationRichTextEditor: typeof import('./components/NotificationRichTextEditor.vue')['default']
|
||||
PerformanceReportPanel: typeof import('./components/PerformanceReportPanel.vue')['default']
|
||||
|
||||
Loaded 3 of 15 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user