web认证机制

202次阅读

共计 2719 个字符,预计需要花费 7 分钟才能阅读完成。

引言
以前对认证这方面的认识一直不太深刻,不清楚为什么需要 token 这种认证,为什么不简单使用 session 存储用户登录信息等。最近读了几篇大牛的博客才对认证机制方面有了进一步了解。
Basic Auth
这种认证直接顺应 HTTP 协议的无状态性,每次执行业务的时候,都暴力地附带 username 与 password 参数,并将其发送给服务器进行验证。尽管在服务器端可以优雅地使用 AOP 技术(如拦截器或动态代理)对所有 controller 进行前置的登录验证。但如果每次验证都要查数据库的话,创建连接与查询操作势必会增大开销。如果服务器端不做任何记忆(有状态性)处理的话,那么这种方式就已经没有其他办法可以优化了。
Cookie/Session Auth
上面已经点到,只要服务器端稍加一些记忆处理(记录哪些用户登录过)即可大大优化这个过程:只需要在用户第一次登录系统的时候,将对应的 username 放入一个类似与 Set<String> 的数据结构中。只要登录一次(保证不退出),那么当用户第二次访问 controller 的时候,只需要查询 Set<String> 中是否有该 username 即可。但这种方式仍有不足,即每次还是必须要求客户端传 username 过来,否则服务器端不知道是谁就无法判断了。要优雅地解决上述问题,就要得益于后来 HTTP 协议中出现的 Cookie 与 Session 技术了。当浏览器利用 HTTP 协议访问服务器的时候,服务器会为其自动创建一个其独有的 session 对象。session 在基本的数据结构上类似于键值对 Map,但不同的是它还提供了若干操作方法,且可以设置时效。既然一个浏览器唯一对应了一个 session,那就好办了,用户第一次登录验证成功后,就可把用户名写入 session 中表征当前处于该浏览器上的用户已经登录,以后访问 controller 只用查 session 中是否有 username 键即可,若有放行,若没有则阻止。如下图所示:
Token Auth
目前被众多公司广泛采用的是 token 认证,它的认证过程都是围绕着一个名为 token 字符串展开的,其认证流程如下:
第一次登录用户携带 username 与 password 请求第一次登录(为保证安全性通常采用 HTTPS 协议传输);服务器接收到后,查询数据库看用户是否存在且密码(MD5 加密后)是否匹配,若匹配,则在用户表中查询该用户信息(角色、权限、状态等);从配置文件中读取签名的私钥,并整合上一步得到的用户信息生成 token(可采用第三方库 JWT lib);将 token 写入 cookie 并重定向到前端。登录后访问业务用户携带从 cookie(若为移动终端,可以是数据库或文件系统)取出的 token 访问需登录及特定权限的业务;请求首先被认证拦截器拦截,并获取到传来的 token 值;根据配置文件中的签名私钥,结合 JWT lib 进行解密与解码;验证签名是否正确(若不正确 JWT 会抛出异常)、token 是否过期与接收方是否是自己(由自己判断)等。若通过则证明用户已登录,进入权限验证阶段;通过权限验证框架(shiro、spring security 等)验证用户是否具有访问该业务的权限,若有则返回相应数据。
认证方式比较
1.cookie 支持问题 session 和 cookie 其实是紧密相联的。浏览器与服务器首次建立连接的时候,服务器会自动生成一个会话号 sid,并写入响应报文的首部字段 <Set-cookie> 中,返回给客户端让其存入 cookie。之后每次的 HTTP 请求报文中均会在首部写入 cookie 中的 sid,服务器接收到后根据 sid 取出对应 session,再进一步根据 username 键是否存在判断登录与否。可以看出 cookie/session 认证要求客户端必须支持 cookie 技术,但很显然,客户端并不是只能为浏览器,还可以是 PC 桌面、移动终端等其它平台,对于这些平台,我们无法保证他们都能支持 cookie 技术。而 token 认证只认 token 这个字符串值,至于前端是浏览器采用 cookie 存的 token 还是 Android 终端用数据库存的 token 都无所谓,只要拿到 token 值即可进行验证。2.session 共享问题 session 是无法在多台服务器之间共享的,特别在分布式部署环境(即多台部署了同一系统的 Web 服务器集群)下将带来很多同步、一致性问题。比如下面这个场景:用户请求登录,HTTP 请求被转发到了服务器 A,在 A 上完成认证后将登录状态记录到了 session;用户后续请求其他需登录的业务,HTTP 请求被转发到了非 A 的服务器上,这时由于这些服务器上的 session 并非 A 上的 session,所以其上就没有登录状态记录,所以业务操作将被拒绝!很显然这时采用 cookie/session 认证就很棘手了,需要自己去维护同步、状态一致等问题。而 token 根本不会依赖 session,所有服务器都是一致地采用私钥 + 解密算法分析签名的正确性。3 时间 / 空间开销问题如果 session 不仅是要存储用户名,还要存储时效时间、登录时间等各种状态信息(特别是大型系统),那么一旦登录的用户数激增,服务器的内存消耗也将急剧增大。而 token 认证是完全将状态存入了 token 值中,再利用加解密算法将状态取出,用时间复杂度换取了空间复杂度,内存开销大大减小,时间效率有降低。4 第三方授权问题采用传统认证方式,若要访问业务,一定要先登录。假如这时一个第三方应用希望获取该用户在本系统的一些资源(如头像、昵称、签名等),则一定要先接受登录拦截器认证才可放行,这时如果第三方应用也通过用户名 + 密码登录的形式来获取信息的话,势必会暴露用户在本系统的信息,很不安全!而一种更巧妙的做法是,先记录第三方应用的 AppID 与其 url 地址,然后跳转到本系统登录页面进行登录,认证成功后,经本系统的认证服务器生成 access_token,携带该参数并重定向到 url 地址所在页面。此后第三方应用即可凭借该 access_token 的权限范围,访问所需的本系统的资源。可以看出,无论是本系统自己凭借 token 访问自己的资源,还是第三方应用凭借 access_token 访问本系统资源,依靠的都是 token 这个凭据,走的都是统一的一套流程,而传统方式,需要额外写一套,可维护性很不好。关于第三方认证文章可以参考理解 Ouath2.0。
结语
虽然 token 认证优势非常明显,但仍然需要考虑如下问题:如何抵御跨站脚本攻击(XSS)、如何防范重放攻击(Replay Attacks)、如何防范 MITM(Man-In-The-Middle)攻击等。对此本文就不再做详细叙述了。

正文完
 0