轻松筹1.6亿注册用户的Passport账户系统架构

轻松筹是全国1.6亿人使用的全民众筹平台,几乎所有核心业务都依赖于账号系统,账号系统的用户体验,安全性,稳定性直接影响着轻松筹所有业务的运行;
轻松筹的发展非常迅速,已经展开了多条产品线,单点登录的需求愈加强烈;另外由于历史包袱的原因,也遗留了一些问题亟待解决。
本次交流主要与大家分享一下轻松筹账号系统(侧重登录授权服务)的架构设计和改造方案。

历史背景:

由于历史包袱的遗留问题,轻松筹的账号系统(登录授权服务)之前主要存在以下几个方面的不足:

  1. 由于历史包袱的遗留问题,三端登录方式不统一
  2. 产品线增多,单点登录的需求越来越强烈
  3. 安全性不够
  4. 容灾能力不够

设计目标:

需要达到以下几个方面的目标

A用户体验:

  1. 保证只要用户较长时间段(比如30天)内登录过,就不需要重新登录
  2. 单点登录,在一个站点登录后,另外一个站点就不需要重复登录(用户几乎无感知)
  3. 采用第三方登录的时候,能使用每一个站点对应的公众号

B安全性:

  1. token难以伪造,具有一定不可逆性
  2. token应该有较快的过期机制,避免被人获取token后伪造用户操作
  3. 被恶意窃取后,具有发现机制

C稳定性:

  1. token具有自解释性,即自带某些信息,在某些极端恶劣情况下(比如存储服务挂了),依然能提供服务

实现方案

概述

账号系统最核心的功能就是登录授权,总体思路也很简单:

用户登录成功后,服务端会生成token信息,并将其和用户信息关联起来,返回给前端token信息,

前端携带token来访问所有接口,后端再根据前端发过来的token信息标识这是哪个用户的行为

1 token信息的设计说明

这里所说的token信息包括以下几个字段(服务端生成后返回给前端的)

字段
意义
说明
备注
access_token 用户的唯一标识 需要验证登录的每一个请求都需要携带,放在http请求头Qsc-Token中 默认过期时间2小时
token_type token的类型 暂时保留 无用
expires_in

access_token的过期时间

前端发起请求的时候可以先用这个时间预先判断一下,减少不必要的请求  
refresh_token 刷新token 当access_token过期时,用refresh_token去换取新的token信息 默认过期时间30天

 

服务端token存储的信息(服务端记录的信息)

字段
意义
说明
备注
platform android,ios,wx-h5,pc-h5等 用户区别当前用户是通过什么形式登录的 同一个平台只维护一个token
session_id 登录后的唯一标识,不重新登录就不会变 不会随着access_token和refresh_token的刷新而改变 可以用于存储一些和登录关联的数据

auth_type

 登录的方式  微信h5,微信公众号,微信app,qq,微博,等等  


服务端存储的数据(key-val存储):

key(access_token) =(user_id,session_id,platform,auth_type)

key(refresh_token) =(user_id,session_id,platform,auth_type)

key(user_id,platform) =(access_token,refresh_token,expires_in,token_type)

 

说明:

  1. access_token=hex(hash(uid+time)+aes(uid+time))得到,达到目标B1
  2. access_token过期时间较短,refresh_token较长,两者结合,达到目标A1B2
  3. token中自带uid信息,即使服务存储挂了,依然不影响其它业务,达到目标C1
  4. 前端h5("wxh5" "pch5" "waph5")每一个平台仅维护一个token,多次登录会剔除旧的,实现目标B3
  5. passport前端拿到这个token信息之后,应该记录一下获取时间cur_time;通过 cur_time+expires_in与当前时间 提前进行比较来判断token是否已经过期,减少不必要的后端请求
  6. 需要登录的接口,前端需要每次都在请求头中写入Qsc-Token:access_token 字段

 

当access_token过期时,用refresh_token去换取新的token信息,刷新token接口流程为:

  1. 用户使用refresh_token调用刷新token接口,后端判断,若key(refresh_token)不存在,直接报错,
  2. 若是存在,再用key(user_id,platform) 存储的信息校验两者当前的refresh_token是否一致,若是不一致,说明有人利用这个token在你之前调用了刷新token接口
  3. 若是一致,则重新生成token信息,替换 key(user_id,platform) 存储的信息,并且处理旧的信息:旧的key(access_token)直接删掉,旧的key(refresh_token)保留一段时间

注意:

  1. 前端刷新token的时候,应该保证操作是互斥(串行)的,否则影响第上述的“踢人”功能,(app的前端互斥很好做,加锁就可以了;h5怎么实现前端互斥,后面会介绍)
  2. 后端刷新token的时候,应该保证操作是互斥(串行)的(分布式锁),保证始终只有一个token有效

 

关于踢人逻辑的说明:

需要在以下两种情况都能达到踢人的效果:

  1. 用户的账号被窃取(第三方账号,或者手机验证码)
  2. 用户前端的refresh_token被窃取

解决方法:

  1. 以上两种情况用户操作后,都会重新生成token信息,并且替换key(user_id,platform) 存储的信息,然后将旧的key(access_token)直接删掉,旧的key(refresh_token)保留一段时间,这个时候这个恶意用户是可以伪装的
  2. 但是当原来的用户调用刷新token接口的时候(使用的是旧的refresh_token),这个时候还是能获得key(refresh_token)里面的信息,再取出key(user_id,platform) 存储的信息来校验,会发现两处的refresh_token是不一致的,既可以发现是被人踢下去的


特别说明:

为什么不用一个token随着请求反复刷新来达到 access_token和refresh_token的效果呢??

  1. 因为这种方式前端页面会有并发请求的情况,token的刷新是需要加锁的,会带来很大的开销;
  2. 没法保证前端 刷新token 的互斥,会导致反复失效的情况

2 web-app平台单点登录方案

对于所有产品线的web平台都实现单点登录SSO(Single Sign On)的功能,这样只要在一个产品线上(比如站点A)登录了,在其它产品线上(比如站点B)就不需要再重新登陆了

实现目标A2,A3

注意:
  1. 如果passport本地有缓存,优先使用缓存
  2. 在token失效的时候,跳转到passport的同时,应该把 旧的token 带着,如果passport本地的token跟这个相同则刷新token,如果不相同,则直接返回这个token 即可
  3. 每一个站点跳转的时候需要携带该站点的标识,passport 根据这个标识决定使用哪一个公众号登录(仅限第三方登录)
  4. 各个站点不应该自己refresh token,在access_token 失效的时候跳转的passport统一处理,这里可以保证刷新token是互斥的
  5. passport前端拿到这个token信息之后,应该记录一下获取时间cur_time;然后把这个cur_time+expires_in+access_token 传给各个站点
    各个站点 通过 cur_time+expires_in与当前时间 进行比较来判断是否已经过期,
    如果过期,则不需要请求后端(当然这个时候请求后端也会返回token失效)
    各个站点可以根据自己的需要,提前过期,比如说提前一个小时就认为token过期了,跳转到passport重新获取,这也是用户体验上的 考虑

3 native-app平台登录方案

对于所有产品线的ios,android等平台暂时不考虑单点登录的功能,但是以后会考虑(比如一个app唤起另外一个app获取登录token信息)

整体方案与web-app几乎一致,但是有一些特殊性:

  1. 对于 ios,android,wxapp小程序,需要独立开发sdk,然后各个站点统一使用同一套sdk
  2. 这个sdk应该包括ui的展现和后端的交互逻辑,统一开发,方便维护
  3. 各平台保证ui风格统一

这里不作过多描述

接下来 介绍一下 具体的登录流程:
目前登录授权支持两种方式
1. 通过手机验证码登录
2.通过第三方平台登录(新用户需要绑定手机号,即再走一遍第一步


4 手机验证码登录

这里仅存在于passport前端和后端交互

流程图:


1. 为了防止刷短信的问题,增加了图片验证码的校验,但是考虑用户体验,仅仅只是在怀疑对方是恶意操作的时候
2. 为了防止短信验证码的暴力破解,做了一些错误次数的校验

 

5 第三方登录(微信,微博,qq等)

流程图:


图中:
黄色区域指的是通过第三方平台获取第三方提供的code,中间需要周转几次,这里省略
红色区域指的是通过手机验证码登录,就是 手机验证码登录-流程图

总结

轻松筹是全国1.6亿人使用的全民众筹平台,我们的账号系统为平台所有用户提供着服务。

账号系统其实还包含很多方面,目前还有很多不足的地方,轻松筹还在一直在用户体验,安全性,稳定性等方面持续改进。

以上 就是今天所有的分享,感谢大家抽出宝贵的时间一起交流

欢迎大家提出宝贵的意见。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章