@{
Layout = null;
}
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<title>SecurityStamp迁移工具 - 认证中心</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="@Url.Content("~/css/global.css")" asp-append-version="true"/>
</head>
<body>
<div class="container wide-container">
<div class="header">
<h1>SecurityStamp迁移工具</h1>
<p>将现有用户的SecurityStamp更新为新的安全格式</p>
</div>
@if (TempData["Message"] is string message)
{
<div class="success-message">
@message
</div>
}
@if (TempData["Error"] is string error)
{
<div class="error-message">
@error
</div>
}
<!-- 批量迁移区域 -->
<div class="section">
<h2>批量迁移</h2>
<p>此操作将更新所有用户的SecurityStamp为新的安全格式。建议在系统维护期间执行。</p>
<div class="warning-section">
<div class="warning-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
<line x1="12" y1="9" x2="12" y2="13"></line>
<line x1="12" y1="17" x2="12.01" y2="17"></line>
</svg>
</div>
<div class="warning-content">
<h3>重要提醒</h3>
<p>执行迁移后,所有用户将被强制重新登录。请确保在合适的时间执行此操作。</p>
</div>
</div>
<form method="post" asp-action="MigrateSecurityStamps" onsubmit="return confirmMigration()">
@Html.AntiForgeryToken()
<button type="submit" class="danger-button">
执行批量迁移
</button>
</form>
</div>
<!-- 单用户操作区域 -->
<div class="section">
<h2>单用户操作</h2>
<div class="input-section">
<label class="input-label" for="userId">用户ID</label>
<div class="input-wrapper">
<input
id="userId"
type="text"
class="text-input"
placeholder="请输入用户ID"
/>
</div>
</div>
<div class="action-buttons">
<button type="button" class="submit-button" onclick="validateUserSecurityStamp()">
验证SecurityStamp
</button>
<button type="button" class="danger-button" onclick="forceUserReauth()">
强制重新登录
</button>
</div>
<div id="userResult" class="result-section" style="display: none;">
<!-- 结果将在这里显示 -->
</div>
</div>
<div class="footer">
<p>SecurityStamp迁移工具 - 仅限系统管理员使用</p>
</div>
</div>
<script src="@Url.Content("~/js/ui-components.js")" asp-append-version="true"></script>
<script>
function confirmMigration() {
return confirm('您确定要执行SecurityStamp批量迁移吗?\n\n此操作将:\n1. 更新所有用户的SecurityStamp\n2. 强制所有用户重新登录\n3. 提高系统安全性\n\n建议在系统维护期间执行。');
}
async function validateUserSecurityStamp() {
const userId = document.getElementById('userId').value;
if (!userId.trim()) {
alert('请输入用户ID');
return;
}
try {
const response = await fetch('/validate-user-security-stamp', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
},
body: `userId=${encodeURIComponent(userId)}`
});
const result = await response.json();
displayResult(result, '验证结果');
} catch (error) {
displayResult({ success: false, message: '请求失败: ' + error.message }, '验证结果');
}
}
async function forceUserReauth() {
const userId = document.getElementById('userId').value;
if (!userId.trim()) {
alert('请输入用户ID');
return;
}
if (!confirm(`确定要强制用户 ${userId} 重新登录吗?`)) {
return;
}
try {
const response = await fetch('/force-user-reauth', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
},
body: `userId=${encodeURIComponent(userId)}&reason=${encodeURIComponent('管理员手动操作')}`
});
const result = await response.json();
displayResult(result, '操作结果');
} catch (error) {
displayResult({ success: false, message: '请求失败: ' + error.message }, '操作结果');
}
}
function displayResult(result, title) {
const resultDiv = document.getElementById('userResult');
const messageClass = result.success ? 'success-message' : 'error-message';
let content = `<h3>${title}</h3>`;
content += `<div class="${messageClass}">${result.message}</div>`;
if (result.success && result.currentKey) {
content += `<p><strong>当前SecurityStamp:</strong> ${result.currentKey}</p>`;
content += `<p><strong>格式状态:</strong> ${result.isValid ? '有效' : '无效'}</p>`;
}
resultDiv.innerHTML = content;
resultDiv.style.display = 'block';
}
</script>
<style>
.section {
margin-bottom: 40px;
padding: 24px;
background: var(--bg-secondary);
border-radius: var(--radius);
border: 1px solid var(--border-color);
}
.section h2 {
color: var(--text-primary);
margin-bottom: 12px;
font-size: 20px;
font-weight: 600;
}
.section p {
color: var(--text-secondary);
margin-bottom: 20px;
line-height: 1.5;
}
.result-section {
margin-top: 20px;
padding: 16px;
background: var(--bg-primary);
border-radius: var(--radius-sm);
border: 1px solid var(--border-color);
}
.result-section h3 {
margin-bottom: 12px;
color: var(--text-primary);
font-size: 16px;
}
</style>
</body>
</html>