1.4.5.2
This commit is contained in:
169
EdgeUser/web/public/js/components/common/csrf-token.js
Normal file
169
EdgeUser/web/public/js/components/common/csrf-token.js
Normal file
@@ -0,0 +1,169 @@
|
||||
Vue.component("csrf-token", {
|
||||
created: function () {
|
||||
this.refreshToken()
|
||||
},
|
||||
mounted: function () {
|
||||
let that = this
|
||||
var form = this.$refs.token.form
|
||||
|
||||
// 监听表单提交,在提交前刷新token并确保更新到 DOM
|
||||
form.addEventListener("submit", function (e) {
|
||||
// 如果正在刷新,等待刷新完成
|
||||
if (that.refreshing) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 阻止默认提交,先刷新 token
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
that.refreshing = true
|
||||
|
||||
// 刷新 token
|
||||
that.refreshToken(function () {
|
||||
// 确保 DOM 中的 token 值是最新的
|
||||
that.$forceUpdate()
|
||||
that.$nextTick(function () {
|
||||
var tokenInput = form.querySelector('input[name="csrfToken"]')
|
||||
if (tokenInput) {
|
||||
tokenInput.value = that.token
|
||||
}
|
||||
if (that.$refs.token) {
|
||||
that.$refs.token.value = that.token
|
||||
}
|
||||
|
||||
// 确保 DOM 已更新后,再触发表单提交
|
||||
setTimeout(function () {
|
||||
that.refreshing = false
|
||||
// 重新触发表单提交
|
||||
Tea.runActionOn(form)
|
||||
}, 50)
|
||||
})
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
// 自动刷新
|
||||
setInterval(function () {
|
||||
that.refreshToken()
|
||||
}, 10 * 60 * 1000)
|
||||
|
||||
// 监听表单提交失败,如果是 CSRF token 错误,自动刷新 token 并重试
|
||||
this.setupAutoRetry(form)
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
token: "",
|
||||
retrying: false,
|
||||
refreshing: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshToken: function (callback) {
|
||||
let that = this
|
||||
Tea.action("/csrf/token")
|
||||
.get()
|
||||
.success(function (resp) {
|
||||
that.token = resp.data.token
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
.fail(function () {
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
},
|
||||
setupAutoRetry: function (form) {
|
||||
let that = this
|
||||
var originalFail = form.getAttribute("data-tea-fail")
|
||||
|
||||
// 确保 Tea.Vue 存在
|
||||
if (typeof Tea === "undefined" || Tea.Vue == null) {
|
||||
if (typeof Tea === "undefined") {
|
||||
window.Tea = {}
|
||||
}
|
||||
if (Tea.Vue == null) {
|
||||
Tea.Vue = {}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建一个包装的 fail 函数
|
||||
var wrappedFailName = "csrfAutoRetryFail_" + Math.random().toString(36).substr(2, 9)
|
||||
form.setAttribute("data-tea-fail", wrappedFailName)
|
||||
|
||||
Tea.Vue[wrappedFailName] = function (resp) {
|
||||
// 检查是否是 CSRF token 错误
|
||||
var isCSRFError = false
|
||||
if (resp && resp.message) {
|
||||
// 检查消息是否包含 "表单已失效" 或 "001"
|
||||
if (resp.message.indexOf("表单已失效") >= 0 || resp.message.indexOf("(001)") >= 0) {
|
||||
isCSRFError = true
|
||||
}
|
||||
}
|
||||
// 检查 HTTP 状态码是否为 403 或 400
|
||||
if (!isCSRFError && resp && (resp.statusCode === 403 || resp.status === 403 || resp.statusCode === 400 || resp.status === 400)) {
|
||||
isCSRFError = true
|
||||
}
|
||||
|
||||
if (isCSRFError) {
|
||||
// 如果不是正在重试,则立即刷新 token 并自动重试
|
||||
if (!that.retrying) {
|
||||
that.retrying = true
|
||||
// 立即刷新 token
|
||||
that.refreshToken(function () {
|
||||
// 强制更新 Vue,确保响应式数据已更新
|
||||
that.$forceUpdate()
|
||||
|
||||
// 使用 $nextTick 等待 Vue 完成 DOM 更新
|
||||
that.$nextTick(function () {
|
||||
// 直接查找并更新 DOM 中的 input 元素(通过 name 属性)
|
||||
var tokenInput = form.querySelector('input[name="csrfToken"]')
|
||||
if (tokenInput) {
|
||||
tokenInput.value = that.token
|
||||
}
|
||||
|
||||
// 如果 ref 存在,也更新它
|
||||
if (that.$refs.token) {
|
||||
that.$refs.token.value = that.token
|
||||
}
|
||||
|
||||
// 使用 setTimeout 确保 DOM 已完全更新
|
||||
setTimeout(function () {
|
||||
// 再次确认 token 值已更新
|
||||
var finalTokenInput = form.querySelector('input[name="csrfToken"]')
|
||||
if (finalTokenInput && finalTokenInput.value !== that.token) {
|
||||
finalTokenInput.value = that.token
|
||||
}
|
||||
|
||||
that.retrying = false
|
||||
// 重新触发表单提交
|
||||
Tea.runActionOn(form)
|
||||
}, 150)
|
||||
})
|
||||
})
|
||||
return // 不调用原始 fail 函数
|
||||
} else {
|
||||
// 如果正在重试,说明已经刷新过 token,直接调用原始 fail 函数
|
||||
if (originalFail && typeof Tea.Vue[originalFail] === "function") {
|
||||
return Tea.Vue[originalFail].call(Tea.Vue, resp)
|
||||
} else {
|
||||
Tea.failResponse(resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 不是 CSRF 错误,调用原始 fail 函数或默认处理
|
||||
if (originalFail && typeof Tea.Vue[originalFail] === "function") {
|
||||
return Tea.Vue[originalFail].call(Tea.Vue, resp)
|
||||
} else {
|
||||
Tea.failResponse(resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `<input type="hidden" name="csrfToken" :value="token" ref="token"/>`
|
||||
})
|
||||
Reference in New Issue
Block a user