在过去一年中,我投入时间和精力相对较多的一件事,便是研究了openresty并学习了一点Lua语言。虽然都并不深入,但基于这两者,在工作的过程中还是写了一些脚本,比如构建公司账户系统的token机制,还有其他一些关于日志收集,限流等功能。总觉得还不够,openresty是一个性能非常高的平台,我觉得应该把更多的任务交由它来运行。但是现在知道或者应用它的人还非常少,所以决定写一个项目。

我并没有什么好的规划,这个项目也只是一时兴起,所以功能可能会比较杂。但是我保证,那些功能绝对在实际运用中是非常急需的,非常有价值的。

功能

在项目开始之前,我要简单的规划一下项目的一些规范,要具备的功能等,为以后的开发指明一个基本方向。

规范

命名规范

  • 文件命名:小写字母 + 下划线_
  • 变量名函数名:小驼峰命名法
  • 全局常量: __开头全大写

接口规范

请求: 使用restful风格GET:查询, POST: 新建 PUT: 更新, DELETE: 删除

响应: 一律响应json串

具体功能

  • 日志收集(kafka) / log_collection
  • token
  • 限流
  • 异常IP,请求头检测及封锁
  • OAuth2 (待定)
  • 动态摘挂机器

token认证机制

在微服务盛行的当下,服务被拆分成多个微服务,它们可能不仅仅是在后端分拆成不同的web服务,甚至可能在不同的域名下,传统的session认证方式已经无法满足业务的需要,因此才需要token这种无状态的认证方式。

token可以翻译成令牌,他的概念很好理解,也就是说,客户端在向服务气短请求数据时,必须要要带着一张令牌,服务器端在验证令牌合法后,才会返回数据。

JWT

JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快 **自包含(Self-contained)**:负载中包含了所有用户所需要的信息,避免了多次查询数据库。

JWT结构

JWT包含了使用.分隔的三部分: header.payload.signature

header种通常包含两部分。token的类型和采用的加密算法:

1
2
3
4
{
"alg":"HS256",
"typ":"JWT"
}

然后使用base64编码该json字符串得到header。

PayLoad

Token的第二部分是负载也可以叫消息体。它包含了一些标准的字段,同时也可以自定义添加字段。

1
2
3
4
5
6
7
8
9
10
11
12
{
"iss":"server.com", # jwt的签发者,非必需
"aud":"server.com", # jwt的接收方,非必需
"iat":"1527948053929", # 签发时间,unix时间戳
"exp":"1527948053929", # 过期时间,unix时间戳
"sub":"", # jwt面向的用户,非必需
"nbf":"", # 定义在什么时间之前,该jwt是不可用的,非必需
"jti":"", # jwt的唯一标识,主要用来作为一次性token,来防止重放攻击
"uid":"", # 自定义字段,标识用户,用户ID加密字符串
"unm":"", # 自定义字段,用户username,可以是phone或者email
"rol":"", # 自定义字段, 标识用户角色
}

将上述json进行base64编码得到payload.

Signature

得到header和payload之后,将header.payload字符串按照header种定义的加密算法进行加密,得到加密字符串signature. 其中加密所用的secret存储在服务器上,在校验token有效性时使用同样的secret进行校验。

token

最后连接header.payload.signature得到token. 这个操作是在服务器上进行。生成token后返回给客户端,客户端在进行请求时,带着token, 服务器端进行token有效性检查。

jwt优缺点

优点

  1. jwt是无状态的,token保存在客户端,服务器端不需要保存,节省服务端开销
  2. jwt种保存了用于校验的信息,服务端不需要查询数据库,性能好

缺点

  1. 由于服务器端不保存token状态,无法做到立即强制失效。这一点很蛋疼。
  2. payload种需要添加可唯一标识用户的信息,而且base64等同于明文

jwt改进

我们把改进的重点放在强制失效上,因为在实际的应用中,这实在是一个刚性需求。那么怎么才能做到呢?我想了很久,好像除了在服务器上保存token的状态之外,别无他法。那么怎么来标识用户当前token是否。我们在服务器端校验有效性时,是使用存储在服务器端的密钥来校验,如果我们修改了这个密钥,那token不就立即失效了吗。所以解决的办法就是,我们在服务器端对每一个用户都保存一个对应的密钥。不同的用户请求使用对应的密钥来进行校验,这样还有一个好处,就是即使密钥泄露,可能也只影响一个用户,安全性更高。

要实现的功能

本次我不打算实现太多的功能,但是会极可能的考虑可扩展性。

实现原则:jwt中存放尽量多的信息,而服务器端存放尽可能少的信息。

进度

7/14 加密算法已经搞定.

密钥存储方式

tkutil.lua