JWT 认证
登录时用 bcrypt 比密码——这是一次性的。登录后每次请求带 token,后端用 JWT_SECRET 验签名判断真伪。
和密码哈希的区别:bcrypt 需要查数据库比对;JWT 验签不需要,纯数学计算,快。
数据建模两层思维
第一层:实体建表——users、books。
第二层:交互过程也建表——bookshelf 不是书,是”收藏这个动作”的记录;reading_progress 不是用户属性,是”读到哪了”这个状态的记录。
upsert 原子操作
INSERT ... ON CONFLICT DO UPDATE 是一条不可打断的 SQL。
先 SELECT 再 UPDATE 的问题:两个请求同时查,都认为”没有记录”,同时 INSERT,第二个就报错。upsert 把判断和执行合成原子操作,数据库内部排队处理,冲突自动变更新。
统一响应格式
前后端约定 { success, data, error } 格式。前端不用处理 HTTP 状态码,所有异常在 request 函数内部统一转化。调用方只关心 success 是 true 还是 false。
好处:TypeScript 类型安全,不用每次都处理低级的异常分支。
输入白名单
用户传的 source 参数只允许三个值,其余全给默认值。不信任任何用户输入,只放行明确安全的。
原则:与其判断什么是危险的,不如只允许那些确认安全的。
token 存 localStorage
localStorage 是浏览器端的简单持久化键值存储,刷新页面不丢。和 PostgreSQL 的区别——localStorage 是客户端个人状态,数据库是服务端全体数据。
前后端完整数据流
1 | 用户操作 → api.ts 发 fetch → Express 路由 → 数据库读写 → JSON 返回 → React state → UI 刷新 |
带认证的请求:authRequest() 自动从 localStorage 取 token 加到 Authorization header,后端 requireAuth 中间件拦截验证。
记住
- 类型约束比记性好
- 原子操作比先查后改安全
- 白名单比黑名单可靠
- 约定统一格式比每次处理异常省代码