在第一部分中,我们创建了基于 JSON API 的 Rails 应用,搭建了 Rspec 测试框架,也介绍了简单的测试驱动的开发。在第二部分中,我们要建立 JWT 认证(JSON Web Token),包换注册和登录,当然还是使用 TDD。认证原理的外链
认证涉及到用户,那先创建用户
建立model的测试,spec/models/user_spec.rb
添加固件,spec/factories/users.rb
添加model的约束,app/models/user.rb
has_secure_password,rails5 会自动添加相关属性 password_digest,经过信息摘要处理的 password,总不能在数据库里存储明文密码吧。检查 gemfile 确认存在
接下去要做的几件事情:
- JsonWebToken: Encode & decode jwt tokens
- AuthorizeApiRequest: 每一个API请求的认证
- AuthenticateUser: 认证用户
- AuthenticationController:认证流程
JsonWebToken
什么是基于token的认证,外链,确认Gemfile里面有
然后我们会把生成 jwt token 的 class 放在 lib 目录下,因为这不是领域特定的。但是 lib 目录下的所有内容是自动加载的。但是 Rails 5 中因为线程问题,取消了自动加载,关于这个问题的讨论,外链
定义这个单例,app/lib/json_web_token.rb
这是标准写法😄,包含 encode 和 decode 两个方法。encode 方法负责生产 token,使用 payload,这里是 user id,还有确定失效时间。因为每个 Rails 应用都有一个唯一的 secret key, 干脆就依据它来产生 token。能看到 decode 是一个相反的过程,使用了相同的 secret key。如果token过期或者无法 decode,那么所有错误我们在 Exception Handler 中统一处理
每一个Request的认证
用户认证以后,每次请求当中都会带一个token表示当前用户。现在创建认证服务
编辑 spec/auth/authorize_api_request_spec.rb
简单解释下,call 方法是入口,先要一个认证的用户,登陆也好,注册也好,结果就是有一个用户被 server 认证了。看到两个方法还没有被定义,token_generator 和 expired_token_generator
创建并编辑它
这太明显了,看字面意思就是了,不多解释。然后要把这个 module 包含在 rails helper 中,我们才能使用。编辑 rails_helper.rb,全局有效,不只是 request
接下去,编辑 app/auth/authorize_api_request.rb,生成对应的路由和方法
看到 Message.missing_token 对吧,我们对所有的详消息做一个封装,app/lib/message.rb
认证用户
就是登陆注册的功能
先写测试,看到 authenticate_user_spec.rb
AuthenticateUser 入口是 call 方法,注释已经说明一切,编辑 authenticate_user.rb
收到 email 和 password,检查如果是有效的就返回一个依据 userid 产生的 token。测试下
应该都是通过的
认证流程
要建立controller
先写测试,看到 spec/requests/authentication_spec.rb
编辑对应的控制器,app/controllers/authentication_controller.rb
最后增加路径,编辑 config/routes.rb
在认证用户之前,这个用户总得注册对吧,这属于 User 的行为,放到 User Controller 中解决,建立控制器和对应的 request 测试文件
还是先写测试,看到spec/requests/users_spec.rb
编辑路由,config/routes.rb,增加
编辑控制器
嗯,还差一件事情,就是直接访问的 API 的时候,必须要有 token,在基类控制器中可以做一个全局的限制。先写测试,spec/controllers/application_controller_spec.rb
修改控制器基类,app/controllers/application_controller.rb
现在是对所有的 request 做限制,但是我在注册和登录操作中是没有 token 的,所以再加一句话
还有 user controller,app/controllers/users_controller.rb
如果现在运行测试,肯定不通过,要更新测试用例,现在加了认证,编辑spec/requests/todos_spec.rb
还要更新 todos_controller.rb,增加 user 和 todo 的关联
同样的,我们更新 spec/requests/items_spec.rb
现在运行测试用例,应该是全部通过的,如果有异常,看提示自行处理。没问题的话,自己在本地用 Postman 或者 httpie 手动测试下