metarush / otp-auth
使用一次性密码(通过电子邮件令牌)进行身份验证的库
Requires
- php: >=7.1
- aura/session: ^2.1
- laminas/laminas-diactoros: 2.11.*
- metarush/data-mapper: ^1
- metarush/email-fallback: ^4
Requires (Dev)
- phpstan/phpstan: ^1.7
- phpunit/phpunit: ^9.5
- vlucas/phpdotenv: ^3.1
README
使用一次性密码(OTP)通过电子邮件进行用户认证和登录。
安装
使用 composer 以 metarush/otp-auth
的形式安装
数据库设置
除了您通常的 username
和/或 email
列外,在您的用户表中创建以下列。
otpHash
TEXT (255)otpToken
TEXT(12)otpExpire
INT (10)rememberHash
TEXT (255)rememberToken
TEXT (12)
注意:如果您想使用其他列名,只需在配置中设置即可。
示例用法
以下示例旨在以简单的方式展示库的功能。您不必按原样使用此实现。
_init.php
创建一个 _init.php
文件并将其包含在脚本顶部,放置以下内容。
初始化构建器
$builder = new MetaRush\OtpAuth\Builder;
定义 SMTP 服务器
$smtpServers = [ 0 => $builder->SmtpServer() ->setHost('host') ->setUser('user') ->setPass('pass') ->setPort(465) ->setEncr('TLS'), 1 => $builder->SmtpServer() ->setHost('host2') ->setUser('user') ->setPass('pass') ->setPort(465) ->setEncr('TLS'), ];
您可以添加任意多个,库将自动故障转移。
使用最小配置初始化库
$auth = $builder->setDsn('mysql:host=localhost;dbname=foo') ->setServers($smtpServers) ->setAdminEmails(['admin@example.com']) ->setAppName('foo') ->setFromEmail('noreply@example.com') ->setUsernameColumn('email') ->setNotificationFromEmail('noreply@example.com') ->setTable('users') ->build();
注意:如果您想扩展 Auth
类并继续使用构建器,您可以将您的子类作为字符串传递给 build()
参数。例如 ->build('MyAuth');
如果通过 cookie 记住了用户名,则自动登录
if (!$auth->authenticated()) { $rememberedUsername = $auth->rememberedUsername(); if (null !== $rememberedUsername) $auth->login($rememberedUsername); }
login.php
创建一个 login.php
文件并放置以下内容。
<?php include '_init.php'; if ($_POST) { $username = $_POST['email']; // check if username exists if (!$auth->userExist($username)) exit('User does not exist'); // remember username for next page (otp.php) setcookie('username', $username); // send OTP to user's email $otp = $auth->generateToken(5); $auth->sendOtp($otp, $username); // redirect to OTP page header('location: otp.php'); } ?> <?php if ($auth->authenticated()): ?> You are already logged-in <?php else: ?> <form method="post"> Email: <input type="text" name="email" /> </form> <?php endif; ?>
otp.php
创建一个 otp.php
文件并放置以下内容。
<?php include '_init.php'; if ($_POST) { $otp = $_POST['otp']; $username = $_COOKIE['username']; // remember username in browser if user wants to if (isset($_POST['remember'])) $auth->remember($username); // check if OTP is valid if (!$auth->validOtp($otp, $username)) exit('Invalid OTP'); // login username $auth->login($username); echo 'OTP is valid'; // redirect to your restricted page } ?> <?php if ($auth->authenticated()): ?> You are already logged-in <?php else: ?> <form method="post"> OTP: <input type="text" name="otp" /> <br /> <br /> Remember? <input type="checkbox" name="remember" /> <br /> <br /> <input type="submit" /> </form> <?php endif; ?>
logout.php
创建一个 logout.php
文件并放置以下内容。
<?php include '_init.php'; // destroy user session $auth->logout();
配置方法
您可以在 ->build();
方法之前使用以下方法在构建器对象中
setAdminEmails(array);
将获取错误通知的管理员电子邮件数组
setAppName(string);
错误通知上您的应用的标签
setBody(string);
OTP 电子邮件的正文。如果您设置此值,则必须包括模板变量 {OTP}
。
默认值: {OTP}\r\n\r\n注意:此 OTP 有效期为 5 分钟
setCharacterPool(string);
令牌从中派生的字符池
默认值: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
setCookiePrefix(string);
metarush/otp-auth
使用的 Cookie 名称前缀
默认值: MROA_
setCookiePath(?string);
metarush/otp-auth
使用的 Cookie 路径
默认值: /
setDbPass(string);
用户表的数据库密码
setDbUser(string);
用户表的数据库用户名
setDsn(string);
用于连接到您的用户表的 PDO DSN
setEmailColumn(string);
存储用户电子邮件的表列
默认值: email
setFromEmail(string);
OTP 消息的发件人电子邮件
setFromName(string);
OTP 消息的发件人姓名
setNotificationFromEmail(string);
如果您通过 setAdminEmail()
设置了管理员电子邮件,则必须设置错误通知的 From Email
setOtpExpire(int);
OTP 过期时间(分钟)。如果您设置此值,请确保通过 setBody(string);
更改电子邮件消息以反映 OTP 过期时间。
默认值: 5
setOtpExpireColumn(string);
存储 OTP 过期的表列
默认值: otpExpire
setOtpHashColumn(string);
存储 OTP 哈希的表列
默认值: otpHash
setOtpTokenColumn(string);
存储 OTP 令牌的表列
默认值: otpToken
setRememberCookieExpire(int);
"记住我" Cookie 过期时间(秒)
默认值: 2592000
(30 天)
setRememberHashColumn(string);
"记住我" 哈希的表列名
默认: rememberHash
setRememberTokenColumn(string);
用于查找“记住我”cookie的令牌的表列名
默认: rememberToken
setServers(array);
SmtpServer对象数组。参见上面的示例“定义SMTP服务器”
setSubject(string);
OTP电子邮件的主题
默认: 这是您的OTP
setTable(string);
用户名将进行身份验证的表
默认: users
setUsernameColumn(string);
存储用户名的表列名
默认: username
setUserIdColumn(string);
存储userId的表列名
默认: id
SMTP轮询模式
您可以使用轮询模式在发送OTP电子邮件时将负载分配到所有SMTP主机。
要启用轮询模式,您必须使用存储驱动程序来跟踪用于发送电子邮件的最后一个服务器。
可用的驱动程序及其配置
files
$driver = 'files'; $driverConfig = [ 'path' => '/var/www/example/emailFallbackCache/' ];
memcached
$driver = 'memcached'; $driverConfig = [ 'host' => '127.0.0.1', 'port' => 11211, 'saslUser' => '', 'saslPassword' => '' ];
注意:目前仅支持单个服务器/非分布式memcached。
redis
$driver = 'redis'; $driverConfig = [ 'host' => '127.0.0.1', 'port' => 6379, 'password' => '', 'database' => 0 ];
注意:如果可用,请使用memcached或redis,因为files不建议用于重负载。
在选择驱动程序后,在->build();
方法之前,在构建对象中设置以下内容
->setRoundRobinMode(true) ->setRoundRobinDriver($driver) ->setRoundRobinDriverConfig($driverConfig)
服务方法
authenticated(): bool
检查用户是否已认证
generateToken(int $length): string
$length
您想要生成的令牌长度。
生成随机令牌
login(string $username, ?array $userData = []): void
以认证用户登录
$username
要登录的用户名
$userData
您定义的任意用户数据,例如firstName,email(可选)
logout(): void
注销用户并删除“记住我”cookie
remember(string $username, int $howLong = null): void
在浏览器中记住用户名登录
$username
要记住的用户名
$howLong
以秒为单位记住用户的时间。默认值为30天,除非在配置中使用了setRememberCookieExpire()
。
rememberedUsername(?string $cookie = null): ?string
如果有的话,通过cookie获取记住的用户名。
$cookie
如果为null,则使用默认cookie。
sendOtp(string $otp, string $username, bool $useNextSmtpHost = false, int $testLastServerKey = null): void
通过电子邮件将OTP发送给用户
$otp
要发送给用户的OTP。您可以使用generateToken()
服务方法生成随机OTP。建议的OTP长度至少为8。
$username
要发送OTP的用户名
$useNextSmtpHost
如果您想在下次使用sendOtp()
时使用相对于当前用户的下一个可用的SMTP主机,请将其设置为true
。如果您上次发送的电子邮件到达缓慢,这很有用。例如,创建一个“重试”UI,然后使用sendOtp($otp, $username, true)
使用下一个SMTP主机发送新的OTP。
userData(): array
如果通过login()参数设置,则返回任意用户数据
userExist(string $username): bool
检查用户是否存在
$username
要检查的用户名
validOtp(string $otp, string $username, ?string $testOtpToken = null): bool
验证OTP
$otp
要验证的OTP
$username
与OTP关联的用户名
otpExpired(string $username): bool
检查OTP是否已过期
$username
与OTP关联的用户名
userId(string $username): int
返回$username
的userId
$username
获取userId的用户名
暴力保护
我们建议使用metarush/firewall库来实现登录暴力保护。