文章目录
几种常见的认证机制
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 应用
授权码通过前端传送,令牌则是储存在后端,而且 所有与资源服务器的通信都在后端完成
这样的前后端分离,可以避免令牌泄漏
- A 网站 提供一个链接,用户点击后就会跳转到 B 网站,授权用户数据给 A 网站使用
跳转到的B网站链接如下:
https://b.com/oauth/authorize?
// 要求返回授权码
response_type = code
// 让B知道是谁在请求
& client_id = CLIENT_ID
// B接受或者拒绝请求后的跳转网址
& redirect_uri = CALLBACK_URL
// 授权范围
& scope = read
- 用户跳转后,B 网站 会要求用户登录,然后询问是否同意给予 A 网站授权。用户如果表示同意,这时 B 网站就会跳回
redirect_uri
参数指定的网址。跳转时,会传回一个授权码 :
https://a.com/callback?code=AUTHORIZATION_CODE
- 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
B 网站收到请求以后,就会 颁发令牌。
具体做法是 B网站 在后端 使用
HttpClient
向redirect_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是由我们系统发放的,其中带的信息是合法有效的;