APP创业项目-用户身份认证架构

喜欢这篇文章吗?分享给你的朋友吧~ 

一个互联网项目通常会标配一个用户模块,主要实现用户注册、登录、忘记密码、密码加密、session创建与销毁。随着第三方认证的流行,还需要实现微信、QQ、微博等第三方账号登录及其账号绑定。另外,需要解决APP用户的持久登录问题,多应用服务器身份共享问题,APP内嵌H5应用的身份注入问题等。

首先我们聊以下几个顶层设计的问题。

用户中心是做成一个模块还是一个独立服务?

首先,我们打开百度、淘宝、京东、QQ等互联网产品,你会发现他们都提供了多种多样的独立服务(网站),例如百度有:贴吧、知道、百度云、音乐随心听、糯米团购、外卖等等,京东作为一个主营电商的网站也划分为若干独立的服务:首页www.jd.com、商品列表list.jd.com、商品详情item.jd.com、订单order.jd.com、个人中心home.jd.com等等。每个公司的各种服务都共用同一个身份认证系统:百度登录对话框、淘宝https://login.taobao.com、京东https://passport.jd.com、QQ登录对话框。可见大公司常见的模式是将用户中心做成一个独立服务。

共用一个身份认证服务有什么好处呢?

假如不使用同一个身份认证服务,各自开发各自的身份认证模块,那么各网站服务之间用户无法共通,从一个网站跳到另一个网站需要重新登录,对用户来说很繁琐。

数据共通能够帮助产品团队更好地了解用户,例如用户使用百度搜索“美人鱼”想看下周星驰导演的这部电影的影评,但是除了周星驰导演的《美人鱼》之外,美人鱼还有别的实体,如伊丽莎白·艾伦导演的《美人鱼》,周杰伦的歌《美人鱼》,如果用户多次点击了周星驰的《美人鱼》的相关网页,那用户可能是这部电影的潜在消费者,如果用户随后进入百度的糯米网(包含电影票销售服务),则可以在首页显示《美人鱼》的片花和优惠信息。因此,打通了各服务之间的用户,可以整合各服务的数据去了解用户(即用户画像)。

京东作为一个电商网站,并不像百度,产品那么多样化,为什么也要把首页、商品列表、商品详情、订单等等这些功能模块分到不同的子域名上,由https://passport.jd.com负责认证呢?

一个大型网站项目,通常由很多开发人员分成小组进行开发,各小组既要解耦又要相互合作。解耦的位置可以在模块化代码层面,也可以在网站架构层面。这里将他们称为:代码模块化解耦、多网站架构解耦。

代码模块化解耦要求各小组基于一个基础核心代码,使用同一个语言在同一个框架下各自编写模块,各模块处理好包管理和命名空间,最终合并代码形成一个独立站点。所有代码运行于同一个服务器环境,运维相对简单。但是这部分代码被各个网站平台共用,代码维护将十分困难。

多网站架构解耦要求各小组共用身份认证服务,其他开发细节不再关心,理论上可自由发挥,甚至可以采用不同的语言、框架和服务器,相比代码模块化更加灵活;在服务器负载均衡上也可以更加灵活,可以针对访问频繁的功能部署更多的服务器以提升速度,针对冷门的功能部署较少的服务器以减少开支。

不管是代码模块化解耦还是多网站架构解耦,只要涉及到多应用服务器架构,实现独立的身份认证服务都是一个最佳的选择。

我们知道不同子域名通常意味着不同的网站,不同的网站应该放置到不同的服务器,那么他们之间不可以使用本地文件系统存储session,因为这将导致各网站之间无法共享session。如何解决这个问题呢?

 

通过上面的几个问题,我们了解了建设一个独立的用户身份认证服务的必要性和优势。下面主要讨论,如何建设一个独立的用户身份认证服务?

业务需求:

1. 实现用户的注册、登录、忘记密码、密码加密、用户激活、用户禁止等基础服务。

2. 实现第三方账号(微信、QQ、微博等)绑定与登录

3. 实现全局用户ID

4. 实现APP内嵌应用身份注入和OpenID机制(类似微信的第三方认证)

5. 保持APP用户身份,一次登录以后都不再需要登录

6. 匿名用户

 

1. 实现用户的注册、登录、忘记密码、密码加密、用户激活、用户禁止等基础服务

这些基础服务由认证服务器实现,具体如何实现这些服务不细谈,主要谈下核心部分:session管理。

1) session管理

所谓的身份认证,服务器生成一个session_id给用户,并以此session_id为索引指向一个存储空间,在存储空间中写入用户的相关数据,如user_id;用户得到session_id,浏览器将其写入用户本地文件系统,每次请求时附带session_id,服务器端依据session_id找到用户数据存储位置,则得到user_id,从而知道当前请求是哪个用户。

用户在认证服务器进行身份认证,认证服务器分配session_id,并指向一个存储位置,这个存储位置必须使所有应用服务器可以读取,且读取速度应该尽可能快,那么Redis内存数据库是当前最佳的选择。

当用户从应用服务器点击登录后,跳转到认证服务器,输入用户名和密码认证通过后,认证服务器向Redis写入以session_id为key,以用户ID、用户Email、用户状态等用户相关数据为value的K-V键值对数据。再将用户跳转到登录前的页面,那么登录前的页面如何确定当前用户的身份呢?

如果认证服务器与应用服务器是同一域下,如passport.jd.com与home.jd.com处于同一个域jd.com下,http协议规定同一个域下的cookie是互相可访问的,即home.jd.com可以获取passport.jd.com的cookie,因此home可以获取认证服务器返回给用户的cookie中的session_id,home连接Redis获取session_id对应的value,并可获得用户ID。如果认证服务器与应用服务器不在同一个域下怎么办?参见4.实现APP内嵌应用身份注入。

应用服务器可以读取认证服务器写入的Redis存储空间,并不意味着应用服务器也使用此存储空间存储自身业务相关的session,每个应用应该有自己的session管理方法。例如应用A自己的业务需要在session中写入用户的昵称、会员等级等数据,这些数据只与应用A有关,不应该写入到认证服务器的Redis空间。

2)session_id安全性

如果A用户伪造B用户的session_id,将会具有B用户的身份,因此session_id的生成算法必须难以猜测,通常采用“时间戳+用户ID+随机数”进行不可逆的Hash散列。同时,引入IP鉴定机制,记录用户常用的IP和归属地,当用户出现在非常用IP归属地时要求用户重新登录。还有很多其他的安全措施,超出了本文的主题。

3)密码加密

关于如何设计加密机制,该系列将专门写一篇文章说明加密。

4)用户激活

传统采用邮箱激活,生成激活Token,以链接的方式发送邮件给用户。Token进行适当加密,并设置过期时间。如今很多互联网服务都要求用户进行手机验证。

5)忘记密码

与用户激活思想一致,通过发送带有Token的邮件给用户。手机验证用户可通过验证码(简单的Token)直接设置新密码。

6)用户禁止

对于存在一些非正常行为的用户禁止账号登录。

 

 2. 实现第三方账号(微信、QQ、微博等)绑定与登录

微信、QQ、微博是装机量很高的互联网服务,使用这些第三方账号登录能够提高用户的体验,免去用户记录用户名和密码的压力。第三方登录的用户等同于注册账户,但即使使用第三方登录,仍然需要在产品本地为其创建用户,有必要的话需要引导用户设置本地用户名和密码、甚至手机认证(这有可能导致用户反感),假设一个使用微信登录产品的用户,因为跟女朋友分手了,卸载了微信,那么他以后怎么登录你的产品?

所有的第三方账号认证都需要申请一个开发者账号,获得Key和Secret,用户点击微信登录时,将用户导引到微信应用,用户授权后,获取到微信返回的Code,用Code换取Token,以Token作为授权依据获取用户的微信OpenID及其个人资料。具体参见微信官方文档。将OpenID写入微信用户表,创建本地用户,将本地用户ID与微信OpenID绑定,将session写入Redis。下次用户登录时,从微信用户表中获取微信OpenID对应的本地用户ID,即可实现认证。

由于用户可能突然放弃使用微信转而使用QQ,但并不想在APP产品中重新创建用户,一般会为APP用户提供绑定多个第三方账号的功能。

 

3. 全局用户ID

为了实现用户的唯一识别,为用户生成全局用户ID,所有应用可根据此ID找到唯一的用户。各应用可以将全局用户ID作为自己系统的用户ID,也可以将全局用户ID与自己系统的用户ID进行一对一绑定。

 

4. 实现APP内嵌应用身份注入和OpenID机制(类似微信的第三方认证)

APP内部常会嵌入一些H5的应用,即混合式APP开发,如果这些应用与认证服务器都是同域的,只需要将APP的cookie写入到WebView即可正常连接Redis获取身份。

如果APP内嵌H5应用与认证服务器不是同域,这就相当于京东的各个模块不在jd.com的域下,例如home.jingdong.com,这将无法获取passport.jd.com的cookie,而无法获取到session_id。此时应采用微信的第三方认证模式,home.jingdong.com(简称H应用)将passport.jd.com(简称Passport)视为第三方认证。

H应用将用户重定向到Passport网站,用户登录成功后被重定向回home.jingdong.com?code=xxxx,附带code,H应用使用code与key、secret发送请求到Passport获取Token,使用Token发送请求获取用户全局ID,最终确定用户登录成功,并将用户全局ID与本地用户ID进行绑定。

认证服务器需建立key、secret、token管理机制。

 

5. 保持APP用户身份,一次登录以后都不再需要登录

APP的登录是一件繁琐的事情,为了让用户一次登录永远不要再登录,一种方法是将session设置为永不过期;如果认证服务器不能全局上将用户session设置为永不过期,那么应用服务器可以为用户生成Token(类似session)存储于应用服务器自身的session空间,未来依据此Token即可认定用户身份有效。

 

6. 匿名用户

有些应用,例如今日头条、网易新闻,用户可以匿名浏览新闻,而且还可以为用户推荐感兴趣的新闻。显然,必须先识别用户才能进行用户推荐。因此,对于匿名用户,应该在APP首次打开时为其创建一个UUID,以此来记录用户的历史行为。引入了匿名用户自然为产品逻辑引入分支,建议尽量限制匿名用户的行为,尽快引导其注册登录。

 

注意点:

1. 身份认证服务应采用https协议

http协议是一个透明的协议,未经加密的明文传输,任何http报文经过的设备(如运营商的路由器)都可以拆开http的报文获取到报文中的用户名和密码。例如,某公司的网管,扫描所有经过路由器的http报文,拆包后,检索http头部Request URL为passport.jd.com的包,获取form表单提交的数据,将得到公司内部员工的jd账号和密码。https对传输过程中的数据进行了加密,无法直接浏览其明文。

2. Redis的安全问题

除了认证服务器之外,所有应用都可以连接到Redis进行读写,如果有些应用对Redis进行错误的写入,有可能导致全局问题。是否可以不让应用服务器连接Redis也能实现身份认证?

可以,由认证服务器提供认证代理。应用服务器从cookie中拿出session_id后向认证服务器请求认证此session_id是否身份有效,并返回该session_id对应用户全局ID。需要注意的安全问题:必须为认证服务器的认证接口指定访问IP白名单,请求认证时必须借助签名机制(具体以后再说)

 

总结

本文说明了使用独立身份认证系统的优势,并分析了认证服务器架构所涉及的常见问题,建议开发者开发大型项目时采用多站点架构方案进行开发解耦,建议所有应用使用同一个域,类似QQ,所有的应用都是XX.qq.com。

声明:本文谢绝转载。

版权声明:本博客所有内容,文尾声明谢绝转载的文章禁止转载,未声明谢绝转载的文章,遵循 CC BY-NC-ND 协议,分享-署名-非商业-禁止演绎。
原文链接:APP创业项目-用户身份认证架构
来源站点:ComingX

0 Responses to “APP创业项目-用户身份认证架构”


  • No Comments

Leave a Reply

使用新浪微博登陆