tuupola/branca-middleware

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

0.7.0 2020-10-29 15:43 UTC

This package is auto-updated.

Last update: 2024-08-30 01:20:05 UTC


README

Latest Version Software License Build Status Coverage

本中间件实现了Branca令牌身份验证。Branca类似于JWT,但更安全,令牌大小更小。该中间件可以与任何使用PSR-7或PSR-15风格中间件的框架一起使用。它已在Slim框架Zend Expressive上进行了测试。

您可能还感兴趣阅读Branca作为JWT的替代方案?

本中间件实现OAuth授权服务器,也不提供生成、颁发或存储身份验证令牌的方式。它仅当令牌通过header或cookie传递时解析和验证令牌。

安装

使用composer安装最新版本。

$ composer require tuupola/branca-middleware

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

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

用法

配置选项以数组形式传递。唯一必需的参数是32字节的secret,用于验证和加密令牌。

为了简单起见,示例显示了硬编码在代码中的secret。在实际生活中,你应该将其存储在其他地方。一个好的选择是环境变量。你可以在开发中使用dotenv或类似工具。示例假设您正在使用Slim框架。

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

一个将您的密钥存储为环境变量的示例

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

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

验证错误会在令牌被篡改或可选地令牌过期时触发。

可选参数

路径

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

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

忽略

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

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

TTL

Branca令牌在header中嵌入创建时间戳。您可以使用ttl参数来控制应用程序接受的令牌的年龄。

$app->add(new Tuupola\Middleware\BrancaAuthentication([
    "ttl" => 3600, /* 60 minutes */
    "secret" => "supersecretkeyyoushouldnotcommit"
]));

Header

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

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

Regexp

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

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

Cookie

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

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

日志记录器

可选的 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\BrancaAuthentication([
    "path" => "/api",
    "logger" => $logger,
    "secret" => "supersecretkeyyoushouldnotcommit"
]));

之前

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

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

之后

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

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

错误

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

$app->add(new Tuupola\Middleware\BrancaAuthentication([
    "secret" => "supersecretkeyyoushouldnotcommit",
    "error" => function ($request, $response, $arguments) {
        $data["status"] = "error";
        $data["message"] = $arguments["message"];
        return $response
            ->withHeader("Content-Type", "application/json")
            ->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
    }
]));

规则

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

默认情况下,中间件配置看起来像这样。所有路径都使用除 OPTIONS 之外的所有请求方法进行身份验证。

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

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

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

安全性

Branga令牌本质上就是密码。您应该将其视为密码,并且始终使用HTTPS。如果中间件检测到通过HTTP的不安全使用,它将抛出 RuntimeException。对于本地主机的请求,此规则会被放宽。要允许不安全的使用,您必须手动将其设置为 false

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

或者,您可以列出您的开发主机以具有放宽的安全性。

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

授权

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

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

假设您有一个包含请求范围和uid的JSON编码的有效负载。使用 before 回调,您可以向请求对象注入未编码的有效负载。

[
    "uid" => 123,
    "scope" => ["read", "write", "delete"]
]
$app->add(new Tuupola\Middleware\BrancaAuthentication([
    "secret" => "supersecretkeyyoushouldnotcommit",
    "before" => function ($request, $arguments) {
        $payload = json_decode($arguments["payload"], true);
        return $request->withAttribute("token", $payload);
    }
]));

$app->delete("/item/{id}", function ($request, $response, $arguments) {
    if (in_array("delete", $request->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)。有关更多信息,请参阅许可文件