源码示例
模拟业务场景
上一篇,我们已经做足了准备工作,并拟定了具体的业务场景:
1. 用户登录成功,生成 TOKEN;
2. 客户端每一次对 TODO 模型进行 CRUD 操作时,都会携带 TOKEN;
3. 服务端会校验 TOKEN 合法性,给予响应请求的返回及数据库操作。
在接下来的具体编码中,不会有表单验证、注册那些逻辑,因为本文只是用于描述如何将 CI 集成为一个 JWT RESTfull API Repo。
定义模型
application/modles/User_model.php
User 模型中,一个简单的 login 方法用于对比账号密码是否正确,我们仅仅只是模拟一下 HTTP 请求进行了真实的数据库操作:
由于没有写注册逻辑,你应该在数据库中提前增加一条用户数据,如:
application/models/Todo_model.php
Todo 模型中,新增、删除、修改、查询单条记录、查询全部记录等操作:
Authorization 辅助类
由于生产 、验证 TOKEN 来自 JWT 类中的方法,为了能更方便的使用 JWT 类,我们可以在 CI 中封装自己的“辅助类”,一般 CI 将自定义的辅助类放在 application/helpers/ 中。
在这个辅助类中,引入 JWT,并且在 Authorization 类中封装了两个方法:
1. func validateToken:用于校验 TOKEN 的合法性;
2. func generateToken:用于生产 TOKEN;
Autoload 配置项
为了能更方便的使用数据库,而不用频繁的在控制器构造函数中书写类似 $this->load>library('database') 这样的代码,在 CI 中,我们可以将需要自动加载的特定 library、helper 等直接配置进 autoload 中。
Auth 控制器
准备了这么多,是时候写第一个控制器了,完成我们的场景一:客户端传递 POST 请求,服务端通过 username & password 参数,并校验参数合法性,生产 TOKEN,并返回给客户端。
接下来,我们需要在客户端中模拟这个“获取TOKEN”的 POST 请求。
打开某个专门用于测试接口的工具,例如 Postman,我这里用的 apizza :apizza.cc/
和上一篇一样,我们使用 php -S localhost:8000,在根目录下键入该命令,将项目运行起来后,在 apizza 中模拟我们这次的请求:
如果客户端传入的账户密码不存在,按照 Restfull 约定,服务端应该返回状态码为 401 Unauthorized:
Todo 控制器
该控制器会给予针对 Todo 模型的 CURD 操作的 API,通常服务端的 API URL 看起来应该像是这样的 : http://example.com/api/,CI 升级至 3.* 版本后控制器便能支持多级目录结构,现在我们来创建 Todo 控制器:
4个方法名分别对应了 GET、POST、PUT、DELETE 这四种请求方式。
1. POST 请求
todo 表现在仍然是一张空表,因此,我们从 POST 请求开始,这个请求通常用来创建资源:
创建成功,接口将会返回 200 状态码,并将新创建的 todo 记录作为返回数据,现在我们发出几次请求,让 API 为我们在数据库中插入3条记录:
2. GET 请求
现在表中有了3条记录,编写 GET 请求来进行获取资源,服务端查看 URL 中是否有 id 参数,如果有则返回该 id 的单条数据,若没有,则返回全部数据,当然这个示例并没有校验 id 的合法性,也没有处理 404 的错误,实际开发你扔需要填补这些逻辑:
单条记录:
全部记录:
3. PUT 请求
PUT 通常用来修改某个指定的资源:
4. DELETE 请求
DELETE 用于删除指定的资源:
钩子函数
至此,我们全部的 API 编写完毕。
接下来,也是最后一步,我们还需要加入钩子函数,在 API 相关控制器调用方法之前,验证请求头是否包含 TOKEN,并在钩子函数中校验其合法性。
1. 首先,我们需要再次打开 application/config/config.php,开启 hooks:
2. 指定 hooks
CI 总共有 7 种钩子,这里我们选择 post_controller_constructor,它会在你的控制器实例化之后立即执行,控制器的任何方法都还尚未调用。我们仍然需要在钩子配置中指定我们将要使用的自定义钩子类以及执行的函数名称:
3. 编写 ApiAuthHook
保护路由
当我们再次访问任意一个 API 请求时,服务端会回应一个 400 的 Error Code:
还记得第一个 auth/token/ 的 POST 请求吗?这个请求服务端返回了 TOKEN,例如:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEifQ.rpPLzNRB1nK_N_Ch7eSvJCpwFKwEWyNozS67RE6eaUU
现在我们在请求头部加入 Authorization,并将其的值设置为 TOKEN:
Cool!接口正常返回了全部数据!
为什么在 TOKEN 值前面加入 lovchun.com?
前端需要通过 cookie 或者 localstorage 来存储 TOKEN,以此来保存调用接口的凭据。
试想一下,如果你的前端工程需要对接多个 API Server,通过什么来标识 TOKEN 分别对应着哪个 Server ?
将 Authorization 的值用“唯一标识”+“一个英文空格”+“TOKEN”来存储,服务端校验时,通过搜索该“唯一标识”的 Authorization 来获取 TOKEN(当然这个标识你完全可以自定义):
如果你是以 Apache 或其他 Web Server 启动项目,Apache 会过滤掉请求头中的 Authorization 字段!
请在根目录下的 .htaccess 文件中进行修改,让你的 Apache 能正常接收 Authorization: