架构设计
- 独立存储:将用户认证状态等敏感信息存储在独立的持久化存储中,如数据库(如Redis)。这样即使多个实例并行处理请求,数据来源唯一且一致。
- 中间件:在请求进入React服务端渲染流程前,通过中间件从存储中获取认证信息,并将其注入到请求上下文(如Express的
req
对象)。这样在后续的渲染过程中,每个请求都能获取到正确的认证状态。
- 缓存机制:对于频繁访问且相对稳定的认证信息,可以在服务端设置缓存。例如,使用内存缓存(如Node.js的
node-cache
),减少对持久化存储的频繁访问,提高性能。但要注意缓存的更新策略,确保数据一致性。
代码实现
- Context 创建与使用
import React from'react';
const AuthContext = React.createContext();
export default AuthContext;
- 注入数据:在服务端渲染时,将从请求上下文获取的认证信息通过`Provider`注入到组件树。
import React from'react';
import AuthContext from './AuthContext';
const serverRender = (req, res) => {
const authInfo = req.authInfo; // 假设中间件已经将认证信息注入到req.authInfo
const app = (
<AuthContext.Provider value={authInfo}>
{/* 应用的根组件 */}
</AuthContext.Provider>
);
// 后续的渲染逻辑
};
- 使用数据:组件通过`Consumer`或`useContext`钩子获取认证信息。
import React from'react';
import AuthContext from './AuthContext';
const MyComponent = () => {
const authInfo = React.useContext(AuthContext);
return (
<div>
{authInfo && <p>User is authenticated: {authInfo.isAuthenticated}</p>}
</div>
);
};
export default MyComponent;
- 数据更新:当认证状态发生变化(如用户登录、登出)时,通过服务端API更新持久化存储,并同时更新缓存(如果有)。例如,在用户登出时:
import redis from'redis';
const client = redis.createClient();
const logout = (req, res) => {
// 更新数据库中的认证状态
client.del('user:auth:' + req.user.id);
// 更新缓存(如果有)
// 假设使用node-cache
const NodeCache = require('node-cache');
const cache = new NodeCache();
cache.del('user:auth:' + req.user.id);
res.send('Logged out successfully');
};
测试方案
- 单元测试:
- 测试
Context
相关组件:使用测试框架(如Jest和React Testing Library)测试Context
的创建、Provider
和Consumer
的功能。例如,测试MyComponent
在不同认证状态下的渲染情况。
import React from'react';
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
import AuthContext from './AuthContext';
describe('MyComponent', () => {
test('renders when user is authenticated', () => {
const authInfo = { isAuthenticated: true };
render(
<AuthContext.Provider value={authInfo}>
<MyComponent />
</AuthContext.Provider>
);
expect(screen.getByText('User is authenticated: true')).toBeInTheDocument();
});
test('renders when user is not authenticated', () => {
const authInfo = { isAuthenticated: false };
render(
<AuthContext.Provider value={authInfo}>
<MyComponent />
</AuthContext.Provider>
);
expect(screen.queryByText('User is authenticated: true')).not.toBeInTheDocument();
});
});
- 集成测试:
- 测试整个服务端渲染流程:使用工具(如Supertest)模拟HTTP请求,测试从请求进入到渲染完成的整个过程中,认证信息是否正确传递和使用。例如,测试登录后访问需要认证的页面,检查是否能正确渲染相关内容。
import request from'supertest';
import app from './app';
describe('Server Rendering with Authentication', () => {
let cookie;
beforeEach(async () => {
const loginRes = await request(app)
.post('/login')
.send({ username: 'test', password: 'test' });
cookie = loginRes.headers['set - cookie'];
});
test('renders authenticated page correctly', async () => {
const res = await request(app)
.get('/authenticated - page')
.set('Cookie', cookie);
expect(res.text).toContain('User is authenticated: true');
});
});
- 性能测试:使用工具(如Artillery)模拟高并发请求,测试在多请求环境下系统的性能表现,确保认证信息的获取和处理不会成为性能瓶颈。例如,测试在100个并发请求下,系统的响应时间和吞吐量。
config:
target: 'http://localhost:3000'
phases:
- duration: 60
arrivalRate: 100
scenarios:
- flow:
- get:
url: '/authenticated - page'
headers:
Cookie: 'your - authentication - cookie'