web开发常见的鉴权方式

结合网上找的资料整理了一下,以下是web开发中常见的鉴权方法:

 

预备:一些基本的知识

RBAC(Role-Based Access Control)基于角色的权限访问控制(参考下面①的连接)

l   用户-角色-权限 的授权模型 : 一个用户拥有若干角色,每一个角色拥有若干权限

l   用户:一个个独立的账号

l   角色:一些权限的集合,是权限的载体(例如:"管理员"、”会员“、"普通用户")

l   权限:权限通常是一组资源的集合。(例如:用户管理、保单管理、系统维护。角色和权限是多对多关系。)

l   用户量大的时候,单独为一个用户授予角色比较繁琐,需增加用户组,可以对用户组内所有的用户统一分配角色,这样一来,用户拥有的所有权限,就是用户个人拥有的权限与该用户所在用户组拥有的权限之和。

l   各种资源的整合,我们在设计权限的时候会遇到多种类型的资源,例如页面元素的显示隐藏、文件的访问、按钮的操作权限等等

ACL 访问控制列表,是前几年盛行的一种权限设计,它的核心在于用户直接和权限挂钩

RBAC的核心是用户只和角色关联,而角色代表对了权限,这样设计的优势在于使得对用户而言,只需角色即可以,而某角色可以拥有各种各样的权限并可继承。

ACL和RBAC相比缺点在于由于用户和权限直接挂钩,导致在授予时的复杂性,虽然可以利用组来简化这个复杂性,但仍然会导致系统不好理解,而且在取出判断用户是否有该权限时比较的困难,一定程度上影响了效率。

基于RBAC模型的权限验证框架与应用

Apache Shiro

Spring Security

SELinux

 

 

Cookie 有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)

l  位于客户端

l  cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器

l  由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。

l  传递过程:

n  浏览器向某个 URL 发送请求

n  对应的服务器收到该 HTTP 请求,生成要发给浏览器的 HTTP 响应

n  在响应头中加入 Set-Cookie 字段,值为要设置的的Cookie

n  浏览器收到来自服务器的 HTTP 响应

n  浏览器在响应头中发现了 Set-Cookie 字段,就会将该字段的值保存在内存或者是硬盘中。

n  当下一次向该服务器发送 HTTP 请求时,会将服务器设置的 Cookie 附加在 HTTP 请求的字段 Cookie 中。

n  服务器收到这个 HTTP 请求之后,发现请求头中有 Cookie 字段,就知道了已经处理过这个用户的请求了。

n  过期的 Cookie 会被删除

n  这里注意下:上面的步骤中cookie是通过服务端下发的,但是cookie是一种客户端技术,也可以客户端脚本通过读取返回的正文解析出结果,然后写入cookie同样能达到相同的效果

 

Session 在计算机中,尤其是在网络应用中,称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息。

l  位于服务端

l  session的创建目的初衷就是为了让服务端记住会话,简而言之就是让服务端能识别出来是哪个客户端

l  每次客户端发起请求的时候带上Session的标识,然后服务端就知道是谁来访问的了,这个标识可以通过上面的Cookie保存在客户端,这样两者就有了关系

 

Cookie和Session的关系:

session和cookie的目的相同,都是为了克服http协议无状态的缺陷,但完成的方法不同。

session可以通过cookie来完成,在客户端保存session id,而将用户的其他会话消息保存在服务端的session对象中。

cookie需要将所有信息都保存在客户端。因此cookie存在着一定的安全隐患,例如本地cookie中保存的用户名密码被破译,或cookie被其他网站收集(例如:1. appA主动设置域B cookie,让域B cookie获取;2. XSS,在appA上通过javascript获取document.cookie,并传递给自己的appB)。

 

 

XSS攻击和CSRF攻击:

XSS(Cross Site Scripting) 跨站脚本攻击,为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。具体可参考7

 

CSRF(Cross-site request forgery):跨站请求伪造。具体可以参考8

防御 CSRF 攻击主要有三种策略:

  1. 验证 HTTP Referer 字段;
  2. 在请求地址中添加 token 并验证;
  3. 在 HTTP 头中自定义属性并验证。

认证和授权

认证和授权是两个不同的概念,更是两个不同的阶段,绝大多数带有认证和授权的系统,在用户操作流程上都是先进行认证,之后根据认证的结果再进行授权操作。

认证是用来证明一个用户身份的操作流程,授权是用来给当前用户赋予权限的操作流程

 

 

一.  API Key + API Secret

实现步骤:

1.服务端和客户端约定好,API Key 以及 API Secret,服务端进行保存,下发给客户端

2.客户端发起请求的时候,客户端使用 API Key  +API Secret + 其他参数 (加密算法) 生成签名字符串 sign1,把 API Key 、其他参数 和 sign1 一起发送给服务器 (API Secret 不发送)

3.服务接收到了客户端发来的请求,将通过其中的API Key找到API Secret,然后按照和客户端约定好的加密方式再进行计算一次,生成一个 sign2

4.服务端比较Sign2和Sign1是否相同来判断是否可以让客户端进行访问。

 

关键点:

加密算法

 

1.设要参与计算签名的数据为集合 M,将集合 M 内非空参数值的参数按照参数名 ASCII 码从小到大排序(字典序),使用 URL 键值对的格式(即key1=value1&key2=value2…)拼接成字符串 signTemp。

    特别注意以下重要规则:

    参数名 ASCII 码从小到大排序(字典序)

    如果参数的值为空不参与签名

    参数名区分大小写

    验证调用返回或主动通知签名时,传送的 sign 参数不参与签名,将生成的签名与该 sign 值作校验

    接口可能增加字段,验证签名时必须支持增加的扩展字段

2.对 signTemp 进行 MD5 运算,再将得到的字符串所有字符转换为大写,得到 sign 值 signValue。

 

访问过期时间

如果不限制签名的使用时间,则生成的签名永远有效,如果别人拿到生成的链接后就能一直使用了,这是比较危险的,所以限制签名时长很有必要,有 2 种方式可以限制签名的有效时间:

1.客户端加签名生成时间

  1. 生成签名时的时间加入计算签名
  2. 服务器收到请求后:签名有效 并且 服务器当前时间 <= 参数中的时间 + 过期时长 则放行访问,否则拒绝访问

n  如果客户端的时间或则服务器端的时间不准,就有可能签名会无效,例如客户端的时间是 2016 年的,而服务器端的是 2017 年的,签名的过期时长为 5 分钟,则计算的签名就会是无效的。反过来签名的有效时间就变长了。

n  还有个办法,使用服务器时间进行签名:在计算签名前先向服务器请求一下服务器的时间,然后用此时间进行签名即可。服务器接收到请求时判断一下这个时间一定要比服务器当前时间早才行。

 

2.服务器记录签名时间

  1. 客户端计算签名(不使用时间)
  2. 服务器收到请求后:

n  如果签名有效且是第一次使用,则保存签名和时间 T1 到数据库或则 Redis 等,放行访问

n  如果签名不是第一次访问(能从数据库或则 Redis 中查找到),并且 服务器当前时间 <= T1 + 过期时长 则放行访问,否则拒绝访问

优点:此种方式是最安全的。

缺点:服务器端需要一直保存签名,否则删除后再次使用时就变成第一次使用了,不过 Redis 之类的存储几亿条记录访问还是很快的。

 

二. Cookie-Session认证

由于http协议是无状态的,所以基于http协议进行的认证,都需要依赖客户端上传的某种标识

基于 Cookie-Session 身份验证机制的过程

  1. 用户输入登录信息
  2. 服务端验证登录信息是否正确,如果正确就在服务器端为这个用户创建一个 Session,并把 Session 存入数据库
  3. 服务器端会向客户端返回带有 sessionID 的 Cookie
  4. 客户端接收到服务器端发来的请求之后,看见响应头中的 Set-Cookie 字段,将 Cookie 保存起来
  5. 接下来的请求中都会带上这个 Cookie,服务器将 sessionID 和 数据库中的相匹配,如果有效则处理该请求
  6. 如果用户登出,Session 会在客户端和服务器端都被销毁

 

Cookie-Session认证的问题

  1. 扩展性不好,当拥有多台服务器的情况下,如何共享 Session 会成为一个问题,也就是说,用户第一个访问的时候是服务器 A,而第二个请求被转发给了服务器 B,那服务器 B 无法得知其状态。(举例来说,A 网站和 B 网站是同一家公司的关联服务。用户只要在其中一个网站登录,再访问另一个网站自动登录)
  2. 安全性不好,攻击者可以利用本地 Cookie 进行欺骗和 CSRF 攻击。
  3. Session 保存在服务器端,如果短时间内有大量用户,会影响服务器性能。
  4. 跨域问题,Cookie 属于同源策略限制的内容之一。

 

 

三. OAuth

OAuth分为OAuth1和OAuth2,目前OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。

应用场景是第三方应用授权登录:在APP或者网页接入一些第三方应用时,时长会需要用户登录另一个合作平台,比如QQ,微博,微信的授权登录。

OAuth2.0本质上就是将这里的客户应用向授权服务器请求令牌与授权服务器颁发令牌的过程标准化了,根据这样一套标准与解决方案,我们就可以安全的让第三方应用访问存储在我们服务器上的用户数据了。

OAuth2.0是用于REST/APIs的代理授权框架(delegated authorization framework),它基于令牌Token的授权,在无需暴露用户密码的情况下,使应用能获取对用户数据的有限访问权限。

OAuth2.0客户端获取授权的四种模式 (具体可以参考6)

l  授权码模式(authorization code)

l  简化模式(implicit)

l  密码模式(resource owner password credentials)

l  客户端模式(client credentials)

 

结合自己的项目文档:OAuth2.0总结.note

 

四. JWT

JWTs(JSON Web Tokens)JWT是一种无状态的鉴权机制。将用户登录后的一些信息(比如用户Id)和过期时间等信息存储在一个加密过的字符串中,当服务器收到请求的时候,进行解密并直接使用信息。可以理解JWT是对于Token的一种规范。

 

Token 相对于 Cookie + Session 的优点,主要有下面两个:

CSRF 攻击

这个原理不多做介绍,构成这个攻击的原因,就在于 Cookie + Session 的鉴权方式中,鉴权数据(cookie 中的 session_id)是由浏览器自动携带发送到服务端的,借助这个特性,攻击者就可以通过让用户误点攻击链接,达到攻击效果。而 token 是通过客户端本身逻辑作为动态参数加到请求中的,token 也不会轻易泄露出去,因此 token 在 CSRF 防御方面存在天然优势。

适合移动应用

移动端上不支持 cookie,而 token 只要客户端能够进行存储就能够使用,因此 token 在移动端上也具有优势。

 

JWT 组成:

一个 JWT token 是一个字符串,它由三部分组成,头部(header)、载荷(Payload:通常也被称作实际数据或者数据体。)与签名(Signature),中间用 . 分隔,例如:xxxxx.yyyyy.zzzzz

①头部通常由两部分组成:令牌的类型(即 JWT)和正在使用的签名算法(如 HMAC SHA256 或 RSA.)。

{   "alg": "HS256",   "typ": "JWT" }

然后用 Base64Url 编码得到头部,即 xxxxx。

 

②载荷中放置了 token 的一些基本信息,以帮助接受它的服务器来理解这个 token。同时还可以包含一些自定义的信息,用户信息交换。

载荷的属性也分三类:

预定义的载荷(Registered)

{   "sub": "1",                                  --主题(subject)   "iss": "http://localhost:8000/auth/login",   --签发人(issuer)   "iat": 1451888119,                           --签发时间(Issued At)   "exp": 1454516119,                           --过期时间(expiration time)   "nbf": 1451888119,                           --生效时间,在此之前是无效的(Not Before)   "jti": "37c107e4609ddbcc9c096ea5ee76c667",   --编号(JWT ID)   "aud": "dev"                                 --受众(audience) }

公有的载荷(public)

在使用 JWT 时可以额外定义的载荷。为了避免冲突,应该使用 IANA JSON Web Token Registry 中定义好的,或者给额外载荷加上类似命名空间的唯一标识。

 

私有的载荷(private)

在信息交互的双方之间约定好的,既不是预定义载荷也不是公有载荷的一类载荷。这一类载荷可能会发生冲突,所以应该谨慎使用。

将上面的 json 进行 Base64Url 编码得到载荷,即 yyyyy

 

③ 签名

签名时需要用到前面编码过的两个字符串,如果以 HMACSHA256 加密,就如下:

HMACSHA256(     base64UrlEncode(header) + "." +     base64UrlEncode(payload),     secret )

加密后再进行 base64url 编码最后得到的字符串就是 token 的第三部分 zzzzz。

组合便可以得到 token:xxxxx.yyyyy.zzzzz。

备注:

HMAC(Hash-based Message Authentication Code) 算法是不可逆算法,类似 MD5 和 hash ,但多一个密钥,密钥(即上面的 secret)由服务端持有,客户端把 token 发给服务端后,服务端可以把其中的头部和载荷再加上事先共享的 secret 再进行一次 HMAC 加密,得到的结果和 token 的第三段进行对比,如果一样则表明数据没有被篡改。

 

JWT 的使用有两种方式:

l  加到 url 中:?token=你的token

l  加到 header 中,建议用这种,因为在 https 情况下更安全:Authorization:Bearer 你的token

 

 

 

参考:

1权限管理控制与RBAC模型   https://blog.csdn.net/MyArray/article/details/81129777

2WebAPI常见的鉴权方法,及其适用范围  https://www.cnblogs.com/mrbug/p/8027918.html

3cookie-session机制与JWT机制对比 https://www.jianshu.com/p/cbe253281d26

4浅谈 Cookie-Session 、Jwt 两种身份认证机制  https://juejin.im/post/5d567e09e51d453c12504dfc

5OAuth2介绍与使用  https://baijiahao.baidu.com/s?id=1620083471706505859&wfr=spider&for=pc     

6理解OAuth 2.0http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

7CSRF攻击和XSS攻击 https://www.jianshu.com/p/4a0e151876ad

8CSRF攻击的应对之道 https://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/

9JWT 超详细分析 https://learnku.com/articles/17883#58ac43

 

posted @ 2019-10-23 13:50  奋斗的大橙子  阅读(4096)  评论(0编辑  收藏  举报