基于 session 和基于 token(jwt [json web token]) 的用户认证方式
要实现用户的登录,在我们这里的前后端分离的开发中和之前基于模板的browser开发是有一定区别的,区别主要在于服务端存放数据与否。
多数网站用户认证都是基于 session 的,即在服务端存放用户相关的 session 数据,而发给客户端 sesssion_id (其实也是token的一种)存放到 cookie 中,这样用客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户认证。
这种认证方式,优点是可以更好的在服务端对会话进行控制,安全性比较高(session_id 随机)。但是服务端需要存储 session 数据(如内存或数据库).缺点是 ①增加维护成本和减弱可扩展性(多台服务器),②存在基于cookie的 CSRF攻击,③原生 app 等前端没有浏览器 的cookie 功能,使用这种服务接口会相对麻烦。
基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用户验证后,服务端生成一个 token(hash 或 encrypt)发给客户端,客户端可以放到 cookie 或 localStorage 中,每次请求时在 Header 中带上 token ,服务端收到 token 通过验证后即可确认用户身份。这种方式相对 于cookie 的认证方式就简单一些,服务端不用存储认证数据.
相应地具有以下优点:①易维护、扩展性强② token 储存在 localStorage 中可避免 CSRF ③ web 和 app 应用这用接口都比较简单。不过这种方式在加密或解密的时候会有一些性能开销(好像也不是很大),有些对称加密存在安全隐患(aes cbc 字节翻转攻击)。
一、基于session
1. 登录 * 用户输入账号密码,从客户端传到服务端 * 服务端效验账号密码是否匹配 * 匹配时,将用户信息存放到session中。session.setAttribute(user); 当执行其他接口请求时,在服务端判断是否已登录,即查看session中是否存在用户数据。session.getAttribute(user) 2. 忘记密码,回答问题答案,注册用户时,设置的,此时就是回答问题,找回密码 * 输入用户名,检验是否存在该用户。 * 找到问题,输入问题答案 * 回答正确,随机生成一个token,存入缓存中,并传给客户端。 * 客户端修改密码后,将新密码和token传到服务端。 token的作用:如果答案正确,那么就针对这个用户,把一个token存入缓存中。当该用户想改密码时,将传入的token跟缓存中的token进行比较,如果一致,则认为是该用户在操作。token要过期时间的原因是,这次修改需要保证在一段时间内有效,过期就无效了。 这在互联网上修改密码是一个很常用的做法,例如,忘记密码修改邮件里面给的链接,都会有一个提示,告诉你,这个修改密码的链接在10个小时之内有效,过期请重新获取该链接~
基于session认证所显露的问题
Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
二、基于token(jwt)鉴权机制
基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。 流程上是这样的: *用户使用用户名密码来请求服务器 * 服务器进行验证用户的信息 * 服务器通过验证发送给用户一个token * 客户端存储token,并在每次请求时附送上这个token值 * 服务端验证token值,并返回数据 * 这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, * 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了 Access-Control-Allow-Origin:*。* token的优点 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输。 无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息。 更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可。 去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可。 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。 CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。 性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多。 不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理。 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)。 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。 它不需要在服务端保存会话信息, 所以它易于应用的扩展。