几种常见的认证机制

BASE64

Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符。三个字节有24个比特,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示。JDK 中提供了非常方便的 BASE64Encoder 和 BASE64Decoder,用它们可以非常方便的完成基于 BASE64 的编码和解码

HTTP Basic Auth

每次请求API都提供用户的username和password,是最简单的认证方式

基本认证很少在可公开访问的互联网网站上使用,有时候会在小的私有系统中使用(如路由器网页管理接口)

但是会把用户名密码暴露给第三方客户端

/** * 服务端代码 */
public void doGet(HttpServletRequest request, HttpServletResponse response) {

    String auth = request.getHeader("Authorization");
    System.out.println("Authorization : " + auth);

    if (auth == null) {
        // 未带auth :通知浏览器 需要带auth
        response.setStatus(401);
        response.setDateHeader("Expires", 0);
        response.setHeader("WWW-authenticate", "Basic Realm=\"test\"");
        System.out.println("set WWW-authenticate\n");
    } else {
        // 解码 浏览器加密的 用户输入的 username、password
        auth = auth.substring(6, auth.length());
        String decodedAuth = getFromBASE64(auth);
        System.out.println("username and password : " + decodedAuth);
        // 在这里处理 username和password的验证逻辑
        // userService.loginAuth(username, password);
        System.out.println("Go\n");
    }
}

/** * 解码 BASE64 */
private String getFromBASE64(String s) {
    
    if (s == null) {
        return null;
    }
    BASE64Decoder decoder = new BASE64Decoder();
    try {
        byte[] b = decoder.decodeBuffer(s);
        return new String(b);
    } catch (Exception e) {
        return null;
    }
}

访问形式

1、使用浏览器请求

在使用浏览器访问设置了 HTTP Basic Auth 的服务器时,会弹出对话框,输入用户名和密码即可。

[外链图片转存失败(img-dRDd39K2-1563614916894)(C:\Users\ADMINI~1\AppData\Local\Temp\1561378186450.png)]

未认证:

[外链图片转存失败(img-2f1cMwnJ-1563614916895)(C:\Users\ADMINI~1\AppData\Local\Temp\1561376628253.png)]

认证后:

[外链图片转存失败(img-n4KQItTo-1563614916898)(C:\Users\ADMINI~1\AppData\Local\Temp\1561376709343.png)]

2、使用 HTTP Client 请求

http://user:passwd@httpbin.org/basic-auth/user/passwd

OAuth

OAuth 的核心就是向第三方应用颁发令牌

令牌(token)与 密码(password)的作用是一样的,都可以进入系统,但是有三点差异:

令牌是短期的

到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。

令牌可以被数据所有者撤销

令牌被撤销后,会立即失效

令牌有权限范围(scope)

比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。

上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。

这就是 OAuth 2.0 的优点。

但是令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。

这也是为什么令牌的有效期,一般都设置得很短的原因。

OAuth 2.0 规定了四种获得令牌的流程。你可以选择最适合自己的那一种,向第三方应用颁发令牌。

下面就是这四种授权方式:

  • 授权码(authorization-code)
  • 隐藏式(implicit)
  • 密码式(password):
  • 客户端凭证(client credentials)

授权码

第三方应用先申请一个授权码,然后再用该码获取令牌

这种方式是最常用的流程,安全性也最高,它 适用于 那些有后端的 Web 应用

授权码通过前端传送,令牌则是储存在后端,而且 所有与资源服务器的通信都在后端完成

这样的前后端分离,可以避免令牌泄漏

  1. A 网站 提供一个链接,用户点击后就会跳转到 B 网站,授权用户数据给 A 网站使用

​ 跳转到的B网站链接如下:

https://b.com/oauth/authorize?
    // 要求返回授权码
    response_type = code
    // 让B知道是谁在请求
  & client_id = CLIENT_ID
    // B接受或者拒绝请求后的跳转网址
  & redirect_uri = CALLBACK_URL 
    // 授权范围
  & scope = read
  1. 用户跳转后,B 网站 会要求用户登录,然后询问是否同意给予 A 网站授权。用户如果表示同意,这时 B 网站就会跳回 redirect_uri 参数指定的网址。跳转时,会传回一个授权码 :
https://a.com/callback?code=AUTHORIZATION_CODE
  1. A 网站拿到授权码以后,就可以 在后端 使用 HttpClient 向 B 网站 请求令牌
https://b.com/oauth/token?
 // 客户端信息
 client_id = CLIENT_ID&
 client_secret = CLIENT_SECRET&
 // 授权方式为授权码
 grant_type = authorization_code&
 // 上一步拿到的授权码
 code = AUTHORIZATION_CODE&
 // 接收令牌的uri
 redirect_uri = CALLBACK_URL
  1. B 网站收到请求以后,就会 颁发令牌

    具体做法是 B网站 在后端 使用 HttpClientredirect_uri 指定的URI,给A网站的后台发送一段包含 Token 的 JSON 数:

{    
  "access_token" : "ACCESS_TOKEN",
  "token_type" : "bearer",
  "expires_in" : 2592000,
  "refresh_token" : "REFRESH_TOKEN",
  "scope" : "read",
  "uid" : 100101,
  "info" : {...}
}

应用场景

这种基于OAuth的认证机制适用于个人消费者类的互联网产品,如社交类APP等应用,但是不太适合拥有自有认证权限管理的企业应用

为了理解OAuth的适用场合,让我举一个假设的例子。

有一个"云冲印"的网站,可以将用户储存在Google的照片,冲印出来。用户为了使用该服务,必须让"云冲印"读取自己储存在Google上的照片。

问题是只有得到用户的授权,Google才会同意"云冲印"读取这些照片。那么,"云冲印"怎样获得用户的授权呢?

传统方法是,用户将自己的Google用户名和密码,告诉"云冲印",后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点。

(1)"云冲印"为了后续的服务,会保存用户的密码,这样很不安全。

(2)Google不得不部署密码登录,而我们知道,单纯的密码登录并不安全。

(3)"云冲印"拥有了获取用户储存在Google所有资料的权力,用户没法限制"云冲印"获得授权的范围和有效期。

(4)用户只有修改密码,才能收回赋予"云冲印"的权力。但是这样做,会使得其他所有获得用户授权的第三方应用程序全部失效。

(5)只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。

OAuth就是为了解决上面这些问题而诞生的

Cookie Auth

Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象;

通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理

默认的,当我们关闭浏览器的时候,cookie会被删除。

但可以通过修改cookie 的expire time使cookie在一定时间内有效;

Token Auth

Token机制相对于Cookie机制又有什么好处呢?

  • 支持跨域访问:

    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).

对Token认证的五点认识

对Token认证机制有5点注意的地方:

  • 一个Token就是一些信息的集合;
  • 在Token中包含足够多的信息,以便在后续请求中减少查询数据库的几率;
  • 服务端需要对cookie和HTTP Authrorization Header进行Token信息的检查;
  • 基于上一点,你可以用一套token认证代码来面对浏览器类客户端和非浏览器类客户端;
  • 因为token是被签名的,所以我们可以认为一个可以解码认证通过的token是由我们系统发放的,其中带的信息是合法有效的;

的五点认识

对Token认证机制有5点注意的地方:

  • 一个Token就是一些信息的集合;
  • 在Token中包含足够多的信息,以便在后续请求中减少查询数据库的几率;
  • 服务端需要对cookie和HTTP Authrorization Header进行Token信息的检查;
  • 基于上一点,你可以用一套token认证代码来面对浏览器类客户端和非浏览器类客户端;
  • 因为token是被签名的,所以我们可以认为一个可以解码认证通过的token是由我们系统发放的,其中带的信息是合法有效的;