常见的服务器鉴权方式

Basic Authentication

basic是最简单的认证机制,客户端向服务器端请求数据时,如果未被认证,服务器会向客户端发送验证请求.

  HTTP/1.0 401 Unauthorised 
  Server: SokEvo/1.0 
  WWW-Authenticate: Basic realm=”google.com” 
  Content-Type: text/html 
  Content-Length: xxx

当客户端收到401返回值时,将自动弹出一个登陆窗口,要求用户输入用户名和密码.

用户输入用户名和密码后,经过浏览器base64编码后,再次发送请求

  Get /index.html HTTP/1.0 
  Host:www.google.com 
  Authorization: Basic d2FuZzp3YW5n

服务器收到请求后将用户信息取出解密,对比验证.

session 是缓存在服务端的,cookie 则是缓存在客户端,他们都由服务端生成,为了弥补 Http 协议无状态的缺陷。

  • 服务器在接受客户端首次访问时在服务器端创建seesion,然后保存seesion(我们可以将seesion保存在内存中,也可以保存在redis中,推荐使用后者),然后给这个session生成一个唯一的标识字符串,然后在 响应头中种下这个唯一标识字符串。
  • 签名。这一步通过秘钥对sid进行签名处理,避免客户端修改sid。(非必需步骤)
  • 浏览器中收到请求响应的时候会解析响应头,然后将sid保存在本地cookie中,浏览器在下次http请求的请求头中会带上该域名下的cookie信息。
  • 服务器在接受客户端请求时会去解析请求头cookie中的sid,然后根据这个sid去找服务器端保存的该客户端的session,然后判断该请求是否合法。

img

Token

一般 token 由用户信息、时间戳和由 hash 算法加密的签名构成。

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者Local Storage
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 Token(request头部添加Authorization),如果验证成功,就向客户端返回请求的数据 ,如果不成功返回401错误码,鉴权失败。

token认证可以支持多种客户端,而不仅是浏览器,且不受同源策略的影响.

基于token的解决方案有很多,常用的是JWT.JWT 的原理是,服务器认证以后,生成一个 JSON 对象,这个 JSON 对象肯定不能裸传给用户,那谁都可以篡改这个对象发送请求。因此这个 JSON 对象会被服务器端签名加密后返回给用户,返回的内容就是一张令牌,以后用户每次访问服务器端就带着这张令牌

JWT由三部分组成: Header.Payload.Signature. Header存放元数据、Payload存放实际传递的数据,因为是Base64编码的,所以不能存放秘密信息.Signature是前两个部分的签名,防止数据篡改.

特点:

  • JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
  • JWT 不加密的情况下,不能将秘密数据写入 JWT。
  • JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
  • JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
  • JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
  • 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

OAuth2.0

OAuth 2.0是目前最流行的授权机制,用来授权第三方应用,获取用户数据.

数据所有者告诉系统,同意第三方应用进入系统,获取这些数据.系统从而产生一个短期的进入令牌用来代替密码,供第三方应用使用.

令牌是短期有效的,可随时被数据所有者撤销,并且有权限范围. 以上特点保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危机系统安全.

四种授权方式

OAuth引入了一个授权层,用来分离两种不同的角色: 客户端和资源所有者.资源所有者同意后,资源服务器向客户端颁发令牌,客户端通过令牌去请求数据.资源服务器会与对应的授权服务器通信、校验令牌来确定客户端是否可以访问资源.

OAuth的核心就是向第三方应用办法令牌, OAuth规定了四种获得令牌的流程:

  • 授权码 (authorization-code): 第三方应用先申请一个授权码, 然后再用该码获取令牌

    这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。

    img

    A网站想要得到B网站的令牌,就会提供B网站的带参数链接,用户跳转到B网站,B网站会要求用户登陆,然后询问"A 网站要求获得 xx 权限,你是否同意?.

    Response_type表示要求返回授权码

    Client_id 让B知道是谁在请求(一个应用要求 OAuth 授权,必须先到对方网站登记,让对方知道是谁在请求。然后该网站会返回client_id和client_secret)

    Scop 表示授权范围

    https://b.com/oauth/authorize?
      response_type=code&
      client_id=CLIENT_ID&
      redirect_uri=CALLBACK_URL&
      scope=read
    

    同意后,B网站跳回redirect_uri参数指定的网址,并附带授权码

    https://a.com/callback?code=AUTHORIZATION_CODE
    

    A拿到授权码后,就可以在后端向B网站请求令牌

    https://b.com/oauth/token?
     client_id=CLIENT_ID&
     client_secret=CLIENT_SECRET&
     grant_type=authorization_code&
     code=AUTHORIZATION_CODE&
     redirect_uri=CALLBACK_URL
    

    B网站收到请求后,向redirect_uri发送一段json数据, A网站在后端就拿到了.

    {    
      "access_token":"ACCESS_TOKEN",
      "token_type":"bearer",
      "expires_in":2592000,
      "refresh_token":"REFRESH_TOKEN",
      "scope":"read",
      "uid":100101,
      "info":{...}
    }
    
  • 隐藏式 (implicit)

    有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端。RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)"隐藏式"(implicit)。

    img

    https://b.com/oauth/authorize?
      response_type=token&
      client_id=CLIENT_ID&
      redirect_uri=CALLBACK_URL&
      scope=read
    

    response_type为token表示直接返回令牌.

    https://a.com/callback#token=ACCESS_TOKEN
    
    Note:
    #代表网页中的一个位置,当浏览器读取这个URL时,会自动将位置滚动到可视区域.
    为网页位置指定标识符,有两个方法。一是使用锚点,比如<a name="print"></a>,二是使用id属性,比如<div id="print" >
    #是用来指导浏览器动作的,对服务器端完全无用。所以,HTTP请求中不包括#。
    

    注意,令牌的位置是 URL 锚点(fragment),而不是查询字符串(querystring),这是因为 OAuth 2.0 允许跳转网址是 HTTP 协议,因此存在"中间人攻击"的风险,而浏览器跳转时,锚点不会发到服务器,就减少了泄漏令牌的风险。>

    这种方式把令牌直接传给前端,是很不安全的。因此,只能用于一些安全要求不高的场景,并且令牌的有效期必须非常短,通常就是会话期间(session)有效,浏览器关掉,令牌就失效了。

  • 密码式 (password)

    如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码式"(password)。

    A网站直接要求用户提供B网站的用户名和密码,拿到以后,A就直接向B请求令牌,B网站验证身份后直接在http回应中给出令牌,不需要跳转.

    https://oauth.b.com/token?
      grant_type=password&
      username=USERNAME&
      password=PASSWORD&
      client_id=CLIENT_ID
    
  • 客户端凭证 (client credentials)

    适用于没有前端的命令行应用,即在命令行下请求令牌。

    https://oauth.b.com/token?
      grant_type=client_credentials&
      client_id=CLIENT_ID&
      client_secret=CLIENT_SECRET
    

使用令牌

A 网站拿到令牌以后,就可以向 B 网站的 API 请求数据了。

此时,每个发到 API 的请求,都必须带有令牌。具体做法是在请求的头信息,加上一个Authorization字段,令牌就放在这个字段里面。

curl -H "Authorization: Bearer ACCESS_TOKEN" \
"https://api.b.com"

更新令牌

OAuth 2.0 允许用户自动更新令牌.B 网站颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌(refresh token 字段)。令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。

https://b.com/oauth/token?
  grant_type=refresh_token&
  client_id=CLIENT_ID&
  client_secret=CLIENT_SECRET&
  refresh_token=REFRESH_TOKEN
posted @ 2021-11-11 13:57  尹瑞星  阅读(1667)  评论(3编辑  收藏  举报