浅析权限认证中的有状态和无状态

前言

我们在设计构建一个系统的时候,权限管理和用户认证是最基本功能,其中关于用户认证这块是一个比较常见的模块。在已有的方案中,我们最常见的就是保存到 tomcat 中的 session 对象中。随着微服务的兴起,一种新的认证方法又火了起来,那就是JWT,下面我就浅析下自己对两种认证方式的认识,一些经验和大家探讨。

浅谈认证

我对认证的理解就是,当一个设备(客户端)向一个设备(服务端)发送请求的时候,服务端如何判断这个客户端是谁。传统意义有两种认证方式:有状态认证、无状态认证。有状态和无状态最大的区别就是服务端会不会保存客户端的信息。

有状态认证

有状态的认证,以 cookie - session 模型为例,当客户端第一次请求服务端时候,服务端会返回客户端一个唯一标识(默认在cookie中),并保存对应的客户端信息。客户端接受到唯一标识之后,将标识位保存到本地cookie中,以后的每次请求都携带此 cookie ,服务端根据此 cookie 标识可以判断请求的用户是谁,然后查到对应用户的信息,大概示意图如下:

请求认证过程(以tomcat为例):

1、客户端向服务端发起请求;

2、第一次客户端发起请求,服务端创建一个 key 为JSESSIONID的值,并写入到客户端的cookie中,同时在服务端的Session Manager中创建一个对象,保存这个 JSESSIONID 对应的信息;

3、以后客户端每次请求,都会根据cookie进行区别,我们可以通过 session.setAttrbutie,session.getAttrbuite 等方法,拓展用户信息,根据用户信息做一些业务判断等;

4、Session Manger 中维护有一个定时器,当 JSESSIONID 对应的信息长时间没有访问(默认30分钟),或者显性调用 session.invalidate 方法,那么这个对应的信息将会被删除。

那么针对有状态的认证,我们分析下他的利弊:

优势

因为客户端的信息都保存在服务端的 Session Manager 中,如果要将客户端的认证信息取消,只需要将对应的session 信息删除即可,及时响应,方便快捷。

劣势

1、因为服务端保存着客户端的信息,当用户量特别多时候,服务端需要特别的内存资源;

2、如果失效时间特别长的情况下,大量资源被占用无法释放,如果释放,那么相当于用户的注销登录;

3、客户端的信息在服务端中维护,如果服务端为集群的场景下,那么客户端信息不共享,必须使用分布式 session 或者其他方案;

4、cookie有同源策略和跨域限制,部分业务场景下cookie并不能传递;

5、部分设备本身不支持cookie或者禁用cookie,还有的手机浏览器也不支持cookie。

应用实战

1、如果公司以集群式部署多台服务,那么可以采用的策略有:配置负载均衡的路由策略为hash一致算法(不推荐),如果某个机器停机,那么会重新分配到新机器,又需要重新登录;session复制方式(不推荐),复杂度过高;分布式 session 方案(强力推荐),目前市场上有 spring-session 的依赖,可以将 session 保存的容器从应用内部抽取到 redis 或者 数据库中供多个应用使用,实现集中管理,为了保证设备的兼容性,spring-session提供了将认证方式从cookie修改为header,web网站可以保存到 SessionStorage,移动端可以保存到本地缓存中。

2、如果一个公司有多个产品需要共享认证信息,此时需要使用 SSO Server。

无状态

无状态的认证,客户端在提交身份信息,服务端验证身份后,根据一定的算法生成一个 token 令牌返回给客户端,之后每次请求服务端,客户端都需要携带此令牌,服务端接受到令牌之后进行校验,校验通过后,提取令牌中的信息用来区别用户,大概的示例图如下:

请求认证过程:

1、执行登录操作,用户端发送账号密码等信息;

2、服务端校验账号密码是否正确,如果正确,根据对应的用户信息和服务端秘钥生成 JWT 令牌,然后通过response.setHeader 返回给客户端(此处假设生成了一个名为 x-auth-token 的令牌);

3、客户端在返回成功之后,将Header中的x-auth-token 保存到本地的LocalStorage中;

4、客户端在以后每次请求服务端时候,都在header中携带x-auth-token令牌的值;

5、服务端每接受到请求之后,判断hader中是否包含x-auth-token,token 是否有效,然后通过 BASE 64 算法 decode,根据解密后的参数,判断当前 token 是否在有效期,所访问的接口是否有权限等操作。

优势

1、因为服务端不保留客户端的任何信息,每次只需要通过特定的算法进行校验,节省了大量存储空间;

2、方便水平扩容,不需要 SSO Server,只要保证新的应用采用同样的验证算法,就可以验证通过并获得对应信息。

劣势

当客户端的token被盗用,或者需要手动封禁某个用户的时候,没办法对此token进行操作,必须等待token失效(如果在服务端维护token和用户的关系,技术可以实现,但是违背无状态的设计理念)。

应用实战

1、生成的 token 中携带用户常用信息,但是不携带用户的敏感信息,比如密码手机号等等,因为这些信息通过BASE 64 可以解密出来的;

2、要处理服务端主动禁用某个 token ,可以采用黑名单措施,每次请求前判断当前token是否已经被禁用;

3、token 中的信息除了基本信息外,还应该携带比如签发时间、有效时间、刷新token等字段,用来处理token的续约问题。

总结

在实际的项目中,我们该如何选择认证模式,是有状态的认证还是无状态的认证呢?以前看文章时候,在掘金中看到有人把 JWT 批评的一无是处,就和垃圾一样。我个人觉得吧技术没有谁好谁坏,谁旧谁新,也没有谁该被时代淘汰,存在及合理,我们需要充分理解需求,合理使用技术,只有最合适的技术才是最好的选择。 下面是我关于技术选型时候的一些个人观点,希望大家批评指正和交流。

在我的经验中,目前比较流行的就是互联网APP中大部分采用 JWT 的认证方式,一些企业内部管理系统则大部分采用 cookie-session 的机制,大量的商业案例下,我个人分析推测的原因可能如下:

1、在互联网APP产品中,尤其以 to C 模式,用户量极大,为了用户体验,一般会将登录信息保留特别长时间,某些APP 只要你不卸载,那么不管几个月之后登录,账户还是处于登录状态。在这种情况下,假如采用 cookie-session 机制,那么你的用户信息保存很多个月,用户量特别大的情况下,会造成大量资源占用和浪费,这种场景采用 JWT 就是相对比较好的方案。

2、企业内部管理系统有以下特点:用户量较少(最多最多不超过10W人),信息安全要求高(及时踢出客户端登录状态,个人浏览器关闭账号退出登录),在这样的场景下占用的内存不会太多,所以基于 cookie-session 这种机制,是比较好的方案,如果企业内部还有其他应用需要集成时候,需要使用 SSO Server 实现。

代码案例

最近在找工作,如果闲下来有时间的时候会写两个项目出来,敬请期待:

1、 spring security + JWT 的验证案例

2、shiro + spring-session (header替换cookie)的验证方案

ps:蚂蚁5面技术过了,期待hr面试的早点到来,给自己加油。

posted @ 2019-04-08 19:49  史亚健  阅读(3251)  评论(0编辑  收藏  举报