PaperMind学术阅读平台搭建(一)

张开发
2026/4/14 13:47:17 15 分钟阅读

分享文章

PaperMind学术阅读平台搭建(一)
一、整体架构说明我们在实现这个功能的时候也去参考了许多的相关登录功能实现但是考虑到我们这个是一个课程项目而不是真正的需要高并发等等复杂场景为了方便实现因此选择了较为简单的轻量方案来实现。后端FastAPI 中间件拦截 Cookie-Session 机制存储暂时JSON 文件存储用户数据无数据库依赖这里只是我们当前开发阶段暂未设计数据库等后续设计好数据库后会加入数据库依赖密码SHA-256 哈希这里也是基于我们上学期密码学与编程也了解到存储密码不应该明文存储而是应该使用哈希加密等等技术手段存储密文值前端原生 HTML/CSS/JS登录页与主页分离这里考虑到因为是作为课程项目我们暂时不需要 JWT、Redis Session、OAuth 这些重型方案我也去了解了相关的技术可能后面几个星期再优化时会加入这些技术方案目前先轻量实现但是轻量实现该有的安全登录认证等等都有二、后端认证实现2.1 用户服务层用户数据存储在一个 JSON 文件中结构很简单{admin: a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3,zhangsan: 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92}键是用户名值是密码的 SHA-256 哈希。核心代码class AuthService:def __init__(self, settings: Settings) - None:self.settings settingsself._lock threading.Lock()self.users_file: Path settings.auth_users_fileif not self.users_file.exists():self._write_users({})if not self.user_exists(settings.auth_username):self.register(settings.auth_username, settings.auth_password)def register(self, username: str, password: str) - None:username username.strip()if not username:raise ValueError(用户名不能为空。)if len(username) 3 or len(username) 24:raise ValueError(用户名长度需在 3~24 之间。)if len(password) 6:raise ValueError(密码长度至少 6 位。)with self._lock:users self._read_users()if username in users:raise ValueError(用户名已存在。)users[username] self._hash_password(password)self._write_users(users)def verify_user(self, username: str, password: str) - bool:users self._read_users()digest users.get(username.strip())if digest is None:return Falsereturn digest self._hash_password(password)staticmethoddef _hash_password(password: str) - str:return hashlib.sha256(password.encode(utf-8)).hexdigest()个人理解这里有几个值得注意的设计决策1. 线程锁注册操作涉及“读-判断-写”三步如果不加锁并发注册可能导致同名用户被重复创建。虽然课程项目并发量不高但养成这个习惯很重要。2. SHA-256 生产环境应该用 bcrypt/scrypt/argon2 这类慢哈希算法来抵抗暴力破解。SHA-256 速度太快攻击者可以高速试错。但对于课程项目SHA-256 足以体现“密码绝不能明文存储”的底线意识。3. JSON 文件存储没有引入数据库降低了部署复杂度。缺点是不适合高并发场景但对于课程项目和演示系统这种轻量方案反而更容易讲清楚。4. 默认管理员自动创建系统启动时会自动生成默认管理员账号这种设计能显著降低首次部署成本。2.2 登录、注册与 Cookie-Session后端登录逻辑写在 app/main.py 中核心流程如下1. 前端提交用户名和密码到 /api/v1/auth/login2. 后端验证账号密码3. 验证通过后生成随机 session_id4. 将 session_id - username 存到 app.state.sessions5. 通过 response.set_cookie() 把 Session 写入浏览器核心代码如下app.post(/api/v1/auth/login)def login(payload: LoginPayload):username payload.username.strip()password payload.passwordif not username or not password:raise HTTPException(status_code400, detail用户名和密码不能为空。)if not auth_service.verify_user(username, password) and not auth_service.user_exists(username):try:auth_service.register(username, password)except ValueError:passsession_id token_urlsafe(32)app.state.sessions[session_id] usernameresponse JSONResponse({message: 登录成功。, username: username})response.set_cookie(keysession_cookie_name,valuesession_id,httponlyTrue,samesitelax,max_age60 * 60 * 12,)return response注册接口则相对直接app.post(/api/v1/auth/register)def register(payload: RegisterPayload):if payload.password ! payload.confirm_password:raise HTTPException(status_code400, detail两次密码输入不一致。)try:auth_service.register(payload.username, payload.password)except ValueError as exc:raise HTTPException(status_code400, detailstr(exc)) from excreturn {message: 注册成功请登录。, username: payload.username.strip()}个人理解这里我觉得最值得讲的是我们没有选 JWT而是选了 Cookie-Session。原因很简单这个项目是一个典型的浏览器访问型系统前后端都由我们自己控制不需要跨端鉴权也没有复杂的第三方 API 调用场景。此时 Cookie-Session 反而更顺手浏览器会自动携带 Cookie前端代码更简洁Session 可以由服务端统一失效控制力更强不需要额外处理 JWT 刷新、续签、黑名单这些问题当然这种方式也有局限当前 Session 数据存放在内存里服务一重启所有登录状态都会消失。如果以后系统要上线我会优先把 Session 放到 Redis 中。if not auth_service.verify_user(username, password) and not auth_service.user_exists(username):try:auth_service.register(username, password)这意味着如果用户不存在登录接口会尝试自动注册。这在严格意义上不属于标准登录逻辑但从课程演示体验来看它降低了首次使用门槛。用户第一次输入账号密码时不一定非要先点“注册”。我的理解是这是一种“演示友好型设计”适合项目答辩或教学场景但如果是正式系统我会把登录和注册职责彻底分离避免语义混乱。2.3 中间件拦截未登录禁止访问解析系统为了防止未登录用户直接访问解析接口我们实现了一个认证中间件app.middleware(http)async def auth_middleware(request: Request, call_next):path request.url.pathif _is_public_path(path):return await call_next(request)if _get_session_user(request):return await call_next(request)if path.startswith(/api/):return JSONResponse(status_code401, content{detail: 请先登录。})return RedirectResponse(url/login)它做了三件事公共路径放行比如 /login、/health、登录接口、注册接口已登录用户放行未登录用户访问 API 时返回 401未登录用户访问页面时重定向到 /login个人理解这一段设计非常“实用主义”。它没有把权限系统做复杂而是先把“登录前不能用核心功能”这个基本约束落实下来。特别值得注意的一点是页面请求和接口请求做了差异化处理。页面请求重定向到登录页用户体验更自然API 请求直接返回 401 JSON方便前端准确识别错误这其实体现了一个很重要的 Web 开发思维同样是未认证不同请求类型的响应策略应该不同。三、前端登录页设计3.1 原生三件套的选型考量本项目采用原生 HTML、CSS、JavaScript 实现前端主要基于以下考量部署便捷性无需前端构建流程静态页面可直接通过 FastAPI 返回。结构透明性代码逻辑直观便于教学场景下的理解与评审。基础能力强化自主实现布局、动画与交互逻辑更能体现设计深度。设计哲学技术选型应服务于场景目标。在课程项目中原生开发能更纯粹地展示底层能力避免框架带来的抽象层干扰。3.2 视觉语言构建登录页以“轻柔治愈学术感”为核心风格关键设计元素色彩蓝粉渐变背景营造舒缓氛围。材质半透明毛玻璃面板增强现代感。布局左表单功能 右吉祥物情感平衡实用与趣味。动效云朵角色眼球跟随鼠标提升交互沉浸感。设计价值通过情感化设计降低工具冰冷感形成产品记忆点。例如云朵吉祥物将“学术解析”转化为具象化的温暖形象。3.3 登录/注册同页切换技术实现function openTab(type) { const isLogin type login; // 切换 Tab 状态 tabLogin.classList.toggle(active, isLogin); tabRegister.classList.toggle(active, !isLogin); // 控制表单显隐与动效 loginForm.classList.toggle(active, isLogin); registerForm.classList.toggle(active, !isLogin); }.form { opacity: 0; transform: translateX(14px); transition: opacity 0.25s, transform 0.25s; } .form.active { opacity: 1; transform: translateX(0); }设计优势操作连贯性避免页面跳转中断用户注意力。精致度提升微动效暗示状态变迁增强界面响应感。3.4 表单交互优化核心策略异步提交拦截默认行为通过fetch发送请求。即时反馈错误提示固定于表单底部非弹窗。密码一致性前端校验if (regPassword.value ! regConfirm.value) { setHint(两次密码不一致); return; }防重机制提交时禁用按钮。体验逻辑反馈贴近操作点形成用户预期路径。例如固定提示区的位置帮助用户快速定位信息。3.5 情感化细节设计云朵吉祥物通过 CSS 状态机表达情绪state-idle悬浮待机state-user输入时微倾state-pass密码输入时眯眼安全暗示state-submit提交时跳动进度反馈眼球追踪算法function moveEyes(clientX, clientY) { const maxOffset 3.5; eyes.forEach((eye) { const pupil eye.querySelector(.pupil); const rect eye.getBoundingClientRect(); const cx rect.left rect.width / 2; // 眼球中心 x const cy rect.top rect.height / 2; // 眼球中心 y const dx clientX - cx; const dy clientY - cy; const dist Math.max(Math.hypot(dx, dy), 1); const ox (dx / dist) * maxOffset; // x 轴偏移量 const oy (dy / dist) * maxOffset; // y 轴偏移量 pupil.style.transform translate(calc(-50% ${ox}px), calc(-50% ${oy}px)); }); }情感价值拟人化交互建立产品亲和力尤其在展示场景中成为差异化记忆符号。四、主页设计延续性主页继承登录页设计语言并强化功能分层布局卡片化分区上传区、任务状态区、结果预览区。信息流按用户操作链路排列模块提交→进度→结果。视觉统一毛玻璃导航栏、蓝粉主色调、圆角元素。设计准则界面应引导用户视线流动。例如任务状态侧栏的固定位置确保进度反馈始终可见。

更多文章