目录
为什么要使用Token
Token的安全性
JWT(Json Web Token)
JWT能力有限
java web使用基于服务器的认证(传统方式Session)
由于HTTP协议是无状态的, 就是说如果我们使用用户名和密码来认证一个用户, 在下一次请求的时候, 我们的应用程序不会知道我们是谁, 我们不得不重新认证。为了解决这个问题,就有了session,利用请求cookie实现。(Session有状态)
由于原来的API认证用户名和密码是存储在服务器端的,由应用完成每次请求的合法性验证。
这种认证存在问题:
很容易收到CSRF攻击,由于该攻击利用cookie获得session,如果是金融类网站且有跳转的,就很危险。
会话多了,服务器session就多,内存占用多,同时限制了伸缩性。(与负载均衡服务器也会有耦合)
Token的使用不存在上述问题
基于token的认证是无状态的,不会将用户信息记录到服务器或者会话中。
可以实现完全的动静分离,实现单页面APP。
更符合RESTful的规范。
每一个请求都需要token, 在HTTP header中发送, 以便我们保持无状态HTTP请求。
我们也需要将服务器设置为能使用Access-Control-Allow-Origin: * 接受来自所有域的请求
OAuth进行授权一般使用在开放平台中,对于API的保护来说,使用OAuth过于复杂,主要还是使用JWT。
token会过期,用户需要再次登录或者使用refreshToken重新获取一个token。
例如:token可以在30分钟过期,refreshToken可以久一点,一旦refreshToken过期了,就需要用户重新登录了。
在使用无状态的Token的时候,需要注意:
Refresh Token有效期较长,所以它应该在服务器端有状态,以增强安全性,确保注销销毁。
应该考虑使用二次认证来增强敏感操作的安全性。
Token不能防重放、不能防篡改
疑问1:如何防止重放(Replay Attacks),HTTPS数据加密是否可以防止重放攻击?
解答:HTTPS不能防止重放,但加密可以有效防止明文数据被监听。
可以利用Nonce(这个是请求的唯一标识,一般使用UUID来标识),或者timeStamp(误差不超过15分钟)做防重放。
疑问2:如何防止篡改?
解答:添加签名。
请求所有的内容都被加入签名计算,所以请求的任何修改,都会造成签名失败。
既然token比较好用,有没有规范呢,JWT。
首先看下JWT生成的token样子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICAgIm5hbWUiOiJUb3B2aWV3ZXJzIi wKICAgICJhZ2UiOiIzMCIsCiAgICAib3JnIjoiQklUQ09NIgp9.55TF72vSkj-a64aHHiYN5eoZ9Nb4w5vb45PsER7x_AB
3.1 JWT的构成
header、payload、signature三部分
第一部分:header
jwt的头部承载两部分信息,完整的头部例如:
{
`typ`:'JWT',//声明类型,这里是jwt
`alg`:'HS256'//声明加密的算法 通常直接使用 HMAC SHA256
}
然后将头部进行base64编码,构成了第一部分:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
第二部分:payload
{
"name":"Topviewers",
"age":"30",
"org":"BITCOM"
}
标准中注册的声明
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
然后将其进行base64加密,得到Jwt的第二部分:
ewogICAgIm5hbWUiOiJUb3B2aWV3ZXJzIiwKICAgICJhZ2UiOiIzMCIsCiAgICAib3JnIjoiQklUQ09NIgp9
第三部分:signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
header (base64后的)
payload (base64后的)
secret
这个部分需要base64编码后的header和base64编码后的payload使用‘.’(点)连接组成的字符串,然后通过header中
声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分:
55TF72vSkj-a64aHHiYN5eoZ9Nb4w5vb45PsER7x_AB
这部分无法在客户端解密,密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和验证,所以需要保护好。
3.2 JWT 实现
JWT实现代码,引入即可,代码实现了token签发与验签
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.1.0</version>
</dependency>
JWT能力有限,主要实现针对不同组织和用户实现API权限控制、访问控制,开放平台API访问几乎都是这种方式。
JWT不能防止客户端数据被拦截篡改,同样也无法阻止重放。
以上只是理论,在应用场景上还需要仔细设计。
微信公众号对接上,使用了很多token,例如access_token、fresh_token。
token背后的权限、访问控制才是设计的要点,例如结合shiro或者spring Security。
本文由威海网络公司半岛科技整理发布!2018.03.21