如果你还不知道 JSON Web Token(简称 JWT)是什么,有什么用途,常见的应用场景是什么,基本原理是什么,请先阅读 JSON Web Tokens 是什么? 学习相关知识。
在 Slim 中,使用中间件引入 JWT 机制是最合适的。我们这里使用的是 Slim-jwt-auth 项目,这个项目的基础是 php-jwt 项目,有兴趣可以研究下这两个项目的代码。
配置中间件
安装 Slim-jwt-auth 后在 Slim 中按如下方式引入
/*
* Slim JWT 身份验证中间件
* https://github.com/tuupola/slim-jwt-auth
*
* 参数说明
*
* cookie :使用 cookie 存储 Token,此参数设置 cookie 的名称
* secure :是否使用 HTTPS 验证
* relaxed :不进行 HTTPS 验证的域名
* secret :密钥
* path : 需要进行身份验证的路径,设置为 '/' 表示验证全部的路径
* passthrough :不需要进行身份验证的路径
* attribute :在 Request 中的标志,可通过 $request->getAttribute('') 读取
* environment :
* header :在 header 中的标志,默认是 Authorization,配合 environment 设置一起使用
* error : 未能通过验证时的回调函数
* callback : 通过身份验证时的回调函数
*/
$app->add(
new \Slim\Middleware\JwtAuthentication(
[
"attribute" => "JWT_Auth",
"path" => "/v1/user",
"passthrough" => "/token",
"environment" => ["HTTP_JWT_AUTH", "REDIRECT_HTTP_JWT_AUTH"],
"header" => "JWT_Auth",
"secret" => getenv("JWT_SECRET"),
"secure" => false,
"error" => function ($request, $response, $args) use ($container) {
$json['code'] = -1;
$json['note'] = $args['message'];
$json['help'] = 'http://api.app.com';
$container->log->write($args['message']);
return $response->withStatus(401)
->withHeader("Content-Type", "application/json")
->write(json_encode($json, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
},
"callback" => function ($request, $response, $args) use ($container) {
$container->log->write($args);
}
]
)
);主要就是要理解这里面的各种设置项,可仔细阅读 Slim-jwt-auth 的说明文档。我这里使用的自己写的日志类库来记录日志,也可以参考官方的做法,使用 Monolog\Logger 来实现。
use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
$logger = new Logger("slim");
$rotating = new RotatingFileHandler(__DIR__ . "/logs/slim.log", 0, Logger::DEBUG);
$logger->pushHandler($rotating);
$app->add(
new \Slim\Middleware\JwtAuthentication(
[
"attribute" => "JWT_Auth",
"path" => "/v1/user",
"passthrough" => "/token",
"logger" => $logger
]
)
);当身份验证未通过时会自动将错误信息写入日志文件,也就是错误回调中参数中的 message 中的内容。
生成 Token
当用户提供 username 和 password 并通过验证后,服务器端需要为其生成一个 JSON Web Token,并返回给客户端,客户端将其存在本地。
无论何时想要访问一个受保护的路由或资源,客户端应该随请求一起发送 JSON Web Token。JSON Web Token 通常在 Authentication HTTP 头部中,使用 Bearer 格式(schema)存放,HTTP 头部的内容通常是这样的格式:
Authorization: Bearer <token>
下面是在 Slim 中生成 JSON Web Token 的例子:
public function index($request, $response, $args)
{
$data = array(
"iss" => "https://auth.medlande.com/v1", //签发者
"sub" => "Auth", // 主题
"aud" => '1', // 受众
"iat" => time(), // 签发时间
"nbf" => time(), // 启用时间
"exp" => time() + 3600 // 过期时间
);
$token = JWT::encode($data, getenv("JWT_SECRET"));
$json['code'] = 0;
$json['note'] = 'Success.';
$json['data'] = array('token' => $token);
$json['help'] = 'http://api.hbdx.com';
// setcookie("JWT_Auth", $token, time() + 7200);
// 前台得到 Token 后,可以写入 cookie,或者 HTTP HEADER,配合 middleware 的设置来做
return $response->withStatus(200)
->withHeader("Content-Type", "application/json")
->write(json_encode($json, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}输出的结果为:
{
code: 0,
note: "Success.",
data: {
token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvYXV0aC5tZWRsYW5kZS5jb21cL3YxIiwic3ViIjoiQXV0aCIsImF1ZCI6IjEiLCJpYXQiOjE1MDI0MzY5ODAsIm5iZiI6MTUwMjQzNjk4MCwiZXhwIjoxNTAyNDQwNTgwfQ.FboOTNextlqK3-7LHyje6-QmlkqhjA-EYnSj-wsE4EM"
},
help: "http://api.hbdx.com"
}客户端请求
我这里使用 PHP 来做模拟请求,使用的是 Guzzle Http 请求库,代码如下:
$server = 'http://api.hbdx.cc/v1';
$client = new \GuzzleHttp\Client();
$res = $client->request('GET', $server . '/token');
$json = $res->getBody();
$data = json_decode($json);
$token = $data->data->token;
// var_dump($token);
$res = $client->request('GET', $server . '/user/1', [
'headers' => [
'JWT_Auth' => 'Bearer ' . $token
]
]);
$json = $res->getBody();
echo $json;首先,我们通过 token 接口向服务器端请求了一个 JSON Web Token,在实际应用中就是登录接口。user 接口是一个受保护资源,需要身份验证,我们在 HTTP Header 中按照 JSON Web Token 要求的格式添加 Token。注意,这里使用的是 JWT_Auth 字段,这是因为我们在中间件的配置中进行了设置:
"environment" => ["HTTP_JWT_AUTH", "REDIRECT_HTTP_JWT_AUTH"],
"header" => "JWT_Auth"默认使用 Authorization 就可以了。
解析 Payload
请求通过 JSON Web Token 验证后,在 Slim 中我们可以通过
$auth = $request->getAttribute("JWT_Auth");来获取 Payload 信息,也就是在生成 JSON Web Token 时设置的内容。JWT_Auth 是在中间件中设置的 attribute 属性,从中取出用户标识,取出用户数据,返回给客户端。
结束。