单点登录(Single Sign-On,SSO)是一种身份验证和授权机制,允许用户使用一组凭据(如用户名和密码)在多个应用程序中进行身份验证,而无需为每个应用程序单独登录。下面是两种常见的实现单点登录的方式:
1. 基于令牌的 SSO
- 用户在登录时提供凭据进行身份验证,并成功通过认证后,身份提供者(Identity Provider,IdP)会生成一个令牌(通常是 JSON Web Token,JWT)并返回给用户。
- 用户访问其他应用程序时,应用程序通过将令牌发送给身份提供者进行验证和解析,以确认用户的身份和权限。
- 如果令牌有效且用户具有适当的权限,应用程序将用户视为已经身份验证,并使其访问应用程序资源。
以下是一个基于令牌的单点登录(SSO)的代码案例。在这个案例中,我们使用 Node.js 和 JSON Web Token(JWT)库来实现。
首先,我们需要安装 jsonwebtoken
包。在终端中运行以下命令进行安装:
npm install jsonwebtoken
然后,我们创建一个 ssoProvider.js
文件,用于模拟身份提供者(Identity Provider)的功能:
// ssoProvider.js
const jwt = require('jsonwebtoken');
const secretKey = 'your-secret-key'; // 指定用于签名令牌的密钥
// 模拟用户数据库,存储用户信息
const users = [
{ id: 1, username: 'user1', password: 'password1' },
{ id: 2, username: 'user2', password: 'password2' },
];
// 用户登录验证,生成 JWT 令牌
function login(username, password) {
const user = users.find((user) => user.username === username && user.password === password);
if (!user) {
throw new Error('Invalid credentials');
}
// 生成 JWT 令牌
const token = jwt.sign({ userId: user.id }, secretKey, { expiresIn: '1h' });
return token;
}
// 验证 JWT 令牌的有效性
function verifyToken(token) {
try {
const decoded = jwt.verify(token, secretKey);
return { success: true, userId: decoded.userId };
} catch (err) {
return { success: false, error: err.message };
}
}
module.exports = { login, verifyToken };
接下来,我们创建一个 app.js
文件,用于演示应用程序如何使用令牌进行单点登录:
// app.js
const express = require('express');
const bodyParser = require('body-parser');
const ssoProvider = require('./ssoProvider');
const app = express();
app.use(bodyParser.json());
// 模拟应用程序的身份认证中间件
function authenticate(req, res, next) {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
// 验证令牌有效性
const verificationResult = ssoProvider.verifyToken(token);
if (!verificationResult.success) {
return res.status(401).json({ error: verificationResult.error });
}
req.userId = verificationResult.userId;
next();
}
// 登录路由,验证用户凭据并返回令牌
app.post('/login', (req, res) => {
const { username, password } = req.body;
try {
const token = ssoProvider.login(username, password);
res.json({ token });
} catch (err) {
res.status(401).json({ error: err.message });
}
});
// 受保护的路由,需要身份验证才能访问
app.get('/protected', authenticate, (req, res) => {
res.json({ message: 'You have accessed the protected route' });
});
// 启动服务器
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
在这个案例中,ssoProvider.js
文件模拟了身份提供者的功能,包括用户登录验证和令牌生成。app.js
文件演示了应用程序如何使用令牌进行身份验证并保护特定的路由。
你可以使用 Postman 或类似的工具来测试这个案例。首先,使用 POST 请求访问 /login
路由,发送用户名和密码作为请求体的 JSON 数据。如果提供的凭据有效,将返回一个包含令牌的 JSON 响应。
然后,你可以使用获得的令牌在请求头中设置 Authorization
字段,使用 GET 请求访问 /protected
路由。如果令牌有效,你将收到一个带有访问成功消息的 JSON 响应。
这个案例演示了基于令牌的单点登录的基本原理,通过令牌进行身份验证和授权。实际应用中,会根据具体需求和场景进行更多的安全性和扩展性的考虑,例如添加过期时间、刷新令牌等功能。
2. 基于代理的 SSO
- 使用一个中心化的身份提供者(Identity Provider,IdP),该身份提供者负责处理用户的身份验证和授权请求。
- 应用程序不直接处理用户的身份验证,而是将身份验证请求重定向到身份提供者。
- 身份提供者验证用户的凭据,并在验证成功后将一个身份凭据(如加密的会话标识符)返回给用户的浏览器。
- 用户访问其他应用程序时,应用程序通过获取该身份凭据,将其传递给身份提供者进行验证,以确认用户的身份和权限。
- 如果身份凭据有效且用户具有适当的权限,应用程序将用户视为已经身份验证,并使其访问应用程序资源。
以下是一个基于代理的单点登录(SSO)的代码案例。在这个案例中,我们使用 Node.js 和 Express 来实现。
首先,我们需要安装 express
和 axios
包。在终端中运行以下命令进行安装:
npm install express axios
然后,我们创建一个 ssoProvider.js
文件,用于模拟身份提供者(Identity Provider)的功能:
// ssoProvider.js
const users = [
{ id: 1, username: 'user1', password: 'password1' },
{ id: 2, username: 'user2', password: 'password2' },
];
function login(username, password) {
const user = users.find((user) => user.username === username && user.password === password);
if (!user) {
throw new Error('Invalid credentials');
}
return user.id;
}
module.exports = { login };
接下来,我们创建一个 app.js
文件,用于演示应用程序如何使用代理进行单点登录:
// app.js
const express = require('express');
const axios = require('axios');
const ssoProvider = require('./ssoProvider');
const app = express();
app.use(express.json());
// 登录路由,验证用户凭据并返回用户ID
app.post('/login', (req, res) => {
const { username, password } = req.body;
try {
const userId = ssoProvider.login(username, password);
res.json({ userId });
} catch (err) {
res.status(401).json({ error: err.message });
}
});
// 受保护的路由,需要验证身份后才能访问
app.get('/protected', async (req, res) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
// 向身份提供者验证令牌的有效性
const response = await axios.post('http://localhost:3001/verify', { token });
const { success, userId } = response.data;
if (success) {
res.json({ message: `You have accessed the protected route with user ID: ${userId}` });
} else {
res.status(401).json({ error: 'Invalid token' });
}
} catch (err) {
console.error(err);
res.status(500).json({ error: 'An error occurred' });
}
});
// 启动服务器
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
接下来,我们创建一个 ssoConsumer.js
文件,用于模拟需要进行单点登录的应用程序:
// ssoConsumer.js
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
// 模拟代理服务器,验证令牌并将结果返回给应用程序
app.post('/verify', (req, res) => {
const { token } = req.body;
// 在这里执行对令牌的验证逻辑(根据你的需求和实际情况)
// 这里只是简单地将令牌解析为用户ID
const userId = parseInt(token);
if (!isNaN(userId)) {
res.json({ success: true, userId });
} else {
res.json({ success: false });
}
});
// 启动代理服务器
app.listen(3001, () => {
console.log('SSO Proxy listening on port 3001');
});
在这个案例中,ssoProvider.js
文件模拟了身份提供者的功能,包括用户登录验证并返回用户ID。app.js
文件演示了应用程序如何使用代理进行单点登录,在访问受保护的路由时,通过向代理服务器验证令牌的有效性来实现身份验证。
你可以使用 Postman 或类似的工具来测试这个案例。首先,使用 POST 请求访问 /login
路由,发送用户名和密码作为请求体的 JSON 数据。如果提供的凭据有效,将返回一个包含用户ID的 JSON 响应。
然后,使用获得的用户ID,在请求头中设置 Authorization
字段,使用 GET 请求访问 /protected
路由。代理服务器将验证令牌的有效性,并将结果返回给应用程序。
这个案例演示了基于代理的单点登录的基本原理,通过代理服务器处理令牌验证逻辑,以实现跨多个应用程序的单点登录功能。实际应用中,会根据具体需求和场景进行更多的安全性和扩展性的考虑,例如添加加密、刷新令牌、多个身份提供者
等功能。
无论使用哪种方式实现单点登录,关键点在于身份提供者的能力,它负责处理用户身份验证的逻辑并管理相关的用户信息和权限
。其他应用程序则依赖身份提供者来验证用户的身份,并根据认证结果授予或限制用户的访问权限。这样可以简化用户的登录体验,提高整体系统的安全性与可维护性。