|
|
@@ -418,6 +418,7 @@ export default WechatLoginButton;
|
|
|
```typescript
|
|
|
// src/client/mobile/pages/AuthPage.tsx - 添加微信登录选项
|
|
|
import WechatLoginButton from '@/client/components/WechatLoginButton';
|
|
|
+import { toast } from 'sonner'
|
|
|
|
|
|
// 在表单后添加微信登录按钮
|
|
|
<div className="mt-6">
|
|
|
@@ -438,7 +439,7 @@ import WechatLoginButton from '@/client/components/WechatLoginButton';
|
|
|
navigate(getReturnUrl(), { replace: true });
|
|
|
}}
|
|
|
onError={(error) => {
|
|
|
- alert(error.message);
|
|
|
+ toast(error.message);
|
|
|
}}
|
|
|
>
|
|
|
微信一键登录
|
|
|
@@ -458,143 +459,6 @@ WECHAT_APP_SECRET=your_wechat_app_secret
|
|
|
WECHAT_REDIRECT_URI=https://yourdomain.com/auth/wechat/callback
|
|
|
```
|
|
|
|
|
|
-### 9. 数据库迁移
|
|
|
-
|
|
|
-创建数据库迁移脚本添加微信字段:
|
|
|
-
|
|
|
-```typescript
|
|
|
-// src/server/migrations/AddWechatFields.ts
|
|
|
-import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
|
-
|
|
|
-export class AddWechatFields1712345678901 implements MigrationInterface {
|
|
|
- public async up(queryRunner: QueryRunner): Promise<void> {
|
|
|
- await queryRunner.query(`
|
|
|
- ALTER TABLE users
|
|
|
- ADD COLUMN wechat_openid VARCHAR(255) NULL COMMENT '微信开放平台openid',
|
|
|
- ADD COLUMN wechat_unionid VARCHAR(255) NULL COMMENT '微信开放平台unionid',
|
|
|
- ADD COLUMN wechat_nickname VARCHAR(255) NULL COMMENT '微信昵称',
|
|
|
- ADD COLUMN wechat_avatar VARCHAR(500) NULL COMMENT '微信头像URL',
|
|
|
- ADD COLUMN wechat_sex TINYINT NULL COMMENT '微信性别(1:男,2:女,0:未知)',
|
|
|
- ADD COLUMN wechat_province VARCHAR(100) NULL COMMENT '微信省份',
|
|
|
- ADD COLUMN wechat_city VARCHAR(100) NULL COMMENT '微信城市',
|
|
|
- ADD COLUMN wechat_country VARCHAR(100) NULL COMMENT '微信国家',
|
|
|
- ADD UNIQUE INDEX idx_wechat_openid (wechat_openid),
|
|
|
- ADD INDEX idx_wechat_unionid (wechat_unionid)
|
|
|
- `);
|
|
|
- }
|
|
|
-
|
|
|
- public async down(queryRunner: QueryRunner): Promise<void> {
|
|
|
- await queryRunner.query(`
|
|
|
- ALTER TABLE users
|
|
|
- DROP COLUMN wechat_openid,
|
|
|
- DROP COLUMN wechat_unionid,
|
|
|
- DROP COLUMN wechat_nickname,
|
|
|
- DROP COLUMN wechat_avatar,
|
|
|
- DROP COLUMN wechat_sex,
|
|
|
- DROP COLUMN wechat_province,
|
|
|
- DROP COLUMN wechat_city,
|
|
|
- DROP COLUMN wechat_country
|
|
|
- `);
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-## 测试验证
|
|
|
-
|
|
|
-### 1. 单元测试
|
|
|
-
|
|
|
-创建微信认证服务的单元测试:
|
|
|
-
|
|
|
-```typescript
|
|
|
-// src/server/modules/wechat/__tests__/wechat-auth.service.spec.ts
|
|
|
-import { WechatAuthService } from '../wechat-auth.service';
|
|
|
-import { UserService } from '../../users/user.service';
|
|
|
-import { AuthService } from '../../auth/auth.service';
|
|
|
-import axios from 'axios';
|
|
|
-
|
|
|
-jest.mock('axios');
|
|
|
-
|
|
|
-describe('WechatAuthService', () => {
|
|
|
- let service: WechatAuthService;
|
|
|
- let userService: UserService;
|
|
|
- let authService: AuthService;
|
|
|
-
|
|
|
- beforeEach(() => {
|
|
|
- userService = {
|
|
|
- findByWechatOpenid: jest.fn(),
|
|
|
- createUser: jest.fn()
|
|
|
- } as any;
|
|
|
-
|
|
|
- authService = {
|
|
|
- generateToken: jest.fn()
|
|
|
- } as any;
|
|
|
-
|
|
|
- service = new WechatAuthService(userService, authService);
|
|
|
- });
|
|
|
-
|
|
|
- it('should be defined', () => {
|
|
|
- expect(service).toBeDefined();
|
|
|
- });
|
|
|
-
|
|
|
- // 添加更多测试用例...
|
|
|
-});
|
|
|
-```
|
|
|
-
|
|
|
-### 2. 集成测试
|
|
|
-
|
|
|
-测试微信认证API端点:
|
|
|
-
|
|
|
-```typescript
|
|
|
-// src/server/api/auth/wechat/__tests__/wechat-auth.e2e-spec.ts
|
|
|
-import { createServer } from 'http';
|
|
|
-import { api } from '@/server/api';
|
|
|
-import * as request from 'supertest';
|
|
|
-
|
|
|
-describe('Wechat Auth (e2e)', () => {
|
|
|
- let server: any;
|
|
|
-
|
|
|
- beforeAll(async () => {
|
|
|
- server = createServer(api.fetch);
|
|
|
- });
|
|
|
-
|
|
|
- afterAll(() => {
|
|
|
- server.close();
|
|
|
- });
|
|
|
-
|
|
|
- it('/auth/wechat (POST) - should return 400 for invalid code', () => {
|
|
|
- return request(server)
|
|
|
- .post('/auth/wechat')
|
|
|
- .send({ code: '' })
|
|
|
- .expect(400);
|
|
|
- });
|
|
|
-
|
|
|
- // 添加更多集成测试...
|
|
|
-});
|
|
|
-```
|
|
|
-
|
|
|
-## 部署说明
|
|
|
-
|
|
|
-### 1. 微信服务号配置
|
|
|
-
|
|
|
-1. 登录微信公众平台
|
|
|
-2. 进入「开发」->「基本配置」
|
|
|
-3. 获取AppID和AppSecret
|
|
|
-4. 进入「设置」->「公众号设置」->「功能设置」
|
|
|
-5. 配置「网页授权域名」
|
|
|
-
|
|
|
-### 2. 服务器配置
|
|
|
-
|
|
|
-确保服务器能够访问微信API:
|
|
|
-- 开放对外网络访问
|
|
|
-- 配置正确的回调域名
|
|
|
-- 设置HTTPS(微信要求)
|
|
|
-
|
|
|
-### 3. 安全考虑
|
|
|
-
|
|
|
-1. **Token安全**: JWT token设置合理的过期时间
|
|
|
-2. **防CSRF**: 使用state参数防止CSRF攻击
|
|
|
-3. **错误处理**: 妥善处理微信API的错误响应
|
|
|
-4. **日志记录**: 记录微信认证过程中的关键操作
|
|
|
|
|
|
## 故障排除
|
|
|
|
|
|
@@ -616,28 +480,3 @@ describe('Wechat Auth (e2e)', () => {
|
|
|
- 微信API有调用频率限制
|
|
|
- 建议添加缓存机制
|
|
|
|
|
|
-## 性能优化
|
|
|
-
|
|
|
-1. **缓存access_token**: 使用Redis缓存微信access_token
|
|
|
-2. **数据库索引**: 为wechat_openid字段添加唯一索引
|
|
|
-3. **异步处理**: 用户信息获取可异步处理
|
|
|
-4. **CDN加速**: 微信头像使用CDN加速
|
|
|
-
|
|
|
-## 监控告警
|
|
|
-
|
|
|
-1. **微信API调用监控**: 监控微信API调用成功率
|
|
|
-2. **登录成功率监控**: 监控微信登录成功率
|
|
|
-3. **错误告警**: 设置微信认证错误告警
|
|
|
-4. **性能监控**: 监控微信认证接口响应时间
|
|
|
-
|
|
|
-## 版本历史
|
|
|
-
|
|
|
-- v1.0.0 (2024-01-01): 初始版本,支持微信网页授权登录
|
|
|
-- v1.1.0 (2024-02-01): 添加UnionID支持,优化错误处理
|
|
|
-- v1.2.0 (2024-03-01): 添加移动端适配,性能优化
|
|
|
-
|
|
|
-## 相关文档
|
|
|
-
|
|
|
-- [微信开放平台文档](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html)
|
|
|
-- [网页授权获取用户信息](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html)
|
|
|
-- [微信JS-SDK说明文档](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html)
|