开发 / 2025-11-29

LansnAccountCenter结业成功~

A
Akanyi
46

这里是Akanyi~ 最近完成了一个个人项目——LansnAccountCenter,一个基于 FastAPI 的身份认证中心。这里是项目结业以后的碎碎念。

最初,我只是想做一个简单的用户中心,但写着写着,我意识到把它做成一个功能更完整的身份和访问管理IAM轻量化解决方案似乎更好。于是,项目的核心定位便改为支持 OAuth 2.0 和 OpenID Connect-OIDC的身份提供商。

这意味着,LansnAccountCenter 不仅能管理自己的用户,还能为我未来的其他个人项目提供统一的账号登录功能,何乐而不为呢?

技术选型

在技术选型上,我主要考虑的是开发效率、性能和社区生态。最终我的技术栈如下:

  • Web 框架: 我选择了 FastAPI + Uvicorn。它的高性能、原生的异步支持和无敌的自动文档生成深得我心。
  • 数据库: SQLAlchemy (异步) + aiosqlite。我希望整个项目是异步的,所以选择了异步 ORM。配合 aiosqlite,即便只是用 SQLite,也能保证 I/O 不会成为瓶颈,对于个人项目来说绰绰有余。
  • 安全:
    • python-jose[cryptography]: 实现 OIDC/OAuth2 的核心,用它来处理 JWT 签名和验证。
    • bcrypt: 密码安全是重中之重,用它来对用户密码进行哈希存储。
    • pyotp & qrcode: 用于实现 2FA 功能,提供基于时间的一次性密码(TOTP)。
  • 其他: Jinja2 用于模板渲染、pydantic-settings 优雅地管理配置、fastapi-mail 负责发送邮件、redis 用于 Session 和缓存。可以说,开发体验非常舒畅。

架构清晰、方便拓展

在项目结构上,我遵循了关注点分离的原则。

路由模块化

根据功能将路由拆分到 auth, oauth, users, two_factor 等不同模块中。这样做的好处是每个模块的职责都非常单一,后期维护和扩展都会轻松很多。

代码如下:

# main.py
app.include_router(auth.router)
app.include_router(oauth.router)
app.include_router(users.router)
app.include_router(two_factor.router)
app.include_router(captcha.router)

数据模型

在数据模型设计上,我主要围绕 OIDC 的核心概念构建了四张表:

  • User: 核心用户表,我设计它支持用户名、手机、邮箱、GitHub ID 等多种登录方式,提供最大的灵活性。
  • Client: 代表接入的第三方应用,存储了 client_idclient_secret 等 OAuth 客户端凭证。
  • UserGrant: 这是一张联结表,用于记录用户对应用的授权关系,解决了用户与应用之间多对多的授权问题。
  • AuthorizationCode: 用于存储 OAuth 授权码流程中产生的临时 code

最好玩的设计,PoW+验证码

在开发过程中,我花心思最多的地方之一就是人机验证。国内验证码成本太高了,为了有效保护钱包,我只能在验证码上下点功夫。

常规 Session Token 验证

这是基础,确保请求来自一个合法的、由服务器初始化的验证会话,防止跨站伪造请求。

工作量证明(PoW)

这是我最喜欢的部分!在用户请求发送验证码前,我要求前端必须先解决一个基于 SHA256 的哈希难题。

# routers/auth.py - 部分
def verify_pow(prefix: str, nonce: int, difficulty: int, dom_value: str) -> bool:
    attempt = f"{prefix}:{nonce}:{dom_value}"
    hash_result = hashlib.sha256(attempt.encode()).hexdigest()
    return hash_result.startswith('0' * difficulty)

前端必须通过不断尝试 nonce 值,来计算出一个符合特定难度(比如前 N 位为 114514)的哈希值。这个过程会消耗攻击者的 CPU 资源,对于正常用户来说几乎无感,但对批量攻击的机器人来说,成本是指数级增加的。

验证前端算出来的答案对不对。算不对?那肯定是脚本小子,直接打回去。

后端验证

人机验证的交互是点击图片中不动的图形。 - 前端只负责记录点击的坐标 (x, y) 并传给后端。 - 后端从 session 中取出在生成挑战时就已确定的、正确的图形位置,然后在后端进行碰撞检测,不信任前端的任何判断。

# routers/auth.py - 部分
# 用session里存的真实坐标和大小来判断用户
correct_shape = captcha_data["shapes"][captcha_data["correct_index"]]
is_hit = check_hit(correct_shape, captcha_x, captcha_y)

---END---

开发这个Project很有意思。我在安全设计上有很多的自由发挥。特别是那个基于 PoW 的人机验证机制,算是我在安全与用户体验之间找到的一个比较满意的平衡点。

另外,Syncer准备上线了,这是一个跨网同步系统,有兴趣的可以关注一下~

:wq

评论区

发表评论

暂无评论。来抢沙发吧!