1. xxl-job 设置虚拟用户 0 登录操作

2. Access-Control-Expose-Headers 允许暴露 content-disposition
This commit is contained in:
chenbowen
2025-09-25 12:01:19 +08:00
parent 32cb704e4f
commit f33d3f07b8
9 changed files with 536 additions and 3 deletions

View File

@@ -0,0 +1,140 @@
package com.zt.plat.module.template.controller.admin.test;
import com.zt.plat.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.nio.charset.StandardCharsets;
/**
* CORS 测试控制器
*
* @author ZT
*/
@Tag(name = "管理后台 - CORS 测试")
@RestController
@RequestMapping("/template/test")
@Slf4j
public class CorsTestController {
@GetMapping("/download")
@Operation(summary = "测试文件下载响应头", description = "测试 content-disposition 响应头是否能被前端获取")
public ResponseEntity<String> testDownload() {
log.info("[testDownload] 测试 content-disposition 响应头");
// 创建响应头
HttpHeaders headers = new HttpHeaders();
// 设置 content-disposition 头,包含文件名
String filename = "测试文件.txt";
String encodedFilename = new String(filename.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
headers.add("Content-Disposition", "attachment; filename*=UTF-8''" +
java.net.URLEncoder.encode(filename, StandardCharsets.UTF_8) +
"; filename=\"" + encodedFilename + "\"");
// 设置其他常用的文件下载响应头
headers.add("Content-Type", "application/octet-stream");
headers.add("Cache-Control", "no-cache");
String responseBody = "这是一个测试文件的内容,用于验证 CORS 配置是否正确。\n" +
"如果前端能够获取到 content-disposition 头信息,说明配置成功。\n" +
"文件名应该是:" + filename;
return ResponseEntity.ok()
.headers(headers)
.body(responseBody);
}
@GetMapping("/cors-info")
@Operation(summary = "获取 CORS 配置信息", description = "返回当前 CORS 配置的相关信息")
public CommonResult<CorsInfo> getCorsInfo() {
log.info("[getCorsInfo] 获取 CORS 配置信息");
CorsInfo corsInfo = new CorsInfo();
corsInfo.setExposedHeaders("content-disposition");
corsInfo.setAllowedOrigins("*");
corsInfo.setAllowedMethods("GET, POST, PUT, DELETE, OPTIONS");
corsInfo.setAllowedHeaders("*");
corsInfo.setAllowCredentials(true);
corsInfo.setMaxAge(3600L);
corsInfo.setMessage("CORS 配置已启用content-disposition 头已暴露给前端");
return CommonResult.success(corsInfo);
}
/**
* CORS 配置信息
*/
public static class CorsInfo {
private String exposedHeaders;
private String allowedOrigins;
private String allowedMethods;
private String allowedHeaders;
private Boolean allowCredentials;
private Long maxAge;
private String message;
// Getters and Setters
public String getExposedHeaders() {
return exposedHeaders;
}
public void setExposedHeaders(String exposedHeaders) {
this.exposedHeaders = exposedHeaders;
}
public String getAllowedOrigins() {
return allowedOrigins;
}
public void setAllowedOrigins(String allowedOrigins) {
this.allowedOrigins = allowedOrigins;
}
public String getAllowedMethods() {
return allowedMethods;
}
public void setAllowedMethods(String allowedMethods) {
this.allowedMethods = allowedMethods;
}
public String getAllowedHeaders() {
return allowedHeaders;
}
public void setAllowedHeaders(String allowedHeaders) {
this.allowedHeaders = allowedHeaders;
}
public Boolean getAllowCredentials() {
return allowCredentials;
}
public void setAllowCredentials(Boolean allowCredentials) {
this.allowCredentials = allowCredentials;
}
public Long getMaxAge() {
return maxAge;
}
public void setMaxAge(Long maxAge) {
this.maxAge = maxAge;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
}

View File

@@ -0,0 +1,206 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CORS content-disposition 测试</title>
<style>
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
margin-bottom: 30px;
}
.test-section {
margin: 20px 0;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 5px;
background-color: #fafafa;
}
button {
background-color: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
font-size: 14px;
}
button:hover {
background-color: #0056b3;
}
.success {
color: #28a745;
font-weight: bold;
}
.error {
color: #dc3545;
font-weight: bold;
}
.result {
margin-top: 15px;
padding: 15px;
border-radius: 5px;
background-color: white;
border: 1px solid #ddd;
white-space: pre-wrap;
font-family: 'Courier New', monospace;
font-size: 13px;
}
.input-group {
margin: 10px 0;
}
label {
display: inline-block;
width: 100px;
font-weight: bold;
}
input {
padding: 5px 10px;
border: 1px solid #ccc;
border-radius: 3px;
width: 400px;
}
</style>
</head>
<body>
<div class="container">
<h1>CORS content-disposition 头测试</h1>
<div class="input-group">
<label for="baseUrl">服务地址:</label>
<input type="text" id="baseUrl" value="http://localhost:48080" placeholder="请输入服务器地址,如: http://localhost:48080">
</div>
<div class="test-section">
<h3>测试1: 获取 CORS 配置信息</h3>
<p>这个测试会调用接口获取当前的 CORS 配置信息</p>
<button onclick="testCorsInfo()">测试 CORS 配置</button>
<div id="corsInfoResult" class="result" style="display: none;"></div>
</div>
<div class="test-section">
<h3>测试2: 文件下载响应头测试</h3>
<p>这个测试会检查是否能获取到 content-disposition 响应头</p>
<button onclick="testDownloadHeaders()">测试下载响应头</button>
<button onclick="downloadFile()">直接下载文件</button>
<div id="downloadResult" class="result" style="display: none;"></div>
</div>
<div class="test-section">
<h3>测试结果说明</h3>
<ul>
<li><strong>成功</strong>: 能够获取到 content-disposition 头,说明 CORS 配置正确</li>
<li><strong>失败</strong>: 无法获取响应头,可能是 CORS 配置问题</li>
<li>请确保服务已启动,并且地址配置正确</li>
</ul>
</div>
</div>
<script>
function getBaseUrl() {
return document.getElementById('baseUrl').value.trim();
}
async function testCorsInfo() {
const resultDiv = document.getElementById('corsInfoResult');
resultDiv.style.display = 'block';
resultDiv.innerHTML = '正在测试...';
try {
const response = await fetch(`${getBaseUrl()}/admin-api/template/test/cors-info`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
const data = await response.json();
let result = `状态码: ${response.status}\n`;
result += `响应头信息:\n`;
for (let [key, value] of response.headers.entries()) {
result += ` ${key}: ${value}\n`;
}
result += `\n响应数据:\n${JSON.stringify(data, null, 2)}`;
if (response.ok) {
resultDiv.innerHTML = `<span class="success">✓ 接口调用成功</span>\n${result}`;
} else {
resultDiv.innerHTML = `<span class="error">✗ 接口调用失败</span>\n${result}`;
}
} catch (error) {
resultDiv.innerHTML = `<span class="error">✗ 请求失败</span>\n错误信息: ${error.message}`;
}
}
async function testDownloadHeaders() {
const resultDiv = document.getElementById('downloadResult');
resultDiv.style.display = 'block';
resultDiv.innerHTML = '正在测试...';
try {
const response = await fetch(`${getBaseUrl()}/admin-api/template/test/download`, {
method: 'GET'
});
let result = `状态码: ${response.status}\n`;
result += `响应头信息:\n`;
let hasContentDisposition = false;
let contentDisposition = null;
for (let [key, value] of response.headers.entries()) {
result += ` ${key}: ${value}\n`;
if (key.toLowerCase() === 'content-disposition') {
hasContentDisposition = true;
contentDisposition = value;
}
}
const responseText = await response.text();
result += `\n响应内容:\n${responseText.substring(0, 200)}${responseText.length > 200 ? '...' : ''}`;
if (hasContentDisposition) {
result = `<span class="success">✓ 成功获取 content-disposition 头</span>\n` +
`Content-Disposition: ${contentDisposition}\n\n` + result;
} else {
result = `<span class="error">✗ 无法获取 content-disposition 头</span>\n` + result;
}
resultDiv.innerHTML = result;
} catch (error) {
resultDiv.innerHTML = `<span class="error">✗ 请求失败</span>\n错误信息: ${error.message}`;
}
}
function downloadFile() {
const url = `${getBaseUrl()}/admin-api/template/test/download`;
const link = document.createElement('a');
link.href = url;
link.download = '';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
const resultDiv = document.getElementById('downloadResult');
resultDiv.style.display = 'block';
resultDiv.innerHTML = `<span class="success">✓ 已触发文件下载</span>\n请检查浏览器下载文件夹`;
}
</script>
</body>
</html>