fix(upload): 支持管理后台管理员身份上传图片并补齐 a-upload 鉴权请求头

这个提交包含在:
xcbyy
2026-10-23 09:48:16 +08:00
父节点 4d2e7b6464
当前提交 8613d40588
共有 3 个文件被更改,包括 30 次插入 和 8 次删除

查看文件

@@ -167,6 +167,7 @@
<a-upload
action="/api/upload"
name="file"
:headers="uploadHeaders"
:show-file-list="false"
@success="handleUploadSuccess"
@error="handleUploadError"
@@ -250,9 +251,15 @@
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { ref, computed, onMounted } from 'vue';
import axios from 'axios';
import { Message } from '@arco-design/web-vue';
import { getToken } from '@/utils/auth';
const uploadHeaders = computed(() => {
const token = getToken();
return token ? { Authorization: `Bearer ${token}` } : {};
});
interface BannerItem {
id: number;
@@ -332,7 +339,11 @@
const handleUploadSuccess = (fileItem: any) => {
const response = fileItem.response;
if (response && response.code === 20000 && response.data?.url) {
formData.value.image = response.data.url;
let url = response.data.url;
if (url.startsWith('/uploads/')) {
url = `https://peiww.gynhc.com${url}`;
}
formData.value.image = url;
Message.success('轮播图片上传成功并已持久化');
} else {
Message.warning('上传完成,但响应格式异常');

查看文件

@@ -355,6 +355,7 @@
<a-upload
action="/api/upload"
name="file"
:headers="uploadHeaders"
:show-file-list="false"
@success="handlePopupUploadSuccess"
@error="handlePopupUploadError"
@@ -1085,6 +1086,12 @@
import { ref, reactive, computed, onMounted } from 'vue';
import { Message } from '@arco-design/web-vue';
import axios from 'axios';
import { getToken } from '@/utils/auth';
const uploadHeaders = computed(() => {
const token = getToken();
return token ? { Authorization: `Bearer ${token}` } : {};
});
const loading = ref(false);
const saving = ref(false);

查看文件

@@ -349,8 +349,8 @@ function requireAdmin(req: Request, res: Response, next: NextFunction): void {
// 仅依据服务端签名校验通过的 admin token 判定管理员请求;
// 严禁信任任何客户端可伪造的角色字段(operatorRole / x-user-role)。
function isVerifiedAdminRequest(req: Request): boolean {
const auth = String(req.headers['authorization'] || '');
const token = auth.startsWith('Bearer ') ? auth.slice(7).trim() : '';
const auth = String(req.headers['authorization'] || req.headers['x-admin-token'] || '');
const token = auth.startsWith('Bearer ') ? auth.slice(7).trim() : auth.trim();
return Boolean(token) && verifyAdminToken(token);
}
@@ -714,12 +714,16 @@ app.all('/api/user/certification', (_req: Request, res: Response) => {
// 真实文件与图片对象存储上传接口 (支持单图上传并持久化至 public/uploads)
const handleFileUpload = (req: Request, res: Response) => {
// 上传要求登录,避免匿名上传被滥用(刷盘/托管非法内容)。
// 兼容管理后台管理员身份与小程序客户端用户身份。
// 不依赖 NODE_ENV:只有显式 ALLOW_INSECURE_DEV_AUTH=1 的本地联调才放开。
if (SECURE_MODE) {
try {
resolveSecureUserId(req, 0, '上传文件');
} catch (e: any) {
return res.status(e.status || 401).json({ code: e.code || 40100, message: e.message || '请先登录后再上传' });
const isAdmin = isVerifiedAdminRequest(req);
if (!isAdmin) {
try {
resolveSecureUserId(req, 0, '上传文件');
} catch (e: any) {
return res.status(e.status || 401).json({ code: e.code || 40100, message: e.message || '请先登录后再上传' });
}
}
}
upload.single('file')(req, res, (err: any) => {