totp前端设计(1/2)
后端快了,别急
This commit is contained in:
parent
febc732e2d
commit
63bc49d63e
@ -354,21 +354,28 @@ class UserController extends Controller
|
|||||||
return $this->redirect(['user/info', 'focus' => 'password']);
|
return $this->redirect(['user/info', 'focus' => 'password']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function actionSetupTwoFactor()
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function actionSetupTwoFactor(): string
|
|
||||||
{
|
{
|
||||||
$user = Yii::$app->user->identity;
|
// ...其他代码...
|
||||||
$totp = TOTP::create();
|
|
||||||
$user->otp_secret = $totp->getSecret();
|
|
||||||
$user->is_otp_enabled = true;
|
|
||||||
$user->save(false);
|
|
||||||
|
|
||||||
$otpauth = $totp->getProvisioningUri($user->username);
|
// 生成恢复代码
|
||||||
$qrCodeUrl = 'https://api.qrserver.com/v1/create-qr-code/?data=' . urlencode($otpauth);
|
$recoveryCodes = $this->generateRecoveryCodes();
|
||||||
|
|
||||||
return $this->render('setup-two-factor', ['qrCodeUrl' => $qrCodeUrl]);
|
// 保存恢复代码到数据库或其他安全的地方
|
||||||
|
|
||||||
|
// 显示恢复代码给用户
|
||||||
|
Yii::$app->session->setFlash('success', '二步验证已启用。您的恢复代码是:' . implode(', ', $recoveryCodes));
|
||||||
|
|
||||||
|
// ...其他代码...
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateRecoveryCodes($length = 10, $numCodes = 10)
|
||||||
|
{
|
||||||
|
$codes = [];
|
||||||
|
for ($i = 0; $i < $numCodes; $i++) {
|
||||||
|
$codes[] = Yii::$app->security->generateRandomString($length);
|
||||||
|
}
|
||||||
|
return $codes;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ use yii\web\IdentityInterface;
|
|||||||
* @property int|null $is_encryption_enabled 启用加密
|
* @property int|null $is_encryption_enabled 启用加密
|
||||||
* @property int|null $is_otp_enabled 启用otp
|
* @property int|null $is_otp_enabled 启用otp
|
||||||
* @property int|null $storage_limit 存储容量限制,MB
|
* @property int|null $storage_limit 存储容量限制,MB
|
||||||
|
* @property string|null $recovery_codes OTP恢复代码
|
||||||
*
|
*
|
||||||
* @property CollectionTasks[] $collectionTasks
|
* @property CollectionTasks[] $collectionTasks
|
||||||
* @property Share[] $shares
|
* @property Share[] $shares
|
||||||
@ -39,6 +40,7 @@ class User extends ActiveRecord implements IdentityInterface
|
|||||||
public $oldPassword; // 旧密码 修改密码用
|
public $oldPassword; // 旧密码 修改密码用
|
||||||
public $newPassword; // 新密码 修改密码用
|
public $newPassword; // 新密码 修改密码用
|
||||||
public $newPasswordRepeat; // 重复新密码 修改密码用
|
public $newPasswordRepeat; // 重复新密码 修改密码用
|
||||||
|
public $totp_input; // otp用户输入值
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
@ -57,7 +59,7 @@ class User extends ActiveRecord implements IdentityInterface
|
|||||||
[['status', 'is_encryption_enabled', 'is_otp_enabled'], 'integer'],
|
[['status', 'is_encryption_enabled', 'is_otp_enabled'], 'integer'],
|
||||||
[['created_at', 'last_login'], 'safe'],
|
[['created_at', 'last_login'], 'safe'],
|
||||||
[['bio'], 'string'],
|
[['bio'], 'string'],
|
||||||
[['encryption_key', 'otp_secret'], 'string', 'max' => 255],
|
[['encryption_key', 'otp_secret','recovery_codes'], 'string', 'max' => 255],
|
||||||
[['last_login_ip'], 'string', 'max' => 45],
|
[['last_login_ip'], 'string', 'max' => 45],
|
||||||
[['username', 'password'], 'required', 'on' => 'login'],
|
[['username', 'password'], 'required', 'on' => 'login'],
|
||||||
[['username', 'password', 'email', 'password2'], 'required', 'on' => 'register'],
|
[['username', 'password', 'email', 'password2'], 'required', 'on' => 'register'],
|
||||||
@ -112,6 +114,8 @@ class User extends ActiveRecord implements IdentityInterface
|
|||||||
'is_encryption_enabled' => 'Is Encryption Enabled',
|
'is_encryption_enabled' => 'Is Encryption Enabled',
|
||||||
'is_otp_enabled' => 'Is Otp Enabled',
|
'is_otp_enabled' => 'Is Otp Enabled',
|
||||||
'storage_limit' => 'Storage Limit',
|
'storage_limit' => 'Storage Limit',
|
||||||
|
'recovery_codes' => 'Recovery Codes',
|
||||||
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
/* @var $totp_url string */
|
/* @var $totp_url string */
|
||||||
|
|
||||||
use app\assets\FontAwesomeAsset;
|
use app\assets\FontAwesomeAsset;
|
||||||
|
use app\models\User;
|
||||||
use app\utils\FileSizeHelper;
|
use app\utils\FileSizeHelper;
|
||||||
use app\utils\IPLocation;
|
use app\utils\IPLocation;
|
||||||
use Endroid\QrCode\Color\Color;
|
use Endroid\QrCode\Color\Color;
|
||||||
@ -56,6 +57,9 @@ if(!is_null($totp_secret)){
|
|||||||
->setBackgroundColor(new Color(255, 255, 255));
|
->setBackgroundColor(new Color(255, 255, 255));
|
||||||
$result = $writer->write($qrCode);
|
$result = $writer->write($qrCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// totp
|
||||||
|
$user = new User();
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
@ -235,11 +239,13 @@ if(!is_null($totp_secret)){
|
|||||||
</h5>
|
</h5>
|
||||||
<div>
|
<div>
|
||||||
<div class="form-check form-switch">
|
<div class="form-check form-switch">
|
||||||
<input class="form-check-input" type="checkbox" role="switch" id="totp-enabled">
|
<input class="form-check-input" type="checkbox" role="switch" id="totp-enabled"
|
||||||
<label class="form-check-label" for="totp-enabled">启用 TOTP</label>
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#totpSetupModal">
|
||||||
|
<label class="form-check-label" for="totp-enabled" data-bs-toggle="modal"
|
||||||
|
data-bs-target="#totpSetupModal">启用 TOTP</label>
|
||||||
</div>
|
</div>
|
||||||
<!--暂时放在这里-->
|
<!--暂时放在这里-->
|
||||||
<img src="<?= is_null($totp_secret)?'':$result->getDataUri() ?>" alt="qrcode"/>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
@ -307,6 +313,51 @@ echo '</div>';
|
|||||||
|
|
||||||
echo Html::endForm();
|
echo Html::endForm();
|
||||||
|
|
||||||
|
Modal::end();
|
||||||
|
|
||||||
|
Modal::begin([
|
||||||
|
'title' => '<h4>需要进一步操作以启用二步验证</h4>',
|
||||||
|
'id' => 'totpSetupModal',
|
||||||
|
'size' => 'model-xl',
|
||||||
|
]);
|
||||||
|
/*<img src="<?= is_null($totp_secret) ? '' : $result->getDataUri() ?>" alt="qrcode"/>*/
|
||||||
|
?>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 text-center center">
|
||||||
|
<img src="<?= is_null($totp_secret) ? '' : $result->getDataUri() ?>" alt="QR Code" class="img-fluid">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<p>使用兼容TOTP的应用程序扫描左侧二维码以添加二步验证</p>
|
||||||
|
<p>推荐以下二步验证器::</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en_US">Google
|
||||||
|
Authenticator</a></li>
|
||||||
|
<li><a href="https://play.google.com/store/apps/details?id=com.azure.authenticator&hl=en_US">Microsoft Authenticator</a></li>
|
||||||
|
<li><a href="https://play.google.com/store/apps/details?id=com.authy.authy&hl=en">Authy</a></li>
|
||||||
|
<!-- Add more applications as needed -->
|
||||||
|
</ul>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<label for="totp_secret">无法扫描?使用下面的密钥来添加</label>
|
||||||
|
<input type="text" class="form-control" value="<?= $totp_secret ?>" id="totp_secret" readonly>
|
||||||
|
<button class="btn btn-outline-secondary" type="button"
|
||||||
|
onclick="navigator.clipboard.writeText('<?= $totp_secret ?>')">Copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<?php $form = ActiveForm::begin([
|
||||||
|
'action' => ['user/actionSetupTwoFactor'],
|
||||||
|
'method' => 'post'
|
||||||
|
]); ?>
|
||||||
|
|
||||||
|
<?= Html::activeHiddenInput($user, 'totp_secret', ['value' => $totp_secret]) ?>
|
||||||
|
<?= $form->field($user, 'totp_input')->textInput()->label('最后一步! 输入TOTP应用程序上显示的密码以启用二步验证') ?>
|
||||||
|
<?= Html::submitButton('启用二步验证', ['class' => 'btn btn-primary']) ?>
|
||||||
|
|
||||||
|
<?php ActiveForm::end(); ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
|
||||||
Modal::end();
|
Modal::end();
|
||||||
$this->registerJsFile('@web/js/user-info.js', ['depends' => [JqueryAsset::class], 'position' => View::POS_END]);
|
$this->registerJsFile('@web/js/user-info.js', ['depends' => [JqueryAsset::class], 'position' => View::POS_END]);
|
||||||
?>
|
?>
|
Loading…
Reference in New Issue
Block a user