登陆服务 | 一、准备

2021 10 26, Tue

最近思忖着自己写一套服务给自己用,或者未来可能有他用也说不定。登陆是其中被依赖最多的,我们先写一个简单的登陆服务。

需求

登陆核心的东西就是……登陆。一般来说有这么一些东西要做

  • 用户
    • 注册、注销
    • 登陆、登出
    • 修改信息
    • 找回账户
  • 额外的保护
    • 验证码
    • 邮件验证
    • 短信验证
    • OTP
    • 弱密码数据库
  • 管理
    • 用户
    • 集团
    • 用户组
  • 提供SSO / 外部SSO / 统一注册
    • OIDC
    • SAML
    • LDAP
    • Kerberos

用户和管理

用户的部分基本上主要就是用户信息的CRUD,这部分没有太多需要担心的,正常实现每个部分的DAO、Controller和View就好

额外保护

额外的保护指的是两方面,一方面是用户本身账号有问题时可以及时恢复,另一方面是如果有人想要做坏事,我们需要能及时阻止对数据和用户的侵犯。

那么无论是做什么,验证码都是一个不能少的东西,一方面可以作为rate limiter使用,另一方面可以阻止自动化工具的侵入……虽然现在很多工具也可以自动解决验证码了……

对于用户来说,找回密码、找回账号一般可以通过Recovery Code、邮件和短信进行,我们在这种情况下一般假定能触碰到用户的其他服务提供商是可信的。

单点登陆

单点登陆也就是SSO,Single Sign On,在一个站点登陆就可以为关联的其他站点提供服务。最常见的就是OIDC和SAML这两种形式,有很多服务支持OIDC登陆,也就是OIDC Client,也有很多服务可以兼职提供OIDC登陆,一般叫提供OIDC登陆的叫Provider。

退一步还有一种形式是在一个位置注册以后,在其他地方可以用同一个身份登陆的,比较常见的就是微软提供的LDAP,和开源的Kerberos。有一些服务只支持外部的LDAP登陆,那么我们就可能需要提供到LDAP的双向同步能力。

取舍

考虑到是给自己写服务,那么我们就可以在一些方面留出空间后作出取舍。考虑实现需要的时间和我自己的士气,第一步大致希望实现的包括这些:

  • 用户
    • 注册、注销
    • 登陆、登出
    • 修改信息
    • 找回账户
  • 额外的保护
    • 验证码
    • 邮件验证
    • 短信验证
    • OTP
    • 弱密码数据库
  • 管理
    • 用户
    • 集团
    • 用户组
  • 提供SSO / 外部SSO / 统一注册
    • OIDC
    • SAML
    • LDAP
    • Kerberos

架构

登陆服务就是一个简单的Web服务,我们需要一个Web前端,用来提供界面,需要一个Worker在后台做一些异步工作,比如说可能会有一个单独的发信服务。

存储的话分为两部分,一部分是持久化数据,对于登陆来说主要就是用户数据,这些数据有明显的关联性质,所以用SQL类数据库就好。另一部分是易失数据,可能会随着时间、升级或者重启不再有效的数据,比如说Session、Rate Limiter这些,通常是以某个key为索引,和其他数据关联不大的东西,Redis或者memcached就足够了。

除此之外,提供SSO的部分我们可以自己写逻辑,但是没有必要,因为一个服务做好一个服务的核心能力就好,OIDC有ory/hydra,我们只需要写两个前端,以及一个管理界面即可。

外部服务我们可能会依赖

  • 验证码服务:自己写图形验证码很容易被破解,故且使用第三方验证码
  • hCAPTCHA / ReCAPTCHA
  • 极验
  • 邮件:自建邮件服务很容易被拦截,使用Mailgun这类发信服务商可以提高到达率
  • SendGrid
  • Mailgun

技术选型

在写这篇博客的时候,我已经使用Go写了一个登陆服务的雏形。

考虑登陆服务的性质,大部分内容不涉及计算,加上我自己希望服务的资源占用小一点。平衡考虑以后,Python资源占用会略微高一点点,Rust资源占用少但是我暂时不需要借助Tokio进行异步过程的调优,Go编写起来足够简单、资源占用非常小、goroutine已经足够好了,所以后段使用Go来实现。

数据库的方面ORM是很香,但是考虑调优我还是用标准库来做了。

Web的部分使用Gorilla/Mux + html/template来实现,数据库用标准库sql.DB + mysql和sqlite。异步服务考虑使用gocelery。