@linyjs/plugin-docs 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -33,6 +33,22 @@ cat node_modules/@linyjs/plugin-docs/docs/getting-started/quick-start.md
|
|
|
33
33
|
ls node_modules/@linyjs/plugin-docs/docs/api-reference/
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
### 快速创建插件项目
|
|
37
|
+
|
|
38
|
+
使用 CLI 工具创建新的插件项目:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# 创建新的插件项目
|
|
42
|
+
npx @linyjs/cli init my-plugin
|
|
43
|
+
cd my-plugin
|
|
44
|
+
npm install
|
|
45
|
+
|
|
46
|
+
# 进入开发模式
|
|
47
|
+
npx @linyjs/cli dev
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
CLI 工具会自动创建完整的项目结构,包含 package.json、TypeScript 配置和示例代码。
|
|
51
|
+
|
|
36
52
|
### 生成示例项目
|
|
37
53
|
|
|
38
54
|
使用 CLI 工具生成示例项目:
|
|
@@ -58,7 +74,8 @@ docs/
|
|
|
58
74
|
│ ├── client-module.md # 客户端模块
|
|
59
75
|
│ ├── ui-slots.md # UI扩展插槽
|
|
60
76
|
│ ├── permissions.md # 权限管理
|
|
61
|
-
│
|
|
77
|
+
│ ├── data-persistence.md # 数据持久化
|
|
78
|
+
│ └── authentication.md # 用户认证
|
|
62
79
|
├── api-reference/ # API参考
|
|
63
80
|
│ ├── server-interface.md
|
|
64
81
|
│ ├── client-interface.md
|
|
@@ -86,12 +103,40 @@ examples/
|
|
|
86
103
|
- 详细的 README 说明
|
|
87
104
|
- 可直接打包为 .mpk 文件
|
|
88
105
|
|
|
106
|
+
## 🔧 核心模块
|
|
107
|
+
|
|
108
|
+
linyjs 提供了以下核心模块,插件可以通过依赖注入方式调用这些基础服务:
|
|
109
|
+
|
|
110
|
+
### @linyjs/mongodb
|
|
111
|
+
**MongoDB 数据库服务**
|
|
112
|
+
- 提供数据持久化存储
|
|
113
|
+
- 支持 CRUD 操作和复杂查询
|
|
114
|
+
- 详情:[数据持久化指南](./docs/guides/data-persistence.md)
|
|
115
|
+
|
|
116
|
+
### @linyjs/auth
|
|
117
|
+
**用户认证系统**
|
|
118
|
+
- 支持用户注册、登录、Token 验证
|
|
119
|
+
- 提供 JWT 身份验证
|
|
120
|
+
- 详情:[认证指南](./docs/guides/authentication.md)
|
|
121
|
+
|
|
122
|
+
### @linyjs/permission
|
|
123
|
+
**权限管理系统**
|
|
124
|
+
- 基于角色的访问控制(RBAC)
|
|
125
|
+
- 细粒度的权限定义
|
|
126
|
+
- 详情:[权限管理指南](./docs/guides/permissions.md)
|
|
127
|
+
|
|
89
128
|
## 🔗 相关链接
|
|
90
129
|
|
|
91
130
|
- [linyjs 框架](https://github.com/linyjs/linyjs)
|
|
92
131
|
- [CLI 工具文档](https://github.com/linyjs/linyjs/tree/main/packages/cli)
|
|
93
132
|
- [报告文档问题](https://github.com/linyjs/linyjs/issues)
|
|
94
133
|
|
|
134
|
+
## 📖 核心模块指南
|
|
135
|
+
|
|
136
|
+
- 💾 [数据持久化](./docs/guides/data-persistence.md) - 使用 @linyjs/mongodb
|
|
137
|
+
- 🔐 [用户认证](./docs/guides/authentication.md) - 使用 @linyjs/auth
|
|
138
|
+
- 🛡️ [权限管理](./docs/guides/permissions.md) - 使用 @linyjs/permission
|
|
139
|
+
|
|
95
140
|
## 📄 许可证
|
|
96
141
|
|
|
97
142
|
MIT License
|
|
@@ -330,11 +330,31 @@ registry.register({
|
|
|
330
330
|
|
|
331
331
|
---
|
|
332
332
|
|
|
333
|
+
## 基础模块
|
|
334
|
+
|
|
335
|
+
linyjs 框架提供了以下基础模块,插件开发者可以通过依赖注入方式使用:
|
|
336
|
+
|
|
337
|
+
### 数据库模块
|
|
338
|
+
- **@linyjs/mongodb** - MongoDB 数据库服务
|
|
339
|
+
- 提供数据库连接、CRUD 操作、索引管理等功能
|
|
340
|
+
- 详见:[数据持久化指南](../guides/data-persistence.md)
|
|
341
|
+
|
|
342
|
+
### 认证模块
|
|
343
|
+
- **@linyjs/auth** - 用户认证系统
|
|
344
|
+
- 提供用户注册、登录、Token 验证等功能
|
|
345
|
+
- 详见:[认证指南](../guides/authentication.md)
|
|
346
|
+
|
|
347
|
+
### 权限模块
|
|
348
|
+
- **@linyjs/permission** - 权限管理系统
|
|
349
|
+
- 提供基于角色的访问控制(RBAC)
|
|
350
|
+
- 详见:[权限管理指南](../guides/permissions.md)
|
|
351
|
+
|
|
333
352
|
## 相关文档
|
|
334
353
|
|
|
335
354
|
- 📖 [服务端模块开发](../guides/server-module.md)
|
|
336
355
|
- 💻 [客户端模块开发](../guides/client-module.md)
|
|
337
356
|
- 🎨 [UI 扩展插槽](../guides/ui-slots.md)
|
|
357
|
+
- 💾 [基础模块使用指南](../README.md#-核心模块)
|
|
338
358
|
|
|
339
359
|
---
|
|
340
360
|
|
|
@@ -24,6 +24,25 @@
|
|
|
24
24
|
|
|
25
25
|
### 步骤 1: 初始化项目
|
|
26
26
|
|
|
27
|
+
**推荐方式:使用 CLI 工具自动创建项目**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# 使用 @linyjs/cli 创建项目目录和基本文件
|
|
31
|
+
npx @linyjs/cli init hello-plugin
|
|
32
|
+
cd hello-plugin
|
|
33
|
+
|
|
34
|
+
# 安装依赖
|
|
35
|
+
npm install
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
CLI 工具会自动创建:
|
|
39
|
+
- package.json (已配置依赖和脚本)
|
|
40
|
+
- tsconfig.json (TypeScript 配置)
|
|
41
|
+
- src/server/index.ts (服务端代码模板)
|
|
42
|
+
- src/client/index.tsx (客户端代码模板)
|
|
43
|
+
|
|
44
|
+
**传统方式:手动创建项目**
|
|
45
|
+
|
|
27
46
|
```bash
|
|
28
47
|
# 创建项目目录
|
|
29
48
|
mkdir hello-plugin && cd hello-plugin
|
|
@@ -36,20 +55,24 @@ npm install @linyjs/server-module-interface @linyjs/client-module-interface reac
|
|
|
36
55
|
npm install -D typescript @types/react
|
|
37
56
|
```
|
|
38
57
|
|
|
39
|
-
### 步骤 2:
|
|
58
|
+
### 步骤 2: 项目结构
|
|
59
|
+
|
|
60
|
+
使用 CLI 工具创建的项目已经包含了完整的文件结构:
|
|
40
61
|
|
|
41
62
|
```
|
|
42
63
|
hello-plugin/
|
|
43
|
-
├── package.json
|
|
44
|
-
├── tsconfig.json
|
|
64
|
+
├── package.json # 已预配置的依赖和脚本
|
|
65
|
+
├── tsconfig.json # 已配置 TypeScript
|
|
45
66
|
└── src/
|
|
46
67
|
├── server/
|
|
47
|
-
│ └── index.ts # 服务端入口
|
|
68
|
+
│ └── index.ts # 服务端入口 (已包含示例代码)
|
|
48
69
|
└── client/
|
|
49
|
-
└── index.tsx # 客户端入口
|
|
70
|
+
└── index.tsx # 客户端入口 (已包含示例代码)
|
|
50
71
|
```
|
|
51
72
|
|
|
52
|
-
|
|
73
|
+
**注意**:使用 CLI 创建的项目已经包含了预配置的 package.json 和 tsconfig.json,无需手动创建。
|
|
74
|
+
|
|
75
|
+
如果你使用了传统方式,需要**手动创建 tsconfig.json:**
|
|
53
76
|
|
|
54
77
|
```json
|
|
55
78
|
{
|
|
@@ -72,7 +95,9 @@ hello-plugin/
|
|
|
72
95
|
|
|
73
96
|
### 步骤 3: 编写服务端代码
|
|
74
97
|
|
|
75
|
-
|
|
98
|
+
**使用 CLI 创建的项目已经包含了示例代码**,你已经可以开始开发或修改现有代码。
|
|
99
|
+
|
|
100
|
+
如果你需要自定义或创建新的服务端代码,编辑 `src/server/index.ts`:
|
|
76
101
|
|
|
77
102
|
```typescript
|
|
78
103
|
import type { IModule } from '@linyjs/server-module-interface';
|
|
@@ -103,7 +128,9 @@ export const helloServerModule: IModule = {
|
|
|
103
128
|
|
|
104
129
|
### 步骤 4: 编写客户端代码
|
|
105
130
|
|
|
106
|
-
|
|
131
|
+
**使用 CLI 创建的项目已经包含了示例代码**,你已经可以开始开发或修改现有代码。
|
|
132
|
+
|
|
133
|
+
如果你需要自定义或创建新的客户端代码,编辑 `src/client/index.tsx`:
|
|
107
134
|
|
|
108
135
|
```typescript
|
|
109
136
|
import type { IModule } from '@linyjs/client-module-interface';
|
|
@@ -140,9 +167,25 @@ export const helloClientModule: IModule = {
|
|
|
140
167
|
|
|
141
168
|
### 步骤 5: 构建和打包
|
|
142
169
|
|
|
170
|
+
**推荐方式:使用开发模式 (dev)**
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# 一键打包、安装到开发环境
|
|
174
|
+
npx @linyjs/cli dev
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
这会:
|
|
178
|
+
1. 自动编译 TypeScript
|
|
179
|
+
2. 打包为 .mpk 文件
|
|
180
|
+
3. 安装到 SSR 的 extensions 目录
|
|
181
|
+
4. 注册到 packages.json
|
|
182
|
+
5. 准备就绪后重启 SSR 服务器即可测试
|
|
183
|
+
|
|
184
|
+
**传统方式:手动打包**
|
|
185
|
+
|
|
143
186
|
```bash
|
|
144
187
|
# 编译 TypeScript
|
|
145
|
-
|
|
188
|
+
npm run build
|
|
146
189
|
|
|
147
190
|
# 使用 CLI 打包为 .mpk 文件
|
|
148
191
|
npx @linyjs/cli pack
|
|
@@ -152,6 +195,18 @@ npx @linyjs/cli pack
|
|
|
152
195
|
|
|
153
196
|
### 步骤 6: 测试插件
|
|
154
197
|
|
|
198
|
+
**使用 CLI dev 模式**(已自动完成安装和注册):
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# 启动 SSR 服务器
|
|
202
|
+
cd /path/to/linyjs/packages/ssr
|
|
203
|
+
npx tsx src/demo/dev-server-with-pkg.tsx
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
访问 `http://localhost:3000/hello` 查看你的插件!
|
|
207
|
+
|
|
208
|
+
**手动部署**:
|
|
209
|
+
|
|
155
210
|
```bash
|
|
156
211
|
# 将插件复制到 extensions 目录
|
|
157
212
|
cp hello-plugin.mpk /path/to/linyjs/packages/ssr/extensions/
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
# 用户认证
|
|
2
|
+
|
|
3
|
+
本指南介绍如何在 linyjs 插件中使用认证系统实现用户登录、注册和权限验证。
|
|
4
|
+
|
|
5
|
+
## 概述
|
|
6
|
+
|
|
7
|
+
linyjs 提供完整的用户认证系统,支持:
|
|
8
|
+
|
|
9
|
+
- 🔐 用户注册和登录
|
|
10
|
+
- 🪙 JWT Token 认证
|
|
11
|
+
- 🛡️ 路由级别的认证保护
|
|
12
|
+
- 👤 用户信息管理
|
|
13
|
+
|
|
14
|
+
## 配置认证服务
|
|
15
|
+
|
|
16
|
+
### 在服务端模块中注入依赖
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import type { IModule } from '@linyjs/server-module-interface';
|
|
20
|
+
import { login, register, verifyToken } from '@linyjs/auth';
|
|
21
|
+
|
|
22
|
+
export const myServerModule: IModule = {
|
|
23
|
+
name: 'myServer',
|
|
24
|
+
moduleDependencies: ['auth'], // 依赖认证模块
|
|
25
|
+
serviceDefs: [
|
|
26
|
+
{
|
|
27
|
+
serviceTag: 'authController',
|
|
28
|
+
serviceImpl: AuthController,
|
|
29
|
+
dependencies: [], // 依赖注入将在类构造函数中使用
|
|
30
|
+
meta: { usageType: 'controller' }
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
};
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 基本认证操作
|
|
37
|
+
|
|
38
|
+
### 用户注册
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { register } from '@linyjs/auth';
|
|
42
|
+
|
|
43
|
+
class AuthController {
|
|
44
|
+
getRoutes() {
|
|
45
|
+
return [{
|
|
46
|
+
method: 'POST' as const,
|
|
47
|
+
path: '/api/auth/register',
|
|
48
|
+
middlewares: [],
|
|
49
|
+
responseType: 'json' as const,
|
|
50
|
+
handler: async (req: any) => {
|
|
51
|
+
const { username, email, password } = req.body;
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const result = await register(username, email, password);
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
message: 'Registration successful',
|
|
58
|
+
data: result
|
|
59
|
+
};
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
message: error instanceof Error ? error.message : 'Registration failed'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 用户登录
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { login } from '@linyjs/auth';
|
|
76
|
+
|
|
77
|
+
class AuthController {
|
|
78
|
+
getRoutes() {
|
|
79
|
+
return [{
|
|
80
|
+
method: 'POST' as const,
|
|
81
|
+
path: '/api/auth/login',
|
|
82
|
+
middlewares: [],
|
|
83
|
+
responseType: 'json' as const,
|
|
84
|
+
handler: async (req: any) => {
|
|
85
|
+
const { identifier, password } = req.body;
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const result = await login(identifier, password);
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
message: 'Login successful',
|
|
92
|
+
data: result
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
message: error instanceof Error ? error.message : 'Login failed'
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Token 验证
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { verifyToken, getUserById } from '@linyjs/auth';
|
|
110
|
+
|
|
111
|
+
class AuthController {
|
|
112
|
+
getRoutes() {
|
|
113
|
+
return [{
|
|
114
|
+
method: 'GET' as const,
|
|
115
|
+
path: '/api/auth/me',
|
|
116
|
+
middlewares: [],
|
|
117
|
+
responseType: 'json' as const,
|
|
118
|
+
handler: async (req: any) => {
|
|
119
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
120
|
+
|
|
121
|
+
if (!token) {
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
message: 'No token provided'
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const payload = await verifyToken(token);
|
|
130
|
+
const user = await getUserById(payload.userId);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
success: true,
|
|
134
|
+
data: user
|
|
135
|
+
};
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
message: 'Invalid token'
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## 认证中间件
|
|
149
|
+
|
|
150
|
+
### 创建认证中间件
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { verifyToken, getUserById } from '@linyjs/auth';
|
|
154
|
+
|
|
155
|
+
async function authMiddleware(req: any, res: any, next: any) {
|
|
156
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
157
|
+
|
|
158
|
+
if (!token) {
|
|
159
|
+
return res.status(401).json({
|
|
160
|
+
success: false,
|
|
161
|
+
message: 'Authentication token required'
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const payload = await verifyToken(token);
|
|
167
|
+
const user = await getUserById(payload.userId);
|
|
168
|
+
|
|
169
|
+
if (!user) {
|
|
170
|
+
return res.status(401).json({
|
|
171
|
+
success: false,
|
|
172
|
+
message: 'User not found'
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 将用户信息添加到请求对象
|
|
177
|
+
req.user = user;
|
|
178
|
+
req.userId = user._id;
|
|
179
|
+
|
|
180
|
+
next();
|
|
181
|
+
} catch (error) {
|
|
182
|
+
return res.status(401).json({
|
|
183
|
+
success: false,
|
|
184
|
+
message: 'Invalid authentication token'
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 使用认证中间件
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
class ProtectedController {
|
|
194
|
+
getRoutes() {
|
|
195
|
+
return [{
|
|
196
|
+
method: 'GET' as const,
|
|
197
|
+
path: '/api/protected',
|
|
198
|
+
middlewares: [authMiddleware], // 使用认证中间件
|
|
199
|
+
responseType: 'json' as const,
|
|
200
|
+
handler: async (req: any) => {
|
|
201
|
+
return {
|
|
202
|
+
success: true,
|
|
203
|
+
message: `Hello ${req.user.username}! You have access to this protected route.`
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## 客户端认证
|
|
212
|
+
|
|
213
|
+
### 登录表单示例
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import type { IModule } from '@linyjs/client-module-interface';
|
|
217
|
+
import React, { useState } from 'react';
|
|
218
|
+
|
|
219
|
+
function LoginForm() {
|
|
220
|
+
const [formData, setFormData] = useState({
|
|
221
|
+
identifier: '',
|
|
222
|
+
password: ''
|
|
223
|
+
});
|
|
224
|
+
const [error, setError] = useState('');
|
|
225
|
+
|
|
226
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
227
|
+
e.preventDefault();
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
const response = await fetch('/api/auth/login', {
|
|
231
|
+
method: 'POST',
|
|
232
|
+
headers: {
|
|
233
|
+
'Content-Type': 'application/json'
|
|
234
|
+
},
|
|
235
|
+
body: JSON.stringify(formData)
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const result = await response.json();
|
|
239
|
+
|
|
240
|
+
if (result.success) {
|
|
241
|
+
// 保存 token 到 localStorage 或 cookie
|
|
242
|
+
localStorage.setItem('token', result.data.token);
|
|
243
|
+
// 跳转或刷新页面
|
|
244
|
+
window.location.reload();
|
|
245
|
+
} else {
|
|
246
|
+
setError(result.message);
|
|
247
|
+
}
|
|
248
|
+
} catch (err) {
|
|
249
|
+
setError('Network error');
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<form onSubmit={handleSubmit}>
|
|
255
|
+
<div>
|
|
256
|
+
<label>用户名或邮箱:</label>
|
|
257
|
+
<input
|
|
258
|
+
type="text"
|
|
259
|
+
value={formData.identifier}
|
|
260
|
+
onChange={(e) => setFormData({ ...formData, identifier: e.target.value })}
|
|
261
|
+
required
|
|
262
|
+
/>
|
|
263
|
+
</div>
|
|
264
|
+
<div>
|
|
265
|
+
<label>密码:</label>
|
|
266
|
+
<input
|
|
267
|
+
type="password"
|
|
268
|
+
value={formData.password}
|
|
269
|
+
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
|
270
|
+
required
|
|
271
|
+
/>
|
|
272
|
+
</div>
|
|
273
|
+
{error && <div className="error">{error}</div>}
|
|
274
|
+
<button type="submit">登录</button>
|
|
275
|
+
</form>
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export const authClientModule: IModule = {
|
|
280
|
+
name: 'authClient',
|
|
281
|
+
serviceDefs: [{
|
|
282
|
+
serviceTag: 'loginForm',
|
|
283
|
+
serviceImpl: function() {
|
|
284
|
+
getComponents() {
|
|
285
|
+
return {
|
|
286
|
+
LoginForm: {
|
|
287
|
+
component: LoginForm,
|
|
288
|
+
meta: { usageType: 'normal_component' }
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
meta: { usageType: 'normal_service' }
|
|
294
|
+
}]
|
|
295
|
+
};
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## 认证状态管理
|
|
299
|
+
|
|
300
|
+
### 创建认证 Hook
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { useState, useEffect } from 'react';
|
|
304
|
+
|
|
305
|
+
interface User {
|
|
306
|
+
_id: string;
|
|
307
|
+
username: string;
|
|
308
|
+
email: string;
|
|
309
|
+
createdAt: Date;
|
|
310
|
+
roles?: string[];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function useAuth() {
|
|
314
|
+
const [user, setUser] = useState<User | null>(null);
|
|
315
|
+
const [loading, setLoading] = useState(true);
|
|
316
|
+
|
|
317
|
+
useEffect(() => {
|
|
318
|
+
const token = localStorage.getItem('token');
|
|
319
|
+
|
|
320
|
+
if (!token) {
|
|
321
|
+
setLoading(false);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
fetch('/api/auth/me', {
|
|
326
|
+
headers: {
|
|
327
|
+
'Authorization': `Bearer ${token}`
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
.then(response => response.json())
|
|
331
|
+
.then(result => {
|
|
332
|
+
if (result.success) {
|
|
333
|
+
setUser(result.data);
|
|
334
|
+
} else {
|
|
335
|
+
localStorage.removeItem('token');
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
.finally(() => {
|
|
339
|
+
setLoading(false);
|
|
340
|
+
});
|
|
341
|
+
}, []);
|
|
342
|
+
|
|
343
|
+
const login = async (identifier: string, password: string) => {
|
|
344
|
+
const response = await fetch('/api/auth/login', {
|
|
345
|
+
method: 'POST',
|
|
346
|
+
headers: {
|
|
347
|
+
'Content-Type': 'application/json'
|
|
348
|
+
},
|
|
349
|
+
body: JSON.stringify({ identifier, password })
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const result = await response.json();
|
|
353
|
+
|
|
354
|
+
if (result.success) {
|
|
355
|
+
localStorage.setItem('token', result.data.token);
|
|
356
|
+
setUser(result.data.user);
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return false;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const logout = () => {
|
|
364
|
+
localStorage.removeItem('token');
|
|
365
|
+
setUser(null);
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const isAuthenticated = !!user;
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
user,
|
|
372
|
+
loading,
|
|
373
|
+
isAuthenticated,
|
|
374
|
+
login,
|
|
375
|
+
logout
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
## 最佳实践
|
|
381
|
+
|
|
382
|
+
### 1. Token 存储
|
|
383
|
+
- 使用 `localStorage` 或 `cookie` 存储 token
|
|
384
|
+
- 考虑安全性时优先使用 `httpOnly` cookie
|
|
385
|
+
- 实现 token 自动刷新机制
|
|
386
|
+
|
|
387
|
+
### 2. 错误处理
|
|
388
|
+
- 捕获并处理认证相关的错误
|
|
389
|
+
- 提供用户友好的错误消息
|
|
390
|
+
- 记录详细的错误日志用于调试
|
|
391
|
+
|
|
392
|
+
### 3. 安全性
|
|
393
|
+
- 始终在服务端验证 token
|
|
394
|
+
- 不要在客户端存储敏感信息
|
|
395
|
+
- 使用强密码策略
|
|
396
|
+
- 考虑实现两步验证增强安全性
|
|
397
|
+
|
|
398
|
+
### 4. 用户体验
|
|
399
|
+
- 记住用户的登录状态
|
|
400
|
+
- 自动续期 token 避免频繁登录
|
|
401
|
+
- 在 token 过期时友好提示用户
|
|
402
|
+
|
|
403
|
+
## 故障排除
|
|
404
|
+
|
|
405
|
+
### 常见问题
|
|
406
|
+
|
|
407
|
+
1. **Token 无法验证**
|
|
408
|
+
- 检查 JWT_SECRET 是否正确配置
|
|
409
|
+
- 确认 token 未过期
|
|
410
|
+
- 验证 token 格式是否正确
|
|
411
|
+
|
|
412
|
+
2. **用户无法登录**
|
|
413
|
+
- 检查用户名/邮箱和密码是否正确
|
|
414
|
+
- 确认用户账户已激活
|
|
415
|
+
- 检查数据库连接是否正常
|
|
416
|
+
|
|
417
|
+
3. **中间件无法获取用户信息**
|
|
418
|
+
- 确认 token 在请求头中正确传递
|
|
419
|
+
- 检查 verifyToken 函数是否正常工作
|
|
420
|
+
- 验证 getUserById 是否能找到对应用户
|
|
@@ -306,6 +306,9 @@ npx @linyjs/cli validate *.mpk
|
|
|
306
306
|
- 🔧 [API 参考](../../docs/api-reference/server-interface.md)
|
|
307
307
|
- 📚 [服务端模块指南](../../docs/guides/server-module.md)
|
|
308
308
|
- 🎨 [客户端模块指南](../../docs/guides/client-module.md)
|
|
309
|
+
- 💾 [数据持久化](../../docs/guides/data-persistence.md) - 使用 MongoDB 模块
|
|
310
|
+
- 🔐 [用户认证](../../docs/guides/authentication.md) - 使用 Auth 模块
|
|
311
|
+
- 🛡️ [权限管理](../../docs/guides/permissions.md) - 使用 Permission 模块
|
|
309
312
|
|
|
310
313
|
## 许可证
|
|
311
314
|
|