非常好的选择。用户权限管理是一个重要的功能,可以帮助我们更好地控制不同用户对博客系统的访问和操作权限。我们将实现管理员、作者和读者三种角色的权限管理。
第二十三部分:用户权限管理
- 首先,更新
src/db/schema.ts文件,在 users 表中添加 role 字段:
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const users = sqliteTable("users", {
id: integer("id").primaryKey(),
username: text("username").notNull().unique(),
email: text("email").notNull().unique(),
password: text("password").notNull(),
avatar: text("avatar"),
role: text("role").notNull().default("reader"),
});
// ... 其他表定义保持不变- 创建一个新的迁移文件
src/db/migrations/0007_add_user_role.ts:
export async function up(db) {
await db.run(sql`
ALTER TABLE users ADD COLUMN role TEXT NOT NULL DEFAULT 'reader';
`);
}
export async function down(db) {
await db.run(sql`
ALTER TABLE users DROP COLUMN role;
`);
}- 运行迁移:
bun run migrate- 创建一个新文件
src/middleware/auth.ts来处理权限验证:
import { Elysia } from "elysia";
export const authMiddleware = new Elysia()
.derive(({ session }) => {
return {
user: session.user,
isAdmin: session.user?.role === "admin",
isAuthor: session.user?.role === "author" || session.user?.role === "admin",
};
})
.onError(({ code, error, set }) => {
if (code === "UNAUTHORIZED") {
set.status = 401;
return "Unauthorized";
}
});
export const adminOnly = authMiddleware.derive(({ isAdmin, set }) => {
if (!isAdmin) {
set.status = 403;
throw new Error("UNAUTHORIZED");
}
});
export const authorOnly = authMiddleware.derive(({ isAuthor, set }) => {
if (!isAuthor) {
set.status = 403;
throw new Error("UNAUTHORIZED");
}
});- 更新
src/routes/users.ts文件,添加用户角色管理功能:
import { Elysia } from "elysia";
import { db } from "../db";
import { users } from "../db/schema";
import { eq } from "drizzle-orm";
import { files } from "@elysiajs/files";
import { adminOnly, authMiddleware } from "../middleware/auth";
export const userRoutes = new Elysia()
.use(authMiddleware)
.use(files())
// ... 保留之前的路由
.get("/users", adminOnly, async ({ render }) => {
const allUsers = await db.select().from(users).all();
return render("users/index", { users: allUsers });
})
.get("/users/:id/edit", adminOnly, async ({ params, render }) => {
const user = await db.select().from(users).where(eq(users.id, parseInt(params.id))).get();
return render("users/edit", { user });
})
.post("/users/:id", adminOnly, async ({ params, body, set }) => {
const { role } = body;
await db.update(users).set({ role }).where(eq(users.id, parseInt(params.id)));
set.redirect = "/users";
});- 创建
src/views/users/index.eta文件:
<% layout('../layouts/main.eta', { title: 'User Management' }) %>
<h1>User Management</h1>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% it.users.forEach(user => { %>
<tr>
<td><%= user.username %></td>
<td><%= user.email %></td>
<td><%= user.role %></td>
<td><a href="/users/<%= user.id %>/edit">Edit</a></td>
</tr>
<% }) %>
</tbody>
</table>- 创建
src/views/users/edit.eta文件:
<% layout('../layouts/main.eta', { title: 'Edit User' }) %>
<h1>Edit User: <%= it.user.username %></h1>
<form action="/users/<%= it.user.id %>" method="POST">
<div>
<label for="role">Role:</label>
<select name="role" id="role">
<option value="reader" <%= it.user.role === 'reader' ? 'selected' : '' %>>Reader</option>
<option value="author" <%= it.user.role === 'author' ? 'selected' : '' %>>Author</option>
<option value="admin" <%= it.user.role === 'admin' ? 'selected' : '' %>>Admin</option>
</select>
</div>
<button type="submit">Update Role</button>
</form>- 更新
src/routes/posts.ts文件,应用权限控制:
import { authorOnly, authMiddleware } from "../middleware/auth";
export const postRoutes = new Elysia()
.use(authMiddleware)
// ... 保留其他路由
.get("/posts/new", authorOnly, async ({ render }) => {
return render("posts/new");
})
.post("/posts", authorOnly, async ({ body, set, session }) => {
// ... 创建文章的逻辑
})
.get("/posts/:id/edit", authorOnly, async ({ params, render }) => {
// ... 编辑文章的逻辑
})
.post("/posts/:id", authorOnly, async ({ params, body, set }) => {
// ... 更新文章的逻辑
})
.delete("/posts/:id", authorOnly, async ({ params, set }) => {
// ... 删除文章的逻辑
});- 更新
src/views/layouts/main.eta文件,根据用户角色显示不同的导航选项:
<header>
<nav>
<a href="/">Home</a>
<% if (it.user) { %>
<% if (it.user.role === 'admin' || it.user.role === 'author') { %>
<a href="/posts/new">New Post</a>
<% } %>
<% if (it.user.role === 'admin') { %>
<a href="/users">User Management</a>
<% } %>
<a href="/profile">Profile</a>
<a href="/logout">Logout</a>
<% } else { %>
<a href="/login">Login</a>
<a href="/register">Register</a>
<% } %>
<a href="/archive">Archive</a>
<a href="/tags">Tag Cloud</a>
</nav>
</header>- 更新
src/routes/auth.ts文件,在注册时设置默认角色:
export const authRoutes = new Elysia()
// ... 保留其他路由
.post("/register", async ({ body, set, session }) => {
const { username, email, password } = body;
const hashedPassword = await Bun.password.hash(password);
const [user] = await db.insert(users).values({
username,
email,
password: hashedPassword,
role: "reader", // 设置默认角色为 reader
}).returning();
session.user = { id: user.id, username: user.username, role: user.role };
set.redirect = "/";
});这些更改实现了基本的用户权限管理系统:
- 我们在用户表中添加了 role 字段,可以是 “admin”、“author” 或 “reader”。
- 创建了中间件来检查用户权限。
- 实现了管理员专用的用户管理页面,可以查看和编辑用户角色。
- 限制了某些操作(如创建、编辑、删除文章)只能由作者或管理员执行。
- 根据用户角色调整了导航栏的显示内容。
主要改进包括:
- 更细致的访问控制,提高了系统安全性。
- 管理员可以轻松管理用户角色。
- 用户界面根据角色动态调整,提供更好的用户体验。
这个实现为博客系统提供了基本的角色基础访问控制(RBAC)。管理员可以全面管理系统,作者可以创建和管理内容,而读者则主要进行阅读和评论。
接下来,我们可以考虑以下方向来进一步完善系统:
- 实现更细粒度的权限控制,例如对特定文章或分类的权限。
- 添加用户审核机制,新注册的用户需要管理员批准。
- 实现文章的协作编辑功能。
- 添加用户活动日志,记录重要操作。
你希望继续哪个方向,或者有其他想法吗?