欢迎光临
我们一直在努力

【claude+weelinking产品经理系列14】用户登录与权限——你最熟悉的“角色权限“落地了

「产品经理用 Claude 实现产品」系列 · 第14篇

你写过无数次"角色权限矩阵"——管理员全权限、普通用户管自己的、访客只读。这些设计以前你只能交给开发实现。这一篇,用 Claude 把你的权限矩阵变成真实代码:完整的登录注册、JWT 认证、三种角色、后端鉴权、前端按钮权限控制。


一、前言:角色权限,产品经理的必修课

作为产品经理,你肯定做过这样一张表:

功能管理员普通用户访客
查看需求列表
创建需求
编辑自己的需求
编辑他人需求
删除需求 仅自己创建的
管理用户

这就是角色权限矩阵。你写了多少次,你把它交给开发,他们实现了多少次。

今天,用 Claude 自己来实现它。

你会发现:你对这个功能的理解,比任何人都深。 因为你写了无数次,你知道每个格子里的逻辑,你只是从来没有自己实现过而已。

💡 本系列全程使用 weelinking 访问 Claude,国内可稳定使用


二、用户注册与登录

2.1 注册功能完善

第11篇我们实现了基础的注册接口,现在完善前端和体验细节:

你: 帮我完善注册页面(src/pages/Register.jsx):

表单字段:

  • 用户名:必填,4-20个字符,只能是字母/数字/下划线,实时校验
  • 密码:必填,最少8位,至少包含数字和字母,显示密码强度条
  • 确认密码:必填,和密码一致
  • 邮箱:选填,格式校验

注册成功后:

  • 显示成功提示:“注册成功!欢迎加入需求管理平台”
  • 3秒后自动跳转登录页(显示倒计时)

注册失败处理:

  • 用户名已存在:在用户名字段下显示"该用户名已被使用"
  • 其他错误:顶部错误提示

密码强度条——这是个你在很多产品里见过的体验细节,Antd 有现成组件,Claude 帮你集成。

2.2 登录功能完善

你: 完善登录页面,在现有基础上增加:

  • "记住我"功能:勾选后,下次打开自动登录(token 存入 localStorage,不勾选存 sessionStorage)
  • 登录失败区分提示:
    • 用户名不存在:“用户名不存在,去注册?”("去注册"是链接)
    • 密码错误:“密码错误,还剩 X 次尝试机会”
    • 连续失败5次:锁定账号1分钟,显示倒计时
  • 忘记密码链接(暂时跳转到一个"请联系管理员"的提示页)

“连续失败锁定”——这是安全措施,也是你在设计登录功能时一定会考虑到的。现在 Claude 帮你实现了:

// 后端:登录失败记录
const loginAttempts = {}; // 临时方案,生产环境用数据库/Redis

const login = (req, res) => {
const { username, password } = req.body;
const key = username;

// 检查是否被锁定
if (loginAttempts[key] && loginAttempts[key].lockUntil > Date.now()) {
const remaining = Math.ceil((loginAttempts[key].lockUntil Date.now()) / 1000);
return res.status(429).json({
success: false,
message: `账号已锁定,请 ${remaining} 秒后再试`
});
}

const user = db.prepare('SELECT * FROM users WHERE username = ?').get(username);

if (!user) {
return res.status(401).json({ success: false, message: '用户名不存在' });
}

const valid = bcrypt.compareSync(password, user.password);

if (!valid) {
// 记录失败次数
if (!loginAttempts[key]) loginAttempts[key] = { count: 0 };
loginAttempts[key].count++;

const remaining = 5 loginAttempts[key].count;

if (loginAttempts[key].count >= 5) {
loginAttempts[key].lockUntil = Date.now() + 60 * 1000; // 锁定1分钟
return res.status(429).json({ success: false, message: '密码错误次数过多,账号已锁定1分钟' });
}

return res.status(401).json({
success: false,
message: `密码错误,还剩 ${remaining} 次尝试机会`
});
}

// 登录成功,清除失败记录
delete loginAttempts[key];

const token = jwt.sign({ id: user.id, username: user.username, role: user.role }, JWT_SECRET, { expiresIn: '7d' });
res.json({ success: true, data: { token, user: { id: user.id, username: user.username, role: user.role } } });
};

2.3 退出登录

你: 实现退出登录功能:

  • 点击右上角用户头像,下拉菜单里有"退出登录"选项
  • 点击后弹出确认框(“确定要退出登录吗?”)
  • 确认后:清除 localStorage 里的 token 和 user 信息,跳转登录页

退出登录是纯前端操作——清除 token 就行。不需要调后端接口(当然加上更好,但对于我们的场景,前端处理就够了)。


三、角色与权限

3.1 定义角色体系

我们的产品有三种角色,把它们精确定义出来:

你: 帮我在后端定义角色权限体系,在 src/middleware/auth.js 里创建权限中间件。

三种角色:

  • admin(管理员):所有操作权限
  • user(普通用户):可以创建需求、编辑/删除自己创建的需求、查看所有需求
  • guest(访客):只能查看需求,不能创建/编辑/删除

权限中间件:

  • authenticate:验证 JWT token,解析用户信息到 req.user
  • requireRole(roles):要求用户角色在指定角色列表内
  • requireOwnerOrAdmin:要求是资源创建者或管理员
  • Claude 会生成:

    // src/middleware/auth.js
    const jwt = require('jsonwebtoken');
    const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';

    // 1. 验证 token
    const authenticate = (req, res, next) => {
    const token = req.headers.authorization?.split(' ')[1];

    if (!token) {
    return res.status(401).json({ success: false, message: '未登录,请先登录' });
    }

    try {
    const decoded = jwt.verify(token, JWT_SECRET);
    req.user = decoded; // { id, username, role }
    next();
    } catch (err) {
    return res.status(401).json({ success: false, message: 'Token 已过期,请重新登录' });
    }
    };

    // 2. 要求特定角色
    const requireRole = (roles) => (req, res, next) => {
    if (!roles.includes(req.user.role)) {
    return res.status(403).json({ success: false, message: '权限不足' });
    }
    next();
    };

    // 3. 要求是创建者或管理员
    const requireOwnerOrAdmin = (getResourceUserId) => async (req, res, next) => {
    if (req.user.role === 'admin') return next(); // 管理员直接放行

    const resourceUserId = await getResourceUserId(req);

    if (req.user.id !== resourceUserId) {
    return res.status(403).json({ success: false, message: '只能操作自己创建的内容' });
    }
    next();
    };

    module.exports = { authenticate, requireRole, requireOwnerOrAdmin };

    3.2 在接口上应用权限

    有了权限中间件,把它加到各个接口上:

    你: 帮我给 requirements 路由加上权限控制:

    • GET /api/requirements(获取列表):所有登录用户可以访问,包括 guest
    • POST /api/requirements(创建):需要是 user 或 admin,guest 不行
    • GET /api/requirements/:id(获取详情):所有登录用户
    • PUT /api/requirements/:id(更新):需要是资源创建者或 admin
    • DELETE /api/requirements/:id(删除):需要是资源创建者或 admin

    Claude 会修改路由文件:

    // src/routes/requirements.js
    const express = require('express');
    const router = express.Router();
    const { authenticate, requireRole, requireOwnerOrAdmin } = require('../middleware/auth');
    const controller = require('../controllers/requirementController');

    // 所有路由需要登录
    router.use(authenticate);

    // 查看:所有角色
    router.get('/', controller.getRequirements);
    router.get('/:id', controller.getRequirementById);

    // 创建:user 和 admin
    router.post('/', requireRole(['user', 'admin']), controller.createRequirement);

    // 更新/删除:创建者或 admin
    const getRequirementOwner = (req) => {
    const req_db = require('../models/db');
    const item = req_db.prepare('SELECT createdBy FROM requirements WHERE id = ?').get(req.params.id);
    return item?.createdBy;
    };

    router.put('/:id', requireOwnerOrAdmin(getRequirementOwner), controller.updateRequirement);
    router.delete('/:id', requireOwnerOrAdmin(getRequirementOwner), controller.deleteRequirement);

    module.exports = router;

    这就是你的权限矩阵的代码实现。 看着这个路由文件,再对照你的权限矩阵——完全一一对应。

    3.3 管理员管理用户

    你: 帮我实现用户管理接口,只有 admin 能访问:

    • GET /api/users(获取用户列表)
    • PUT /api/users/:id/role(修改用户角色):只能改成 user 或 guest,不能改成 admin(避免意外)
    • DELETE /api/users/:id(删除用户):不能删除自己,不能删除其他 admin

    四、前端权限控制

    后端已经拦截了非法操作,但用户体验上,没权限的按钮最好直接不显示,而不是点击后报错。

    4.1 获取并存储当前用户信息

    登录成功后,把用户信息(包括角色)存起来,全局可用:

    你: 帮我创建用户信息的全局状态管理(用 React Context),在 src/contexts/AuthContext.jsx:

    • 存储:user(用户信息,含 role)、token
    • 登录后设置,退出后清空
    • 提供 useAuth() 钩子,在任何组件里都能获取当前用户信息和角色
    • 页面刷新后从 localStorage 恢复状态

    Claude 会生成 AuthContext,所有组件都能用 useAuth() 获取当前用户角色。

    4.2 权限控制组件

    你: 帮我创建一个权限控制组件 src/components/PermissionGuard.jsx:

    • 用法:<PermissionGuard roles={['admin', 'user']}><CreateButton /></PermissionGuard>
    • 当前用户角色在 roles 数组内,显示子元素
    • 不在的话,不渲染(或显示 fallback 内容)

    另外,用法:<PermissionGuard ownerOnly requirementId={id}><EditButton /></PermissionGuard>

    • ownerOnly 模式:只有需求创建者或 admin 才显示

    // src/components/PermissionGuard.jsx
    import { useAuth } from '../contexts/AuthContext';

    const PermissionGuard = ({ roles, ownerOnly, createdBy, children, fallback = null }) => {
    const { user } = useAuth();

    if (!user) return fallback;

    // 角色权限检查
    if (roles && !roles.includes(user.role)) return fallback;

    // 创建者权限检查
    if (ownerOnly && user.role !== 'admin' && user.id !== createdBy) return fallback;

    return children;
    };

    export default PermissionGuard;

    4.3 在页面里应用权限控制

    把 PermissionGuard 加到需要控制的地方:

    你: 在需求列表页,用 PermissionGuard 控制:

    • "创建需求"按钮:只有 user 和 admin 显示
    • "编辑"操作:只有创建者和 admin 显示
    • "删除"操作:只有创建者和 admin 显示

    在需求详情页:

    • "编辑"按钮:创建者或 admin
    • "删除"按钮:创建者或 admin
    • 状态修改:user 和 admin

    在侧边菜单:

    • "用户管理"菜单项:只有 admin 显示

    改造后,以访客身份登录,你会发现"创建需求"按钮不见了,没有任何编辑删除操作——这就是你设计的权限矩阵,真实地运行在产品里。

    4.4 路由权限保护

    你: 帮我给前端路由加上权限保护:

    • 未登录用户访问任何页面,自动重定向到 /login
    • 已登录用户访问 /login,自动重定向到 /home
    • 访客访问 /requirements/new(创建需求页),重定向到 /requirements 并提示"无权创建需求"

    Claude 会创建 PrivateRoute 组件:

    // src/components/PrivateRoute.jsx
    import { Navigate } from 'react-router-dom';
    import { useAuth } from '../contexts/AuthContext';

    const PrivateRoute = ({ children, roles }) => {
    const { user, token } = useAuth();

    // 未登录,跳登录页
    if (!token) return <Navigate to="/login" replace />;

    // 角色不够,跳列表页
    if (roles && !roles.includes(user.role)) {
    return <Navigate to="/requirements" replace />;
    }

    return children;
    };

    路由配置:

    <Route path="/requirements/new" element={
    <PrivateRoute roles={['user', 'admin']}>
    <RequirementForm />
    </PrivateRoute>
    } />


    五、安全基础

    权限控制不只是"显不显示按钮",还有真正的安全防线。Claude 帮你处理的安全细节:

    5.1 密码存储安全

    你: 确认密码存储方案是否安全。

    Claude 会解释:我们用的 bcryptjs 是业界标准的密码哈希库,密码不是加密,而是单向哈希——存的是哈希值,无法反向得到原始密码。这是正确的做法。

    // 注册时:哈希密码后存储
    const hashedPassword = bcrypt.hashSync(password, 10); // 10 是计算强度

    // 登录时:验证密码
    const valid = bcrypt.compareSync(inputPassword, hashedPassword);

    无论是你的数据库被盗,还是开发人员查数据库,都不会看到用户的真实密码。 这是基础安全红线。

    5.2 JWT Token 安全

    你: 帮我检查 JWT 实现是否存在安全问题,并给出改进建议。

    Claude 会指出几个需要注意的地方:

    问题1:JWT Secret 写死在代码里

    // ❌ 不安全
    const JWT_SECRET = 'your-secret-key';

    // ✅ 改用环境变量
    const JWT_SECRET = process.env.JWT_SECRET;

    问题2:Token 过期时间太长

    7天的 Token 意味着:用户密码被改了,旧 Token 还能用7天。可以缩短为1天,或实现 Token 刷新机制。我们暂时保持7天,后续优化。

    问题3:没有 Token 黑名单

    用户退出登录后,Token 技术上还有效(除非过期)。进阶方案是维护一个 Token 黑名单(Redis 存储),但对我们的场景,前端删除 Token 后用户就无法再使用,影响有限。

    你: 帮我创建 .env 文件模板,和 .env.example,把 JWT_SECRET 改成从环境变量读取。

    # .env(不提交 git)
    JWT_SECRET=your-very-long-random-secret-key
    PORT=3001

    # .gitignore
    .env

    // 读取环境变量
    require('dotenv').config();
    const JWT_SECRET = process.env.JWT_SECRET;

    if (!JWT_SECRET) {
    throw new Error('JWT_SECRET 环境变量未设置!');
    }

    5.3 常见安全问题防范

    让 Claude 帮你做一次安全检查:

    你: 帮我检查当前后端代码,是否存在以下安全问题,并给出修复建议:

  • SQL 注入(直接拼接 SQL 字符串)
  • XSS(存储了用户输入,但没有做转义)
  • 接口没有限流(暴力破解)
  • 敏感信息暴露(错误信息里包含技术细节)
  • Claude 会逐一检查并给出建议。常见问题:

    安全问题我们的情况解决方案
    SQL 注入 用 prepared statements,安全 ✅ 已处理
    XSS 存储了 description 字段 前端渲染时用 DOMPurify 过滤
    接口限流 未做 加 express-rate-limit
    敏感信息 错误可能暴露 SQL 生产环境只返回通用错误消息

    简单加一个限流:

    你: 帮我给登录接口加上 IP 限流:同一个 IP,每15分钟最多尝试20次。使用 express-rate-limit 库。

    const rateLimit = require('express-rate-limit');

    const loginLimiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15分钟
    max: 20, // 最多20次
    message: { success: false, message: '请求过于频繁,请15分钟后再试' }
    });

    router.post('/login', loginLimiter, authController.login);


    六、验收权限功能

    权限系统做完后,需要专项测试。这类功能最容易有漏洞——后端拦截了,但前端没拦;或者前端不显示,但后端没验证,直接调 API 还是能操作。

    6.1 权限测试矩阵

    注册三个测试账号,分别设置三种角色(在数据库里手动改 role 字段):

    账号角色密码
    admin_test admin test123456
    user_test user test123456
    guest_test guest test123456

    逐一验证每种角色的操作权限:

    以 guest_test 登录:
    □ 能看到需求列表(✅ 应该能)
    □ 看不到"创建需求"按钮(✅ 应该看不到)
    □ 看不到任何"编辑"、"删除"按钮
    □ 直接访问 /requirements/new,被重定向
    □ 用 Apifox 直接调用 POST /api/requirements,带 guest token,返回 403

    以 user_test 登录:
    □ 能创建需求
    □ 能编辑自己创建的需求
    □ 看不到别人需求的"编辑"按钮
    □ 用 Apifox 直接调用 PUT /api/requirements/他人需求ID,返回 403

    以 admin_test 登录:
    □ 能看到所有操作按钮
    □ 能编辑任何人的需求
    □ 能删除任何需求
    □ 能看到"用户管理"菜单
    □ 能修改用户角色

    关键测试:后端也要拦截

    即使前端不显示按钮,用 Apifox 直接发请求,后端也必须拒绝非法操作。这才是真正的安全——不能依赖"用户看不到按钮就不会点"。


    七、完整登录流程体验

    完成本篇后,完整的登录体验应该是这样的:

  • 首次访问:自动跳到登录页
  • 注册:填写用户名、密码、邮箱,密码强度条实时显示
  • 注册成功:跳转登录页,提示"注册成功,请登录"
  • 登录:输入用户名密码,勾选"记住我"
  • 登录成功:跳转到需求列表页,右上角显示用户头像和名称
  • 使用系统:根据角色显示不同功能
  • 退出:点右上角头像 → 退出登录 → 清除 token → 跳转登录页
  • 下次访问:(因为选了记住我)直接显示列表页,无需再次登录
  • 这就是一个完整的、安全的认证流程。


    八、阶段成果

    完成本篇后:

    • ✅ 完整注册流程(密码强度、校验、成功跳转)
    • ✅ 完整登录流程(记住我、失败限制、锁定保护)
    • ✅ JWT 认证(环境变量管理 Secret)
    • ✅ 三种角色定义(admin/user/guest)
    • ✅ 后端接口鉴权(未登录401、无权限403)
    • ✅ 前端权限控制(PermissionGuard 组件、路由保护)
    • ✅ 基础安全处理(密码哈希、限流、SQL 注入防范)

    你的"需求管理平台"现在是一个有完整权限体系的系统了。 不同角色,看到不同的界面,有不同的操作权限——这就是你在 PRD 里设计的,现在实现了。

    git add .
    git commit -m "完成用户认证和角色权限系统"
    git push


    九、总结与下期预告

    🎯 本篇核心要点

    1. 角色权限矩阵 = 中间件 + 路由配置。 你在 PRD 里画的那张表,在代码里就是这两个东西。一旦理解了这个对应关系,"实现权限"就不神秘了。

    2. 前端不显示 ≠ 后端安全。 真正的权限控制在后端。前端不显示按钮只是用户体验,后端才是真正的防线。两者都要做。

    3. 密码安全是底线。 任何真实产品,密码必须哈希存储,绝不能明文。bcrypt 是标准方案,直接用。

    4. 安全问题用 Claude 做检查。 你不需要精通安全,但要知道有哪些常见漏洞,用 Claude 帮你检查和修复。这个习惯很重要。

    5. 权限系统要做专项测试。 每种角色、每个操作,都要测一遍。权限漏洞是最严重的产品问题之一。

    📌 记住这句话

    你写过无数张角色权限矩阵,今天第一次自己把它变成了代码。下次跟开发说"这个权限控制很复杂",你心里已经知道它到底复杂在哪里。这就是"技术型 PM"的价值。

    📣 下期预告

    第15篇:《UI/UX 打磨——产品经理的审美终于能自己实现》

    核心功能全部完成!下一篇,我们来提升产品的"颜值"和体验——这是产品经理最擅长的领域。

    你会做到:

    • 整体配色方案优化
    • 加载状态(骨架屏、Loading)
    • 操作反馈(Toast 提示、按钮状态)
    • 空状态设计
    • 页面切换动画
    • 响应式适配

    产品经理的审美,终于能自己实现了,不用再跟设计师解释"我要的是这个感觉"。

    💡 本系列全程使用 weelinking 访问 Claude,国内可稳定使用


    📎 配套资源

    📋 角色权限矩阵模板

    【需求管理平台角色权限矩阵】

    功能模块 | admin | user | guest
    ————|——-|——|——
    需求 – 查看列表 | ✅ | ✅ | ✅
    需求 – 查看详情 | ✅ | ✅ | ✅
    需求 – 创建 | ✅ | ✅ | ❌
    需求 – 编辑自己的 | ✅ | ✅ | ❌
    需求 – 编辑他人的 | ✅ | ❌ | ❌
    需求 – 删除自己的 | ✅ | ✅ | ❌
    需求 – 删除他人的 | ✅ | ❌ | ❌
    需求 – 归档管理 | ✅ | ❌ | ❌
    用户 – 查看列表 | ✅ | ❌ | ❌
    用户 – 修改角色 | ✅ | ❌ | ❌
    用户 – 删除用户 | ✅ | ❌ | ❌

    📋 认证与权限检查清单

    □ 注册功能
    □ 用户名唯一性验证
    □ 密码强度校验(长度、包含数字字母)
    □ 密码哈希存储(bcrypt)
    □ 注册成功跳转

    □ 登录功能
    □ JWT token 生成
    □ token 存储(localStorage/sessionStorage)
    □ 登录失败计数和锁定
    □ 记住我功能

    □ 认证中间件
    □ token 验证(authenticate)
    □ 角色验证(requireRole)
    □ 创建者验证(requireOwnerOrAdmin)

    □ 后端接口鉴权
    □ 需要登录的接口都加了 authenticate
    □ 需要特定角色的接口加了 requireRole
    □ 需要创建者权限的接口加了 requireOwnerOrAdmin

    □ 前端权限控制
    □ 创建 AuthContext,全局管理用户状态
    □ 创建 PermissionGuard 组件
    □ 按钮/操作根据角色显示/隐藏
    □ 路由保护(未登录重定向)
    □ 页面刷新后恢复登录状态

    □ 安全处理
    □ JWT_SECRET 从环境变量读取
    □ 创建 .env 文件,加入 .gitignore
    □ 登录接口加限流
    □ 密码不在响应中返回

    □ 权限测试
    □ 三种角色分别测试
    □ 后端接口直接调用测试(验证后端真的拦截)

    📋 常用提示词模板

    实现权限控制:

    帮我给 [接口/功能] 加上权限控制:

    角色权限要求:
    – admin:[能做什么]
    – user:[能做什么]
    – guest:[能做什么]

    特殊规则:
    – [规则1:比如创建者才能编辑自己的内容]

    后端:修改路由配置,加上对应中间件
    前端:用 PermissionGuard 组件控制 [哪些按钮/操作]

    安全检查:

    帮我检查这段代码的安全问题:
    [粘贴代码]

    重点关注:
    1. 是否有 SQL 注入风险
    2. 用户输入是否有过滤/验证
    3. 是否有敏感信息泄露
    4. 权限是否真的在后端校验

    请给出具体的安全建议和修复方案。


    🌟 如果这篇文章对你有帮助,请点赞👍 收藏⭐ 关注🔔

    你的支持是我持续更新的最大动力!

    💬 评论区聊聊:你之前设计的权限矩阵里,有没有遇到过开发说"这个实现不了"?看完这篇,你觉得那个"实现不了"是真的吗?


    📌 系列导航: 产品经理用 Claude 实现产品 · 系列目录

    ⏪ 上一篇: 第13篇:核心功能实现——需求的增删改查全流程

    ⏩ 下一篇: 第15篇:UI/UX 打磨——产品经理的审美终于能自己实现

    💡 本系列全程使用 weelinking 访问 Claude,国内可稳定使用 🚀 整个系列的核心理念:你不需要变成程序员,你只需要从"找人做"变成"自己能做"。

    赞(0)
    未经允许不得转载:171主机测评 » 【claude+weelinking产品经理系列14】用户登录与权限——你最熟悉的“角色权限“落地了
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址