202 lines
7.6 KiB
HTML
202 lines
7.6 KiB
HTML
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="zh-CN">
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8">
|
||
|
|
<title>OIDC配置测试</title>
|
||
|
|
<style>
|
||
|
|
body {
|
||
|
|
font-family: 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);
|
||
|
|
}
|
||
|
|
.test-section {
|
||
|
|
margin: 20px 0;
|
||
|
|
padding: 15px;
|
||
|
|
border: 1px solid #ddd;
|
||
|
|
border-radius: 5px;
|
||
|
|
}
|
||
|
|
.success { background-color: #d4edda; color: #155724; border-color: #c3e6cb; }
|
||
|
|
.error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
|
||
|
|
button {
|
||
|
|
background-color: #007bff;
|
||
|
|
color: white;
|
||
|
|
border: none;
|
||
|
|
padding: 10px 20px;
|
||
|
|
border-radius: 5px;
|
||
|
|
cursor: pointer;
|
||
|
|
margin: 5px;
|
||
|
|
}
|
||
|
|
button:hover { background-color: #0056b3; }
|
||
|
|
pre {
|
||
|
|
background-color: #f8f9fa;
|
||
|
|
padding: 10px;
|
||
|
|
border-radius: 5px;
|
||
|
|
overflow-x: auto;
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<div class="container">
|
||
|
|
<h1>OIDC配置测试</h1>
|
||
|
|
|
||
|
|
<div class="test-section">
|
||
|
|
<h3>1. 当前配置</h3>
|
||
|
|
<pre id="config"></pre>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="test-section">
|
||
|
|
<h3>2. 测试OIDC端点</h3>
|
||
|
|
<button onclick="testOidcEndpoints()">测试OIDC端点</button>
|
||
|
|
<div id="endpointResults"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="test-section">
|
||
|
|
<h3>3. 测试登录流程</h3>
|
||
|
|
<button onclick="testLoginFlow()">测试登录流程</button>
|
||
|
|
<div id="loginResults"></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="test-section">
|
||
|
|
<h3>4. 当前Token状态</h3>
|
||
|
|
<button onclick="checkTokenStatus()">检查Token状态</button>
|
||
|
|
<div id="tokenResults"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
// OIDC配置
|
||
|
|
const oidcConfig = {
|
||
|
|
clientId: 'a-client',
|
||
|
|
clientSecret: 'a-secret',
|
||
|
|
redirectUri: 'https://a.com/callback',
|
||
|
|
authorizationEndpoint: 'https://oidc.com/oauth2/authorize',
|
||
|
|
tokenEndpoint: 'https://oidc.com/oauth2/token',
|
||
|
|
userInfoEndpoint: 'https://oidc.com/userinfo',
|
||
|
|
jwksEndpoint: 'https://oidc.com/oauth2/jwks',
|
||
|
|
scope: 'openid read'
|
||
|
|
};
|
||
|
|
|
||
|
|
// 显示配置
|
||
|
|
document.getElementById('config').textContent = JSON.stringify(oidcConfig, null, 2);
|
||
|
|
|
||
|
|
// 测试OIDC端点
|
||
|
|
async function testOidcEndpoints() {
|
||
|
|
const results = document.getElementById('endpointResults');
|
||
|
|
results.innerHTML = '<p>正在测试端点...</p>';
|
||
|
|
|
||
|
|
const tests = [
|
||
|
|
{ name: 'JWKS端点', url: oidcConfig.jwksEndpoint },
|
||
|
|
{ name: '授权端点', url: oidcConfig.authorizationEndpoint },
|
||
|
|
{ name: 'Token端点', url: oidcConfig.tokenEndpoint },
|
||
|
|
{ name: '用户信息端点', url: oidcConfig.userInfoEndpoint }
|
||
|
|
];
|
||
|
|
|
||
|
|
let resultsHtml = '';
|
||
|
|
|
||
|
|
for (const test of tests) {
|
||
|
|
try {
|
||
|
|
const response = await fetch(test.url, { method: 'GET' });
|
||
|
|
const status = response.status;
|
||
|
|
const statusText = response.statusText;
|
||
|
|
|
||
|
|
if (status === 200 || status === 401) {
|
||
|
|
resultsHtml += `<p class="success">✅ ${test.name}: ${status} ${statusText}</p>`;
|
||
|
|
} else {
|
||
|
|
resultsHtml += `<p class="error">❌ ${test.name}: ${status} ${statusText}</p>`;
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
resultsHtml += `<p class="error">❌ ${test.name}: 连接失败 - ${error.message}</p>`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
results.innerHTML = resultsHtml;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 测试登录流程
|
||
|
|
function testLoginFlow() {
|
||
|
|
const results = document.getElementById('loginResults');
|
||
|
|
results.innerHTML = '<p>正在生成登录URL...</p>';
|
||
|
|
|
||
|
|
const state = generateRandomString();
|
||
|
|
localStorage.setItem('oauth_state', state);
|
||
|
|
|
||
|
|
const authUrl = new URL(oidcConfig.authorizationEndpoint);
|
||
|
|
authUrl.searchParams.set('response_type', 'code');
|
||
|
|
authUrl.searchParams.set('client_id', oidcConfig.clientId);
|
||
|
|
authUrl.searchParams.set('redirect_uri', oidcConfig.redirectUri);
|
||
|
|
authUrl.searchParams.set('scope', oidcConfig.scope);
|
||
|
|
authUrl.searchParams.set('state', state);
|
||
|
|
|
||
|
|
results.innerHTML = `
|
||
|
|
<p class="success">✅ 登录URL已生成</p>
|
||
|
|
<p><strong>State:</strong> ${state}</p>
|
||
|
|
<p><strong>授权URL:</strong></p>
|
||
|
|
<pre>${authUrl.toString()}</pre>
|
||
|
|
<button onclick="window.open('${authUrl.toString()}', '_blank')">在新窗口打开登录</button>
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 检查Token状态
|
||
|
|
function checkTokenStatus() {
|
||
|
|
const results = document.getElementById('tokenResults');
|
||
|
|
const token = localStorage.getItem('access_token');
|
||
|
|
const refreshToken = localStorage.getItem('refresh_token');
|
||
|
|
const state = localStorage.getItem('oauth_state');
|
||
|
|
|
||
|
|
let html = '<h4>本地存储状态:</h4>';
|
||
|
|
html += `<p><strong>Access Token:</strong> ${token ? '已保存' : '未保存'}</p>`;
|
||
|
|
html += `<p><strong>Refresh Token:</strong> ${refreshToken ? '已保存' : '未保存'}</p>`;
|
||
|
|
html += `<p><strong>OAuth State:</strong> ${state ? '已保存' : '未保存'}</p>`;
|
||
|
|
|
||
|
|
if (token) {
|
||
|
|
html += '<h4>Token详情:</h4>';
|
||
|
|
html += `<p><strong>Token前50字符:</strong> ${token.substring(0, 50)}...</p>`;
|
||
|
|
html += `<button onclick="validateToken()">验证Token有效性</button>`;
|
||
|
|
}
|
||
|
|
|
||
|
|
results.innerHTML = html;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 验证Token
|
||
|
|
async function validateToken() {
|
||
|
|
const token = localStorage.getItem('access_token');
|
||
|
|
const results = document.getElementById('tokenResults');
|
||
|
|
|
||
|
|
try {
|
||
|
|
const response = await fetch(oidcConfig.userInfoEndpoint, {
|
||
|
|
headers: {
|
||
|
|
'Authorization': `Bearer ${token}`
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
if (response.ok) {
|
||
|
|
const userInfo = await response.json();
|
||
|
|
results.innerHTML += `
|
||
|
|
<p class="success">✅ Token有效</p>
|
||
|
|
<pre>${JSON.stringify(userInfo, null, 2)}</pre>
|
||
|
|
`;
|
||
|
|
} else {
|
||
|
|
results.innerHTML += `<p class="error">❌ Token无效: ${response.status} ${response.statusText}</p>`;
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
results.innerHTML += `<p class="error">❌ Token验证失败: ${error.message}</p>`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 生成随机字符串
|
||
|
|
function generateRandomString() {
|
||
|
|
const array = new Uint32Array(28);
|
||
|
|
window.crypto.getRandomValues(array);
|
||
|
|
return Array.from(array, dec => ('0' + dec.toString(16)).substr(-2)).join('');
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
</body>
|
||
|
|
</html>
|