marcel-maqsood/session-auth-middleware

SessionAuthMiddleware 是一个 PSR-15 中间件,用于在 Laminas/Mezzio 应用程序中处理会话和登录


README

您可以使用以下命令安装此包:composer require marcel-maqsood/session-auth-middleware

配置

附加说明

由于我们的中间件可以在任何请求上运行,因此它应该注入到您的应用程序的 config\autoload\dependencies.global.php 文件中,就像在 dependencies.global.php 中看到的那样

'dependencies' => 
[
    'aliases' => 
    [
        AuthenticationInterface::class => PhpSession::class,
        UserRepositoryInterface::class => PDORepository::class,
    ],
    'invokables' => [],
    'factories' => 
    [
        PersistentPDO::class => PersistentPDOFactory::class,
        PDORepository::class => PDORepositoryFactory::class,
        Mezzio\Session\SessionMiddleware::class => Mezzio\Session\SessionMiddlewareFactory::class
    ],
],

这实现了多个目的

  • 您不必在每个模块中配置每个 ConfigProvide
  • 任何请求都将始终具备 SessionAuth 处理能力(但只有在路由包含我们的 SessionAuthMiddleware 时才会使用)
  • 您不能忘记在每个新模块中添加我们的基本配置;否则可能会很麻烦。

您可以在 config\autoload\authentication.global.php 中找到我们的默认配置,并将其放入您的应用程序的 config\autoload\ 文件夹中。它包含运行我们的 SessionAuthMiddleware 所需的所有配置,并且可以轻松复制和调整。

此外,您还必须将 Mezzio\Session\SessionMiddleware 添加到您的管道中(config\pipeline.php),它必须在管道的顶部包含

$app->pipe(ErrorHandler::class);
$app->pipe(ServerUrlMiddleware::class);
$app->pipe(SessionMiddleware::class); // <<<<<-----

如果您想为每个请求启用会话身份验证,也可以将我们的 MazeDEV\SessionAuth\SessionAuthMiddleware 添加到管道中。确保在管道中包含 SessionMiddleware 之前包含我们的中间件!

如果您想在管道中添加我们的中间件,那么在包含 Mezzio\Helper\UrlHelperMiddleware(它包含在每个基本管道中)之后包含我们的 SessionAuthMiddleware 是至关重要的。

$app->pipe(UrlHelperMiddleware::class);
$app->pipe(SessionAuthMiddleware::class);

为了便于使用,我们还包括了一个基本的数据库-sql 文件,其中包含此中间件需要的所有表和字段(类似于本文档中描述的默认设置)。您可以在 db\base.sql 中找到它,我们还包含了一个 MySQLWorkbench 文件 db\SQL-model.mwb,以便您可以根据需要调整它,而无需重新构建它。

此外:我们的 SessionAuthMiddleware 不允许一个账户进行多登录,我们故意实现了防止这种情况的功能,因为我们认为这是注销任何其他设备并通知它们已被注销的最安全方法。您只需在模板中使用“error”(作为可迭代对象)变量来显示发生的任何错误即可。

权限、组和登录甚至可以在数据库中标记为“隐藏”,这样就可以防止它们在列表中显示,从而没有人可以使用它们将它们添加到用户/组中,如果他们没有直接访问您的数据库的话。

无身份验证路由

如果您选择在管道中包含我们的 SessionAuthMiddleware 而不是在特定路由中,我们提供了一些可配置性,以排除某些路由的检查

'no-auth-routes' => [ //Routes that wont even be checked for authentication if the AuthSessionMiddleware is placed inside the pipe.
	'adminPasswordReset',
	'userPasswordReset',
],

这是强制性的,因为如果用户想要重置密码,他们就不会登录。

登录处理程序

为了提供您一个工作的登录处理程序,我们包含了一个具有本文档中提到的所有功能的处理程序,您可以在 src\LoginHandler\GlobalLoginHandler.php 中找到它

要使用它,您只需为其定义一个路由即可,因为它已经包含在我们的 ConfigProvider 中


$app->route('/authorized[/]',
    [
        MazeDEV\SessionAuth\SessionAuthMiddleware::class,
        MazeDEV\SessionAuth\LoginHandler\GlobalLoginHandler::class
    ],
    [
        'GET',
        'POST'
    ],
    'login1'
);

$app->route('/authorized/landing[/]',
    [
        MazeDEV\SessionAuth\SessionAuthMiddleware::class,
        App\Handler\YourLandingHandler::class
    ],
    [
        'GET',
        'POST'
    ],
    'authorizedPage'
);

所有以我们的SessionAuthMiddleware开始的路由都将被完全加密,并且需要有效的登录且权限设置正确。我们的GlobalLoginHandler可以将登录从不同的登录表单重定向到不同的目的地,你只需要为它设置配置即可。

'loginHandling' => [
    'login1' => [
        'name' => 'Base Login',
        'destination' => 'authorizedPage',
    ],
    'login2' => [
        'name' => 'Base Login2',
        'destination' => 'authorizedPage2',
    ],
    //...
],

我们的LoginHandler和SessionAuthMiddleware需要确切的语法才能正常运行。如果你没有定义loginHandling,SessionAuthMiddleware将无法检测路由及其所属位置。

请注意

每个路由的“routename”(例如:“authorizedPage”)也是其权限;因此,对于你定义的每个路由,你必须在数据库中添加一个权限并将其连接到所需的组。但是,这仅适用于我们的Middleware涉及的路由,任何其他路由都不需要在数据库中添加。

此外,你必须在你的'src\App\src\templates\app'文件夹中提供名为"Login.html.twig"的模板,它被我们的GlobalLoginHandler用于渲染登录表单。

我们在src\Templating\中提供给您一个基本的登录表单,名为Login.html.twig

注销处理器

我们提供一个默认的LogoutHandler,它只是从请求的会话中移除UserInterface并将请求重定向到您的首页路由。由于当前是硬编码的,您必须为您的应用程序提供一个名为"home"的路由,最佳情况:您的主页。这就是如何在您的路由中添加LogoutHandler的方法

$app->route('/logout[/]',
    [
        MazeDEV\SessionAuth\LogoutHandler\LogoutHandler::class,
    ],
    [
        'GET',
        'POST'
    ],
    'logout'
);

从您的应用程序中,您只需将其添加为链接或重定向,以便用户可以注销。

persistentpdo - 一个数组,我们在其中定义我们的数据库连接规则

有关更多信息,请参阅MazeDEV/Marcel-Maqsood(https://github.com/marcel-maqsood/DatabaseConnector)。我们的SessionAuthMiddleware使用此DatabaseConnector,因此需要其配置设置。在我们的默认配置中,我们已经提供了这些设置,您只需调整它们即可。此外,PersistentPDO必须包含在您的应用程序的config\autoload\dependencies.global.php中,因为它是SessionAuthMiddleware所必需的。我们已经在我们的config\dependencies.global.php中包含了它。

在“authentication”条目中,我们定义了Session-Auth Middleware的特定属性
'authentication' => [
    'redirect' => '/', //- The Link at which unauthorized request get redirect (As of PHPSession), however, the SessionAuthMiddleware won't use it.
    'username' => 'username', //- The key in which the username is within $_POST. default: 'username'
    'password' => 'password', //- The key in which the password is within $_POST. default: 'password'
    'repository' => [ //- An array, in which the details for our database-table are.
        'table' => 'login', //- The table, in which we look for the user.  default: 'logins'
        'fields' => [ //- An array in which the fields of that table are to authenticate a user.
            'identities' => [ //An array with all fields that contains login-names or mails, and so on.
                'username',
                'email'
            ], 
            'password' => 'anyPass' //- The key, with which we check if the password in $_POST is equal.
        ],
        'table_override' => [ // - An array, in which we define routes and their database-table prefix that the system will use tot check if they start with the key of any entry.
            'user'  => 'user', // Routename starts with 'user' => use table prefix 'user' : user - for base table, user_permissions for all permissions that only user-groups can have, etc.
            'admin' => 'admin',
        ],
    ],
    'security' => [ //- An array for our security features.
        'algo' => 'sha256', //- The algorithm used for generating the SessionHash stored in the database. default: 'sha256'
        'salt' => 'anySalt', // - The string which we use to harden our hashes be appending it.
        'fields' => [ //- An array, in which we define session related fields within our 'logins' table to be used to check if the session is valid.
            'session' => 'sessionhash', //- The key which we use to get the users current session-hash and check if it matches the request. default: 'sessionhash'
            'stamp' => 'sessionstart' //- The key which we use to get the session-start of the current session to check if it is still valid. default: 'sessionstart'
        ]
    ]
]

如果'repository'中的'key_override'未设置,系统将只使用'repository'中设置的'key'值映射到表。

我们的SessionAuthMiddleware也需要这个配置条目

'session' => [
    'config' => [
        'cookie_lifetime' => 60 * 60 * 1, //- Time in seconds which the cookie is valid. default: '1h'
        'gc_lifetime' => 60 * 60 * 24 //- Time in seconds which the created session is valid. default: '24h'
    ]
]
权限管理

作为认证处理器,我们还想检查用户是否有权限查看其请求的内容。

  • 检查请求的用户是否对当前路由有权限。
  • 如果用户没有权限查看其请求的内容,则将其重定向到引用页面。
  • 如果用户直接请求了一个没有权限且之前不在页面上的页面,则将其重定向到登录表单。
  • 如果用户有权限访问该页面,则从登录表单重定向到该页面。
  • 权限可以标记为"allowBypass",这授予用户与具有权限相同的权利,例如对于始终可访问但定义为回退的路由。
  • 如果用户对其当前路由没有权限且应重定向到另一个路由,则定义回退权限(路由)。
  • 您可以使用值为"*"(星号)定义权限,以授予组所有权限。

全局或本地config.php(位于config\autoload\)中的默认表定义

return [
    'tables' => [
        'user' => [
            'tableName' => 'users',
            'identifier' => 'loginId',
            'loginName' => 'username',
            'display' = 'hidden',
            'resetHash' => 'forgothash',
            'resetValid' => 'forgotvalid'
        ],
        'user_group_relation' => [
            'tableName' => 'user_has_groups',
            'identifier' => 'lhgId',
            'group_identifier' => 'groupId',
            'login_identifier' => 'loginId',
        ],
        'user_groups' => [
            'tableName' => 'user_groups',
            'identifier' => 'groupId',
            'name' => 'name',
            'display' = 'hidden'
        ],
        'user_permissions' => [
            'tableName' => 'user_permissions',
            'identifier' => 'permissionId',
            'name' => 'name',
            'value' => 'value',
            'noPermFallback' => 'noPermFallback',
            'allowBypass' => 'allowBypass',
            'display' = 'hidden'
        ],
        'user_group_permission_relation' => [
            'tableName' => 'user_group_has_permissions',
            'identifier' => 'ghpId',
            'permission_identifier' => 'permissionId',
            'group_identifier' => 'groupId',
        ],
    ]
]

如前所述,您可以定义权限回退,如果给定的权限未被授予,应重定向到其他位置。

权限不能授予给特定用户,而应授予给组,组可以授予给用户。用户可以有尽可能多的组,组可以有尽可能多的权限。

密码重置功能

由于您的应用程序可能需要重置密码的功能,我们在Handler\ForgotPasswordHandler中包含了一个基本的处理器。它使用基本的表单提交,并包含以下必要的输入字段:

  • 用户名(用于查找以该值作为用户名或电子邮件的用户账户)
  • 密码
  • 操作;可以是“提交”或“请求”,以便处理器知道它应该做什么。

密码重置处理器会向用户(如果存在)发送一封包含指向指定密码更改表单链接的电子邮件。这是一个基本的“请求”重置密码表单

<form id="resetPwForm" method="post">
	<input type="hidden" name="action" value="request"/>
	<div class="input-group mb-3">
		<input id="username" name="username" type="text" class="form-control" placeholder="Username or E-Mail">
		<div class="input-group-append">
			<div class="input-group-text">
				<span class="fas fa-envelope"></span>
			</div>
		</div>
	</div>
	
	div class="row mb-2">
		<div class="col-6">
		</div>			
		<div class="col-6">
			<button type="submit" class="btn btn-primary btn-block reset-password" data-target="{{ path(resetDestination) }}">Reset Password</button>
		</div>	
	</div>
	<div class="row">
		<div class="col-6">
		</div>
		<div class="col-6">
			<button type="button" class="btn btn-success btn-block to-login" >Back to Login</button>
		</div>
	</div>
</form>

它应该包含在您的“login.html.twig”中

	{{ include('@app/ForgotPassword.html.twig') }}

并使用变量“resetDestination”将密码重置请求发送到正确的处理器,如您的配置定义的那样

'loginHandling'  => [
    'adminLogin' => [
        'name'             => 'Admin',
        'destination'      => 'adminLanding',
        'resetDestination' => 'adminPasswordReset',
    ],
]

请注意:这仍然是在您的LoginRoute上,因此针对PasswordResetHandler的请求需要直接指向它。

在用户提交密码重置请求后;他会收到一封包含指向我们的PasswordResetHandler的链接的电子邮件,其中包含查询参数“hash”,该参数在提交请求后保存在用户账户中。我们还保存了该hash的有效截至日期,因为它必须在某一点上过期;默认配置下,我们使用30天。

一个基本的密码提交表单看起来像这样

<form method="post" id="savePwForm">
    <input type="hidden" name="action" value="submit"/>
	<div class="input-group mb-3">
		<input id="password" name="password" type="password" class="form-control" placeholder="Password">
		<div class="input-group-append">
			<div class="input-group-text">
				<span class="fas fa-lock"></span>
			</div>
		</div>
	</div>
	<div class="row">
		<div class="col-4"></div>
		<div class="col-4"></div>
		<div class="col-4">
			<button type="submit" class="btn btn-primary btn-block save-pw">Update</button>
		</div>
	</div>
	<div class="row">
		<div class="col-12">
			<div class="bg-gradient-success mt-3 set-sent text-center" style="display:none">
			    <p class="text-dark font-weight-bold mt-2">Your password was changed. You will receive an email.</p>
		    </div>
		    <div class="bg-gradient-danger mt-3 set-fail text-center" style="display:none">
			    <p class="text-light font-weight-bold mt-2">Your password couldn't be changed.</p>
		    </div>
		</div>
	</div>
</form>

由于我们的HTML模板使用了一些JavaScript,我们包含了所有可能有用的函数;您可以在js\basic.js中找到这些js,它基于JQuery,因此请确保在您的项目中包含了JQuery。

错误信息

如果Session-Auth-Middleware遇到任何问题,它会存储一个有效期为60秒的cookie

setcookie("error", $this->errorMessage, time() + 60, '/');

您可以使用该cookie接收错误信息并将其显示给用户。

致谢

本软件由MazeDEV/Marcel-Maqsood开发(https://github.com/marcel-maqsood)。

许可证

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