浅析权限认证中的有状态和无状态
网络安全 网络
原文:浅析权限认证中的有状态和无状态前言我们在设计构建一个系统的时候,权限管理和用户认证是最基本功能,其中关于用户认证这块是一个比较常见的模块。在已有的方案中,我们最常见的就是保存到tomcat中的session对象中。随着微服务的兴起,一种新的认证方法又火了起来,那就是JWT,下面我就浅析下自己对两种认证方式的认识,一些经验和大家探讨。浅谈认证我对认证的理解就是,当一个设备(客户端)向一个设备…
原文:浅析权限认证中的有状态和无状态

前言

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

浅谈认证

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

有状态认证

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

/upload/image/201908/1221720-20190408194618868-1994019084.png>

请求认证过程(以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 令牌返回给客户端,之后每次请求服务端,客户端都需要携带此令牌,服务端接受到令牌之后进行校验,校验通过后,提取令牌中的信息用来区别用户,大概的示例图如下:
/upload/image/201908/1221720-20190408194552431-894144887.png>

请求认证过程:

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面试的早点到来,给自己加油。