tuupola/slim-jwt-auth

此包已被废弃且不再维护。作者建议使用jimtools/jwt-auth包。

PSR-7和PSR-15 JWT身份验证中间件

3.8.0 2023-10-20 09:51 UTC

README

重要

此包已被废弃。有关替代方案,请参阅jimtools/jwt-auth

PSR-7和PSR-15 JWT身份验证中间件

此中间件实现了JSON Web Token身份验证。最初是为Slim框架开发的,但也可以与任何使用PSR-7和PSR-15风格中间件的框架一起使用。它已在Slim框架和Zend Expressive框架上进行了测试。

Latest Version Packagist Software License Build Status Coverage

请注意!您正在阅读3.x分支的文档,该分支仅适用于PHP 7.1及以上版本。如果您使用的是较旧的PHP版本,请参阅2.x分支。这两个分支不兼容,有关升级说明,请参阅UPGRADING

中间件不实现OAuth 2.0授权服务器,也不提供生成、颁发或存储身份验证令牌的方式。它仅在通过标题或cookie传递时解析和验证令牌。例如,当您想使用JSON Web Tokens作为API密钥时,这很有用。

有关示例实现,请参阅Slim API Skeleton

安装

使用composer安装最新版本。

$ composer require tuupola/slim-jwt-auth

如果使用Apache,请将以下内容添加到.htaccess文件中。否则,PHP将无法访问Authorization: Bearer标题。

RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

使用方法

配置选项以数组形式传递。唯一必需的参数是secret,用于验证令牌签名。请注意,secret不是令牌。这是您用于签署令牌的秘密。

出于简便起见,以下示例显示了硬编码在代码中的secret。在现实生活中,您应该将其存储在别处。一个好的选择是环境变量。您可以使用dotenv或类似工具进行开发。以下示例假设您正在使用Slim框架。

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

以下是一个示例,其中您的秘密存储为环境变量

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "secret" => getenv("JWT_SECRET")
]));

当发出请求时,中间件会尝试验证和解码令牌。如果找不到令牌或验证和解码时发生错误,服务器将以401 Unauthorized响应。

当令牌被篡改或令牌过期时,会触发验证错误。有关所有可能的验证错误,请参阅JWT库源代码。

可选参数

路径

可选的path参数允许您指定网站的受保护部分。它可以是字符串或数组。您不需要指定每个URL。相反,将path设置视为文件夹。在以下示例中,以/api开头的所有内容都将被认证。如果您不定义path,则所有路由都将受保护。

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "path" => "/api", /* or ["/api", "/admin"] */
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

忽略

使用可选的ignore参数,您可以对path参数进行例外处理。在下面的示例中,所有以/api/admin开头的路径都将进行身份验证,但/api/token/admin/ping将不会进行身份验证。

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "path" => ["/api", "/admin"],
    "ignore" => ["/api/token", "/admin/ping"],
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

头部

默认情况下,中间件会尝试从Authorization头部中查找令牌。您可以使用header参数更改头部名称。

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "header" => "X-Token",
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

正则表达式

默认情况下,中间件假定头部值的格式为Bearer <token>。您可以使用regexp参数更改此行为。例如,如果您有自定义头部,例如X-Token: <token>,则应传递头部和正则表达式参数。

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "header" => "X-Token",
    "regexp" => "/(.*)/",
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

Cookie

如果既没有从环境变量也没有从头部找到令牌,则中间件会尝试从名为token的Cookie中找到它。您可以使用cookie参数更改Cookie名称。

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "cookie" => "nekot",
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

算法

您可以通过algorithm参数设置支持算法。这可以是字符串或字符串数组。默认值为["HS256", "HS512", "HS384"]。支持的算法有HS256HS384HS512RS256。请注意,同时启用HS256RS256是一个安全风险

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "secret" => "supersecretkeyyoushouldnotcommittogithub",
    "algorithm" => ["HS256", "HS384"]
]));

属性

当令牌成功解码且身份验证成功时,解码后的令牌内容将作为token属性保存到$request对象中。您可以使用attribute参数更改此设置。设置为nullfalse以禁用此行为

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "attribute" => "jwt",
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

/* ... */

$decoded = $request->getAttribute("jwt");

记录器

可选的logger参数允许您传入一个PSR-3兼容的记录器,以帮助调试或其他应用程序记录需求。

use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;

$app = new Slim\App;

$logger = new Logger("slim");
$rotating = new RotatingFileHandler(__DIR__ . "/logs/slim.log", 0, Logger::DEBUG);
$logger->pushHandler($rotating);

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "path" => "/api",
    "logger" => $logger,
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

之前

仅在身份验证成功但在调用下一个中间件之前调用之前函数。您可以使用此函数在传递给下一个中间件之前更改请求。如果返回值不是Psr\Http\Message\ServerRequestInterface,则将忽略返回值。

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "secret" => "supersecretkeyyoushouldnotcommittogithub",
    "before" => function ($request, $arguments) {
        return $request->withAttribute("test", "test");
    }
]));

之后

仅在身份验证成功且已调用传入中间件堆栈之后调用之后函数。您可以使用此函数在传递给堆栈中的下一个传出中间件之前更改响应。如果返回值不是Psr\Http\Message\ResponseInterface,则将忽略返回值。

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "secret" => "supersecretkeyyoushouldnotcommittogithub",
    "after" => function ($response, $arguments) {
        return $response->withHeader("X-Brawndo", "plants crave");
    }
]));

请注意,after和before回调函数都通过$arguments参数接收原始令牌字符串以及解码后的声明。

错误

当身份验证失败时调用错误。它接收最后的错误信息作为参数。您可以使用此功能,例如返回JSON格式的错误响应。

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "secret" => "supersecretkeyyoushouldnotcommittogithub",
    "error" => function ($response, $arguments) {
        $data["status"] = "error";
        $data["message"] = $arguments["message"];

        $response->getBody()->write(
            json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
        );

        return $response->withHeader("Content-Type", "application/json")
    }
]));

规则

可选的rules参数允许您传入定义请求是否应进行身份验证的规则。规则是一个可调用的,接收请求作为参数。如果任何规则返回布尔值false,则请求将不会被身份验证。

默认情况下,中间件配置如下。所有路径都使用所有请求方法(除了OPTIONS)进行身份验证。

$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "rules" => [
        new Tuupola\Middleware\JwtAuthentication\RequestPathRule([
            "path" => "/",
            "ignore" => []
        ]),
        new Tuupola\Middleware\JwtAuthentication\RequestMethodRule([
            "ignore" => ["OPTIONS"]
        ])
    ]
]));

RequestPathRule包含一个path参数和一个ignore参数。后者包含不应进行身份验证的路径。RequestMethodRule包含不应进行身份验证的请求方法的ignore参数。将ignore视为白名单。

在99%的情况下,您不需要使用rules参数。它仅用于默认值不适用时的特殊情况。

安全

JSON Web Tokens本质上相当于密码。您应该将其视为密码,并且始终使用HTTPS。如果中间件检测到在HTTP上的不安全使用,则将抛出RuntimeException。默认情况下,对于运行在localhost上的服务器,此规则较宽松。要允许不安全的使用,您必须手动将其设置为false

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "secure" => false,
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

另外,您也可以列出多个开发服务器以使用宽松的安全设置。以下设置允许 localhostdev.example.com 接收未加密的请求。

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "secure" => true,
    "relaxed" => ["localhost", "dev.example.com"],
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

授权

默认情况下,中间件仅进行身份验证,这并不有趣。JWT 的优点在于您可以在令牌中传递额外的数据。这些数据可以包括用于授权的范围等。

如何实现令牌数据的存储或可能的授权实施取决于您。

假设您有一个包含范围数据的令牌。默认情况下,中间件将令牌内容保存到请求的 token 属性。

[
    "iat" => "1428819941",
    "exp" => "1744352741",
    "scope" => ["read", "write", "delete"]
]
$app = new Slim\App;

$app->add(new Tuupola\Middleware\JwtAuthentication([
    "secret" => "supersecretkeyyoushouldnotcommittogithub"
]));

$app->delete("/item/{id}", function ($request, $response, $arguments) {
    $token = $request->getAttribute("token");
    if (in_array("delete", $token["scope"])) {
        /* Code for deleting item */
    } else {
        /* No scope so respond with 401 Unauthorized */
        return $response->withStatus(401);
    }
});

测试

您可以选择手动或自动在每次代码更改时运行测试。自动测试需要 entr 运行。

$ make test
$ brew install entr
$ make watch

贡献

有关详细信息,请参阅 CONTRIBUTING

安全

如果您发现任何与安全相关的问题,请通过电子邮件 tuupola@appelsiini.net 而不是使用问题跟踪器来报告。

许可

MIT 许可证(MIT)。有关更多信息,请参阅 许可文件