app 登录浅析

mr4831 2年前
   <h2>1.登录方式</h2>    <h3>(1)登录方式</h3>    <p>一般有以下几种登录方式</p>    <p>传统的账号密码登录,如下面的知乎账号/密码 登录,账号可以是邮箱,手机号或者账户名</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/7c3afa768b8a231d251c40e798dc0b5a.jpg"></p>    <p>手机验证码登录,如下面的今日头条手机验证码登录</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/177aee7aa2352353ea76e7c37a8fd3a5.jpg"></p>    <p>第三方登录,如下面的额知乎日报,上面的今日头条也一样</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/45dd1f5d3f151f424af5656c9624c81c.jpg"></p>    <p>还有一些新型的登录方式,比如图案锁,指纹,语音验证,虹膜扫描等等,这些都是在client进行验证,或者是一种变种的账号密码形式,在这里不作讨论。`为表述方便,分别使用 <strong>账密</strong> , <strong>验证码</strong> 和 <strong>第三方</strong> 来表述上面三种登录方式。</p>    <h3>(2)登录方式的大概流程</h3>    <p>账密:用户在client输入账号密码,发送到server端,server端进行database验证,如果验证通过则该用户认证成功,否则认证失败</p>    <p>验证码:用户输入手机号,并且点击获取验证码,server产生随机验证码并打上过期时间,通过SMS传输给用户,client输入该验证码,传到server进行判断</p>    <p>第三方:第三方相对复杂一点,client需要先到第三方网站或者APP进行授权,授权后跳转到server,server通过code码获取到相应的access_token,然后通过access_token获得这个用户在这个第三方的基本信息。</p>    <h3>(3)登录方式的优缺点以及适用场景</h3>    <p>账密:安全性相对较高,需要用户输入保护信息。使用场景较广,一般的应用都能够适用。</p>    <p>验证码:优点是便捷,缺点是安全系数相对较低,手机丢失即账号丢失,一般用在验证不是非常严密的应用,比如新闻资讯,地图这种获取咨询的app或者一些即用即关的一些应用,比如滴滴,货拉拉这种app,(注:内部支付有另外的验证)。</p>    <p>第三方:优点同样是便捷,比手机号还要编辑,都不需要输入验证码。安全性需要依托第三方的可信度。不过现在微信,微博等第三方的登录还是相对比较安全的。使用范围相对较广。便捷性方面不定,需要依托第三方的便捷性,比如微信就是需要安装微信的app,而有些第三方登录会跳转到该第三方的登录网站中,使用账号密码登录。所以安全性方面需要看第三方了,像微信这样的直接点击确定登录肯定没有输入第三方账号密码安全。综合来讲 <strong>第三方</strong> 登录安全性不差,便捷性较高,另外不用注册新的账号密码,在现在的登录方式中非常普遍。</p>    <h2>2.认证(Authentication)</h2>    <p>首先普及两个知识点:</p>    <p>a. <strong>认证(Authentication)和授权(Authorization)</strong> 。借用别人的一个例子,你要登陆论坛,输入用户名张三,密码1234,密码正确,证明你张三确实是张三,这就是 authentication;再一check用户张三是个版主,所以有权限加精删别人帖,这就是 authorization。</p>    <p>b. <strong>token</strong> :token就是令牌的意思。为什么client中使用token代替cookie/session呢?token去掉了服务器端的状态保持,扩展性更好,不需要在服务器进行存储,只需要验证就好。</p>    <h3>(1)账密认证</h3>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/d893adfdee5c0dfae7c4e5058da8fbc9.png"></p>    <p>b1b31c5f-d4ad-44b4-ae2a-3092b2e5474a.png</p>    <p>Client指的是移动终端,Application指的是应用服务器。</p>    <p>账密的认证很简单,数据库验证下就行。</p>    <p>注意:密码一般都会使用MD5进行hash下</p>    <h3>(2)验证码认证</h3>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/d39697604adcc9990ca99d84f08ee440.png"></p>    <p>比账号密码多了步产生验证码的过程,并且匹配一般在缓存数据库(如memcache或者redis)中进行,验证是需要验证过期时间。</p>    <h3>(3)第三方认证</h3>    <p>对于说使用友盟和shareSDK的同学,这篇文章不是讲怎么做,而是表述一些我在APP登录方面的一些见解,以及原理剖析。</p>    <p>为了描述清楚,使用微信作为代表(事先需要到各个迪桑放网站注册自己的server Application)</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/aa104096af6965323782c35794e095e8.png"></p>    <p>步骤如下:</p>    <p>1.client点击微信图标登录,带上Application ID和scope,这些都是在你注册Application的时候可以看到的</p>    <p>2.微信SDK会调用本地接口,打开微信的授权页,如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/b6dbd2ec3d3a8dbc5e3fedd3f47bea6f.png"></p>    <p>3.点击确定登录,微信server验证用户,然后会redirect to 你的Application server,带上code码</p>    <p>4.code码的有效期很短,需要立即使用code码用API从微信server换取 access_token 等验证数据</p>    <p>5.application获取到token后就能将这些数据存储到数据库,(数据库具体格式后面会有介绍)。并且通过 access_token 从微信server拿到用户的一些基本信息,比如头像,昵称等等。</p>    <p>6.有些应用不允许单独社交账号存在,必须绑定手机号码/密码,因此对于不同的业务,不同的场景各自去做。这其实也是一大块,手机号码的绑定,解绑,注册,第三方账号的绑定解绑,绑定到另外的手机号,密码找回,是否用手机号为主账号,等等等等一系列的业务逻辑,各个公司各个产品因为安全性能要求和设计不同,表现形式也是多种多样。在这里不做详细的探讨了,有兴趣的同学可以自己去梳理梳理。</p>    <p>7.application server产生自己的 access_token ,返回给client,client进行用户数据的访问和API的调用。</p>    <p>关于微信的第三方登录流程具体的可以去 微信开放平台 中的->资源中心->移动应用->微信登录功能。其他的平台都差不多。</p>    <p>Client始终没有拿到第三方的token,是因为client获取第三方数据完全可以通过server中转,这样就避免将token暴露给Client,这样第三方中用户的数据也会更加安全</p>    <h2>3.授权(Authorization)</h2>    <p>对于不同的登录方式会有不同的认证过程,但是授权就需要进行统一管理。</p>    <p>授权的关键点在于怎么产生一个好的access_token让client进行数据访问。oAuth2是一个很好的解决方案,JWT(JSON Web Token)是目前比较流行也比较安全的一种token产生和验证机制。</p>    <p>当然也可以自己产生token,一般使用"时间戳+UserGuid+椒盐噪声"然后非对称加密,传输给Client。JWT是一种更加规范,更加安全的Token的生成和校验方式。</p>    <p><a href="/misc/goto?guid=4959739845082114008" rel="nofollow,noindex">jwt</a></p>    <p><a href="/misc/goto?guid=4959739845166555514" rel="nofollow,noindex">jwt规范</a></p>    <p><a href="/misc/goto?guid=4959739845264970448" rel="nofollow,noindex">JWT的介绍看这里</a></p>    <h3>授权过程:</h3>    <p>(1)认证成功后,得到或者生成 UserGui d(用户的唯一id码,同一个账户的不同登录方式拥有同一个 UserGuid ),然后通过JWT生成token,token中包含 UserGuid ,token过期时间等等。</p>    <p>(2)将JWT token传送给client,client收到后保存下来。之后在向服务器请求数据时带在Header上</p>    <p>(3)服务器收到client的请求,然后使用中间件进行JWT拆分,得到其中的token过期时间, UserId 以及其他一些Claims。根据其中的Claim进行身份校验,这里不同的server有不同的操作。</p>    <p>注意:1.JWT token是明文传输的,可以通过 base64url_decode 获得其中的Claims信息,因此不要将敏感信息放进去</p>    <p>2.JWT的安全:因为JWT的第三部分是通过HS256加密的,所以client不能够通过Claims进行仿造token,这样会导致在server校验不通过。但是如果整个token被盗取了就没有办法了,毕竟这个tokn是所有请求权限的令牌。当然也可以通过在token中添加设备码等信息进一步加强其安全性。</p>    <p>3.token过期刷新问题:如果server检查到client的token过期,就会返回401错误。而一般的token设置有效期不长,总不能每次过期就重新登录吧。有两个解决方案。一是在在有效时间,比如在时间到达有效时间的3/4时,就开始向客户端推送新的token,最极端的做法是每次请求都换token,这样安全性高,但是太费server资源,并且在高并发情况下会有旧的请求被否决的情况。还有一种方案,就是在过期时间外再设定一个刷新时间期限的Claim,这个时间设定长一些,跟OAuth2中的 refresh_token 相似。当检测到token过期时,查看刷新时间过期没有,如果没有过期,则重新生成JWT token返回给client。</p>    <h2>4.server端数据库设计</h2>    <p>用户登录模块在数据库模块也需要好好设计,不能因为每添加一种第三方认证就修改数据库表。</p>    <h3>(1)用户基本信息表User</h3>    <table>     <thead>      <tr>       <th>UserID</th>       <th>avatar</th>       <th>name</th>       <th>...</th>      </tr>     </thead>     <tbody>      <tr>       <td>2</td>       <td>url1</td>       <td>name1</td>       <td>-</td>      </tr>      <tr>       <td>1</td>       <td>url2</td>       <td>name2</td>       <td>-</td>      </tr>      <tr>       <td>3</td>       <td>url3</td>       <td>name3</td>       <td>-</td>      </tr>     </tbody>    </table>    <p>用户基本信息表包含一些用户基本信息,主键是用户的 UserGuid ,是唯一的。</p>    <h3>(2)用户验证表User_auths</h3>    <table>     <thead>      <tr>       <th>primary</th>       <th>UserID</th>       <th>identity_type</th>       <th>identify</th>       <th>credential</th>       <th>isFirstParty</th>      </tr>     </thead>     <tbody>      <tr>       <td>1</td>       <td>2</td>       <td>email</td>       <td>12345@gmial.com</td>       <td>MD5MD5MD5</td>       <td>True</td>      </tr>      <tr>       <td>2</td>       <td>2</td>       <td>account</td>       <td>accountName</td>       <td>accountPwMD5</td>       <td>True</td>      </tr>      <tr>       <td>3</td>       <td>1</td>       <td>phone</td>       <td>12345678909</td>       <td>pwMD5</td>       <td>True</td>      </tr>      <tr>       <td>4</td>       <td>2</td>       <td>wechat</td>       <td>wechatID</td>       <td>access_token & refresh_token</td>       <td>False</td>      </tr>      <tr>       <td>5</td>       <td>3</td>       <td>weibo</td>       <td>weiboID</td>       <td>access_token & refresh_token</td>       <td>False</td>      </tr>     </tbody>    </table>    <p>用户验证表,包含用户的各种登录方式,对列名解释: <strong>primaryKey</strong> :主键,唯一。 <strong>UserGuid</strong> :用户唯一标识,在表中不唯一。 <strong>identity_type</strong> :登录方式。 <strong>identify</strong> :该方式的账户名或ID。 <strong>isFirstParty</strong> :是否是第一方登录。 <strong>credential</strong> :对于第一方是密码,对于第三方是 access_token 和 refresh_token 。</p>    <p>很多现在的APP第三方登录成功获得用户基本资料后便丢弃第三方的 access_token 和 refresh_token ,其实如果为了真正的安全,还是需要保存下来,每次自动登录后通过第三方 access_token 和 refresh_token 来更新用户信息以及验证用户是否是真实身份。</p>    <p>UserGuid 不唯一,可能有多个账号对应同一个 UserGuid (第三方绑定到电话账号等等)。使用 isFirstParty 来标注是否是第一方,对于有些应用email/phone/accout的密码是同一个,修改的话就需要同时修改,需要通过标志位来定位这些第一方账号。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/c95c4741d725</p>    <p> </p>