v0.1.0
This commit is contained in:
274
examples/oauth-client-example.html
Normal file
274
examples/oauth-client-example.html
Normal file
@ -0,0 +1,274 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>OAuth客户端示例</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: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
button:disabled {
|
||||
background-color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.result {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
white-space: pre-wrap;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
.success {
|
||||
background-color: #d4edda;
|
||||
border: 1px solid #c3e6cb;
|
||||
color: #155724;
|
||||
}
|
||||
.error {
|
||||
background-color: #f8d7da;
|
||||
border: 1px solid #f5c6cb;
|
||||
color: #721c24;
|
||||
}
|
||||
.info {
|
||||
background-color: #d1ecf1;
|
||||
border: 1px solid #bee5eb;
|
||||
color: #0c5460;
|
||||
}
|
||||
.step {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.step h3 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>OAuth 2.0 客户端示例</h1>
|
||||
<p>这个页面演示了如何使用我们的OAuth提供商进行身份验证。</p>
|
||||
|
||||
<div class="step">
|
||||
<h3>步骤 1: 配置OAuth客户端</h3>
|
||||
<div class="form-group">
|
||||
<label for="clientId">客户端ID:</label>
|
||||
<input type="text" id="clientId" placeholder="输入你的OAuth客户端ID">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="clientSecret">客户端密钥:</label>
|
||||
<input type="text" id="clientSecret" placeholder="输入你的OAuth客户端密钥">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="redirectUri">重定向URI:</label>
|
||||
<input type="text" id="redirectUri" value="http://localhost:3001/callback" readonly>
|
||||
</div>
|
||||
<button onclick="testOAuthFlow()">开始OAuth流程</button>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<h3>步骤 2: 获取授权码</h3>
|
||||
<p>点击上面的按钮开始OAuth授权流程。你需要先登录到OAuth提供商。</p>
|
||||
<div id="authResult" class="result" style="display: none;"></div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<h3>步骤 3: 交换访问令牌</h3>
|
||||
<button onclick="exchangeToken()" id="exchangeBtn" disabled>交换访问令牌</button>
|
||||
<div id="tokenResult" class="result" style="display: none;"></div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<h3>步骤 4: 获取用户信息</h3>
|
||||
<button onclick="getUserInfo()" id="userInfoBtn" disabled>获取用户信息</button>
|
||||
<div id="userInfoResult" class="result" style="display: none;"></div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<h3>步骤 5: 撤销令牌</h3>
|
||||
<button onclick="revokeToken()" id="revokeBtn" disabled>撤销访问令牌</button>
|
||||
<div id="revokeResult" class="result" style="display: none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let authCode = '';
|
||||
let accessToken = '';
|
||||
let refreshToken = '';
|
||||
|
||||
async function testOAuthFlow() {
|
||||
const clientId = document.getElementById('clientId').value;
|
||||
const redirectUri = document.getElementById('redirectUri').value;
|
||||
|
||||
if (!clientId) {
|
||||
showResult('authResult', '请输入客户端ID', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 构建授权URL
|
||||
const authUrl = new URL('http://localhost:3000/api/oauth/authorize');
|
||||
authUrl.searchParams.set('response_type', 'code');
|
||||
authUrl.searchParams.set('client_id', clientId);
|
||||
authUrl.searchParams.set('redirect_uri', redirectUri);
|
||||
authUrl.searchParams.set('scope', 'read write');
|
||||
authUrl.searchParams.set('state', 'test_state_' + Date.now());
|
||||
|
||||
showResult('authResult', `正在重定向到授权页面...\n${authUrl.toString()}`, 'info');
|
||||
|
||||
// 在实际应用中,这里会重定向到授权页面
|
||||
// 为了演示,我们模拟一个授权码
|
||||
authCode = 'demo_auth_code_' + Date.now();
|
||||
showResult('authResult', `模拟授权成功!\n授权码: ${authCode}`, 'success');
|
||||
|
||||
document.getElementById('exchangeBtn').disabled = false;
|
||||
} catch (error) {
|
||||
showResult('authResult', `授权失败: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function exchangeToken() {
|
||||
const clientId = document.getElementById('clientId').value;
|
||||
const clientSecret = document.getElementById('clientSecret').value;
|
||||
const redirectUri = document.getElementById('redirectUri').value;
|
||||
|
||||
if (!clientId || !clientSecret) {
|
||||
showResult('tokenResult', '请输入客户端ID和密钥', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/oauth/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
grant_type: 'authorization_code',
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
code: authCode,
|
||||
redirect_uri: redirectUri
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
accessToken = data.data.access_token;
|
||||
refreshToken = data.data.refresh_token;
|
||||
showResult('tokenResult', `令牌交换成功!\n访问令牌: ${accessToken.substring(0, 20)}...\n刷新令牌: ${refreshToken.substring(0, 20)}...`, 'success');
|
||||
document.getElementById('userInfoBtn').disabled = false;
|
||||
document.getElementById('revokeBtn').disabled = false;
|
||||
} else {
|
||||
showResult('tokenResult', `令牌交换失败: ${data.message}`, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showResult('tokenResult', `请求失败: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function getUserInfo() {
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/oauth/userinfo', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showResult('userInfoResult', `用户信息获取成功!\n${JSON.stringify(data.data, null, 2)}`, 'success');
|
||||
} else {
|
||||
showResult('userInfoResult', `获取用户信息失败: ${data.message}`, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showResult('userInfoResult', `请求失败: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function revokeToken() {
|
||||
const clientId = document.getElementById('clientId').value;
|
||||
const clientSecret = document.getElementById('clientSecret').value;
|
||||
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/oauth/revoke', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
token: accessToken,
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showResult('revokeResult', '令牌撤销成功!', 'success');
|
||||
accessToken = '';
|
||||
refreshToken = '';
|
||||
document.getElementById('userInfoBtn').disabled = true;
|
||||
document.getElementById('revokeBtn').disabled = true;
|
||||
} else {
|
||||
showResult('revokeResult', `令牌撤销失败: ${data.message}`, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showResult('revokeResult', `请求失败: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function showResult(elementId, message, type) {
|
||||
const element = document.getElementById(elementId);
|
||||
element.textContent = message;
|
||||
element.className = `result ${type}`;
|
||||
element.style.display = 'block';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user