news 2026/9/3 0:38:44

【技术项目】用户注册、登录与个人主页完整 MVP交付

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【技术项目】用户注册、登录与个人主页完整 MVP交付

项目:用户注册、登录与个人主页(完整 MVP 交付)

本文档提供了一个完整的、可直接落地的用户认证体系 MVP 套件,包括:

  • 产品需求文档(PRD)
  • 两个架构决策记录(ADR)
  • OpenAPI 3.0 规范(YAML)
  • 可运行的 FastAPI 实现(完整代码 + 运行说明)

可直接将文档放入仓库docs/目录,将代码放入src/或根目录。


一、产品需求文档(PRD)

1. 概述

功能名称:用户注册、登录与个人主页
目标:构建一个简洁、安全的用户账户体系,支持邮箱+密码注册、登录,以及登录后查看/编辑个人主页(昵称、简介、头像 URL)。作为后续社交、内容等功能的用户基础模块。
目标用户:所有新用户及需要账户体系的业务线。

2. 背景与问题

当前系统缺少账户体系,用户无法保存个性化信息、偏好设置或发布内容。需要一个最小可行产品(MVP)快速验证用户对注册及完善个人信息的意愿。

3. 目标与成功指标

核心目标:4 周内完成注册、登录、个人主页编辑功能并上线。

成功指标(上线后可量化跟踪):

  • 新用户注册转化率 ≥ 5%(访问注册页 → 成功注册的比例)
  • 7 天用户留存率 ≥ 15%
  • 注册后 7 天内完成个人主页信息填写率 ≥ 40%(至少填写昵称、简介或头像之一)

4. 用户故事(User Stories)

  • 作为访客,我可以通过邮箱 + 密码注册账号,以获得系统使用权限。
  • 作为已注册用户,我可以使用邮箱 + 密码登录,并获取访问令牌(JWT)。
  • 作为已登录用户,我可以查看并编辑自己的个人主页信息(昵称、简介、头像 URL)。

5. 功能清单(按优先级)

MVP 必选功能

  1. 注册接口POST /signup(邮箱 + 密码,密码哈希存储,返回 201)
  2. 登录接口POST /login(返回 JWT access_token)
  3. 获取个人信息GET /me(需认证)
  4. 更新个人信息PUT /me(需认证,支持更新昵称、简介、头像 URL)

可选后续迭代

  • 邮箱验证
  • 密码找回
  • 第三方社交登录(OAuth)
  • 头像文件上传与存储
  • 多因素认证(MFA)

6. 非功能性需求

  • 安全性:密码使用 bcrypt 哈希存储;JWT 有效期 7 天;接口限流防暴力破解。
  • 可用性:MVP 阶段单实例部署即可,长期目标 API 可用率 ≥ 99%。
  • 隐私保护:响应中绝不返回密码或哈希值;日志对敏感字段脱敏。

7. 验收标准(Acceptance Criteria)

  • 注册:合法参数返回 201;重复邮箱返回 400。
  • 登录:正确凭证返回 200 + access_token;错误凭证返回 401。
  • 认证保护:未携带有效 token 访问/me返回 401。
  • 更新个人信息:仅更新提供的字段,返回最新用户信息。

8. MVP 范围与时间规划

范围:仅实现上述 4 个核心 API(后端),可选最简前端演示页面。
时间箱:4 周(含设计、开发、测试、验证上线)。

9. 风险与缓解措施

风险:简单实现可能存在安全薄弱点(如 JWT 密钥管理不当)。
缓解

  • 使用成熟库(PyJWT + passlib[bcrypt])
  • 设置合理 token 有效期
  • 短周期迭代 + 安全审核

二、架构决策记录(ADR)

建议存放路径:docs/adr/

ADR 0001 - 使用 JWT(HS256)作为认证令牌

日期:2025-12-17
状态:Accepted

背景:需要快速实现前后端分离的认证机制,用户量中等,短期内无需复杂会话一致性。

决策:采用 JWT(HS256 对称签名),token 中包含sub(用户 ID)和exp(过期时间)。

考虑的备选方案

  1. 完整 OAuth2:对第三方友好,但实现成本高,超出 MVP。
  2. 后端 Session:实现简单,但不利于横向扩展和移动端。

后果

  • 优点:实现快速,客户端易存储与传递,满足 MVP 需求。
  • 缺点:无法服务器端即时撤销(可通过短有效期或后续引入黑名单缓解),需妥善管理密钥。

ADR 0002 - MVP 阶段使用 SQLite 存储用户信息

日期:2025-12-17
状态:Accepted

背景:MVP 需要快速、可单机运行的 PoC,降低开发与部署成本。

决策:使用 SQLite 文件数据库,后续按需迁移至 MySQL/PostgreSQL。

考虑的备选方案

  1. PostgreSQL/MySQL:生产更稳健,但需额外运维配置。
  2. MongoDB 等 NoSQL:结构灵活,但事务一致性不如关系型数据库。

后果

  • 优点:零运维、易迁移,适合早期测试与演示。
  • 缺点:并发与扩展能力有限,生产环境必须迁移。

三、OpenAPI 规范(openapi.yaml)

建议存放路径:docs/spec/openapi.yaml

openapi:3.0.1info:title:用户系统 APIversion:"1.0.0"description:用户注册、登录与个人主页 APIservers:-url:http://localhost:8000paths:/signup:post:summary:用户注册requestBody:required:truecontent:application/json:schema:$ref:'#/components/schemas/SignupReq'responses:'201':description:注册成功content:application/json:schema:$ref:'#/components/schemas/MessageResp''400':description:参数错误或用户已存在/login:post:summary:用户登录requestBody:required:truecontent:application/json:schema:$ref:'#/components/schemas/LoginReq'responses:'200':description:登录成功,返回 access_tokencontent:application/json:schema:$ref:'#/components/schemas/LoginResp''401':description:凭证错误/me:get:summary:获取当前用户信息security:-bearerAuth:[]responses:'200':description:用户信息content:application/json:schema:$ref:'#/components/schemas/User''401':description:未认证或 token 无效put:summary:更新当前用户信息security:-bearerAuth:[]requestBody:required:truecontent:application/json:schema:$ref:'#/components/schemas/UserUpdateReq'responses:'200':description:更新成功,返回最新用户content:application/json:schema:$ref:'#/components/schemas/User'components:securitySchemes:bearerAuth:type:httpscheme:bearerbearerFormat:JWTschemas:SignupReq:type:objectrequired:-email-passwordproperties:email:type:stringformat:emailpassword:type:stringminLength:8LoginReq:type:objectrequired:-email-passwordproperties:email:type:stringformat:emailpassword:type:stringLoginResp:type:objectproperties:access_token:type:stringtoken_type:type:stringdefault:BearerMessageResp:type:objectproperties:message:type:stringUser:type:objectproperties:id:type:integeremail:type:stringformat:emailnickname:type:stringbio:type:stringavatar_url:type:stringUserUpdateReq:type:objectproperties:nickname:type:stringbio:type:stringavatar_url:type:string

四、FastAPI MVP 实现

目录结构

mvp-user-auth/ ├─ README.md ├─ requirements.txt ├─ app.py ├─ db.py ├─ utils.py └─ schemas.py

requirements.txt

fastapi uvicorn[standard] sqlalchemy passlib[bcrypt] pyjwt pydantic

db.py

fromsqlalchemyimportcreate_engine,MetaData,Table,Column,Integer,String,Textfromsqlalchemy.sqlimportselect DATABASE_URL="sqlite:///./users.db"engine=create_engine(DATABASE_URL,connect_args={"check_same_thread":False})metadata=MetaData()users=Table('users',metadata,Column('id',Integer,primary_key=True,autoincrement=True),Column('email',String(255),unique=True,nullable=False),Column('password_hash',String(255),nullable=False),Column('nickname',String(100),nullable=True),Column('bio',Text,nullable=True),Column('avatar_url',String(512),nullable=True),)metadata.create_all(engine)defget_user_by_email(conn,email):q=select(users).where(users.c.email==email)returnconn.execute(q).fetchone()defcreate_user(conn,email,password_hash):ins=users.insert().values(email=email,password_hash=password_hash)res=conn.execute(ins)returnres.inserted_primary_key[0]defget_user_by_id(conn,user_id):q=select(users).where(users.c.id==user_id)returnconn.execute(q).fetchone()defupdate_user(conn,user_id,**fields):upd=users.update().where(users.c.id==user_id).values(**fields)conn.execute(upd)returnget_user_by_id(conn,user_id)

utils.py

frompasslib.contextimportCryptContextimportjwtimporttime PWD_CTX=CryptContext(schemes=["bcrypt"],deprecated="auto")SECRET="change_this_to_a_long_random_secret"# 生产环境务必更换为安全密钥ALGORITHM="HS256"ACCESS_TOKEN_EXPIRE_SECONDS=7*24*3600# 7 天defhash_password(password:str)->str:returnPWD_CTX.hash(password)defverify_password(plain:str,hashed:str)->bool:returnPWD_CTX.verify(plain,hashed)defcreate_access_token(subject:str):now=int(time.time())payload={"sub":subject,"iat":now,"exp":now+ACCESS_TOKEN_EXPIRE_SECONDS}returnjwt.encode(payload,SECRET,algorithm=ALGORITHM)defdecode_access_token(token:str):try:returnjwt.decode(token,SECRET,algorithms=[ALGORITHM])exceptjwt.ExpiredSignatureError:raiseexceptException:raise

schemas.py

frompydanticimportBaseModel,EmailStr,FieldfromtypingimportOptionalclassSignupReq(BaseModel):email:EmailStr password:str=Field(...,min_length=8)classLoginReq(BaseModel):email:EmailStr password:strclassLoginResp(BaseModel):access_token:strtoken_type:str="Bearer"classMessageResp(BaseModel):message:strclassUser(BaseModel):id:intemail:EmailStr nickname:Optional[str]=Nonebio:Optional[str]=Noneavatar_url:Optional[str]=NoneclassUserUpdateReq(BaseModel):nickname:Optional[str]=Nonebio:Optional[str]=Noneavatar_url:Optional[str]=None

app.py

fromfastapiimportFastAPI,HTTPException,Dependsfromfastapi.securityimportHTTPBearer,HTTPAuthorizationCredentialsfromsqlalchemyimportcreate_engineimportdbas_dbimportschemasas_schemasimportutilsas_utils app=FastAPI(title="User Auth MVP")engine=create_engine(_db.DATABASE_URL,connect_args={"check_same_thread":False})bearer_scheme=HTTPBearer()@app.post('/signup',status_code=201,response_model=_schemas.MessageResp)defsignup(req:_schemas.SignupReq):withengine.connect()asconn:if_db.get_user_by_email(conn,req.email):raiseHTTPException(status_code=400,detail="user exists")pw_hash=_utils.hash_password(req.password)_db.create_user(conn,req.email,pw_hash)return{"message":"created"}@app.post('/login',response_model=_schemas.LoginResp)deflogin(req:_schemas.LoginReq):withengine.connect()asconn:user=_db.get_user_by_email(conn,req.email)ifnotuserornot_utils.verify_password(req.password,user['password_hash']):raiseHTTPException(status_code=401,detail="invalid credentials")token=_utils.create_access_token(str(user['id']))return{"access_token":token}defget_current_user(creds:HTTPAuthorizationCredentials=Depends(bearer_scheme)):token=creds.credentialstry:payload=_utils.decode_access_token(token)except:raiseHTTPException(status_code=401,detail="invalid or expired token")user_id=int(payload["sub"])withengine.connect()asconn:user=_db.get_user_by_id(conn,user_id)ifnotuser:raiseHTTPException(status_code=401,detail="user not found")returnuser@app.get('/me',response_model=_schemas.User)defme(user=Depends(get_current_user)):return{k:user[k]forkinuser.keys()ifkin('id','email','nickname','bio','avatar_url')}@app.put('/me',response_model=_schemas.User)defupdate_me(req:_schemas.UserUpdateReq,user=Depends(get_current_user)):fields={k:vfork,vinreq.dict().items()ifvisnotNone}ifnotfields:returnme(user)withengine.connect()asconn:updated=_db.update_user(conn,user['id'],**fields)return{k:updated[k]forkinupdated.keys()ifkin('id','email','nickname','bio','avatar_url')}

README.md

# User Auth MVP ## 快速启动 1. 创建并激活虚拟环境 ```bash python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate
  1. 安装依赖
pipinstall-rrequirements.txt
  1. 启动服务
uvicorn app:app--reload--port8000
  1. 访问交互文档
    http://localhost:8000/docs
--- ## 五、后续扩展建议(生产化路线) - 数据库迁移至 PostgreSQL/MySQL,使用 Alembic 管理迁移。 - 引入 Redis 实现 token 黑名单与接口限流。 - 使用密钥管理服务(KMS)存储 JWT secret,或改为非对称签名(RS256)。 - 增加邮箱验证、密码找回、头像上传(S3/Object Storage)等功能。 - 添加单元测试与集成测试覆盖核心流程。 --- 至此,一个完整、可运行的用户注册、登录、个人主页 MVP 已交付,可直接用于开发验证或作为后续功能的基础。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 18:52:59

Uppy文件上传验证终极指南:从基础限制到智能过滤的深度探索

Uppy文件上传验证终极指南:从基础限制到智能过滤的深度探索 【免费下载链接】uppy The next open source file uploader for web browsers :dog: 项目地址: https://gitcode.com/gh_mirrors/up/uppy 你是否曾经在文件上传功能中遇到这样的困惑:为…

作者头像 李华
网站建设 2026/8/31 16:37:07

28、网络编程与数据结构实战指南

网络编程与数据结构实战指南 在网络编程和数据结构领域,有许多实用的技术和方法可以帮助我们高效地处理各种任务。以下将详细介绍网络编程中的表单处理、网页内容提取,以及数据结构中的关联数组和哈希表的使用。 网络编程中的表单处理 在处理表单数据时,可以使用如下的脚…

作者头像 李华
网站建设 2026/9/2 4:49:34

Mac百度网盘加速插件:如何让下载速度提升70倍?

还在为百度网盘的蜗牛下载速度而烦恼吗?作为一名Mac用户,你一定经历过下载大文件时的漫长等待。今天,我要为你揭秘一个开源神器——BaiduNetdiskPlugin-macOS,它能让你告别限速困扰,享受全速下载的体验!这款…

作者头像 李华
网站建设 2026/9/2 15:14:49

GSE插件宏编辑终极指南:版本升级完整教程

GSE插件宏编辑终极指南:版本升级完整教程 【免费下载链接】GSE-Advanced-Macro-Compiler GSE is an alternative advanced macro editor and engine for World of Warcraft. It uses Travis for UnitTests, Coveralls to report on test coverage and the Curse pac…

作者头像 李华
网站建设 2026/9/1 21:46:09

DeepSeek-V3推理性能实战调优:从延迟瓶颈到吞吐量巅峰

你是否曾经在深夜盯着监控面板,看着P99延迟曲线不断攀升而束手无策?或者面对昂贵的GPU集群,却发现利用率始终无法突破60%?这些正是大模型推理优化中最常见的痛点。本文将带你深入DeepSeek-V3的性能调优实战,帮你找到那…

作者头像 李华