一站式网上办事大厅
小李:最近我们公司要开发一个“一站式网上办事大厅”,你觉得应该怎么做?
小张:这个项目听起来挺有挑战性的。首先,你需要明确“一站式”的定义,它通常意味着用户可以在一个平台上完成多种业务操作,比如申请、审批、查询等。
小李:对,我们希望厂家也能通过这个平台进行登录和管理他们的业务。
小张:那我们就需要一个安全的登录系统。你可以考虑使用OAuth2.0或者JWT来处理认证。
小李:JWT是什么?能详细说说吗?
小张:JWT是JSON Web Token的缩写,它是一种用于在客户端和服务器之间传递信息的安全方式。你可以在用户登录后生成一个token,然后将其存储在客户端,比如浏览器的localStorage中。
小李:那具体的代码怎么写呢?
小张:我可以给你一个简单的例子。首先,我们需要一个登录接口,用户输入用户名和密码后,服务器验证成功后返回一个JWT。
小李:好的,那我先写一个登录接口的代码吧。
小张:可以这样写:
// Node.js 示例
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
const users = [
{ id: 1, username: 'admin', password: '123456' }
];
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username && u.password === password);
if (!user) return res.status(401).json({ message: '用户名或密码错误' });
const token = jwt.sign({ userId: user.id }, 'your-secret-key', { expiresIn: '1h' });
res.json({ token });
});
app.listen(3000, () => console.log('Server running on port 3000'));
小李:这段代码看起来不错,但怎么在前端使用这个token呢?
小张:前端在收到token后,可以将其保存在localStorage中,然后每次请求都需要带上这个token。比如,在axios请求中添加一个拦截器。
小李:那前端代码应该怎么写呢?
小张:这里是一个简单的例子:
// 前端使用Axios
import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:3000'
});
api.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, error => {
return Promise.reject(error);
});
// 登录函数
async function login(username, password) {
try {
const response = await api.post('/login', { username, password });
localStorage.setItem('token', response.data.token);
alert('登录成功!');
} catch (error) {
alert('登录失败:' + error.response?.data?.message || '未知错误');
}
}
小李:明白了,这样就可以实现登录功能了。但是,我们还需要确保安全性。
小张:没错,JWT虽然方便,但必须注意签名密钥的保密性。此外,还可以在服务器端设置一个黑名单,防止token被篡改或盗用。
小李:那如果用户登出怎么办?
小张:对于JWT来说,传统的“登出”机制并不容易实现,因为token是无状态的。不过,你可以采用一些变通方法,比如在服务器端维护一个黑名单,记录无效的token。
小李:听起来有点复杂,有没有更简单的方法?
小张:另一种方法是使用refresh token,即当用户登录时,除了发放一个短期有效的access token外,还发放一个长期的refresh token。当access token过期时,用户可以通过refresh token重新获取新的access token。
小李:那这样的代码该怎么实现呢?
小张:我们可以修改之前的登录接口,让它同时返回access token和refresh token,然后在中间件中检查token的有效性。
小李:好的,那我们再来看一下整个系统的架构。
小张:系统整体分为前端、后端和数据库三部分。前端负责用户交互和登录界面;后端负责处理登录逻辑、生成和验证token;数据库则存储用户信息和token黑名单。
小李:那数据表的设计应该怎么安排呢?
小张:可以设计一个users表,存储用户的ID、用户名、密码(加密存储)等信息;另一个tokens表,存储token和其过期时间,以及是否被撤销。
小李:那具体的SQL语句应该怎么写呢?

小张:这里是一个简单的示例:
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- token表
CREATE TABLE tokens (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
token VARCHAR(255) NOT NULL,
expires_at TIMESTAMP,
revoked BOOLEAN DEFAULT FALSE,
FOREIGN KEY (user_id) REFERENCES users(id)
);
小李:这样设计的话,可以更好地管理token的状态。
小张:没错,而且还可以在每次请求时检查token是否有效,避免非法访问。

小李:那我们在后端如何验证token呢?
小张:可以用中间件来处理,例如在Express中,可以创建一个验证token的函数。
小李:那代码应该怎么写呢?
小张:这里是一个示例:
// 验证token的中间件
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, 'your-secret-key', (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
// 使用中间件
app.get('/protected', authenticateToken, (req, res) => {
res.send('这是一个受保护的路由');
});
小李:这样就能保证只有合法用户才能访问某些资源。
小张:是的,但还要注意token的存储和传输安全。建议使用HTTPS,防止token被截获。
小李:那我们是不是还需要考虑多厂商登录的问题?
小张:对,如果是多个厂家登录,可能需要引入OAuth2.0,让厂家通过第三方平台(如微信、支付宝)登录,这样可以减少我们自己的登录系统压力。
小李:那这样的话,代码又会变得复杂吗?
小张:确实会复杂一些,但可以借助现有的库来简化开发,比如passport.js。
小李:那我们是不是还需要一个统一的API网关?
小张:是的,API网关可以帮助我们统一处理所有请求,比如身份验证、负载均衡、日志记录等,提升系统的可维护性和扩展性。
小李:那这对我们后续的系统扩展会有很大帮助。
小张:没错,所以你现在不仅要考虑登录功能,还要为未来的扩展做好准备。
小李:看来这个“一站式网上办事大厅”不仅仅是简单的登录系统,而是一个综合性的平台。
小张:没错,它的核心是用户体验和系统安全性,而登录只是其中的一部分。
小李:谢谢你的讲解,我现在对这个项目有了更清晰的认识。
小张:不客气,有问题随时问我。