Обновление (2020-04-07) : код решения на основе принятого ответа ниже строки.
Я создаю расширение для Typo3 v9.5 с использованием extbase, где я пытаюсь реализовать другую точку входа для входа в веб-интерфейс (например: одноразовый токен по электронной почте). Таким образом, форма входа в систему будет обойдена, и учетные данные будут извлечены из БД для входа пользователя через код. С учетом сказанного, я хочу повторно использовать как можно больше логина и логина сеанса c, который уже есть.
Я нашел приемлемое решение, которое внешне кажется работающим, но я не конечно, если это то, что будет продолжать работать через обновления ($GLOBALS['TSFE']->fe_user
преобразуется в Context API), и я уверен, что это не самый элегантный способ. В идеале я мог бы использовать это с небольшими изменениями в предстоящем Typo3 v10. В некоторых случаях я даже не уверен, что использую API, которые должны использоваться публично.
Позвольте мне изложить то, что я сделал до сих пор, самым компактным способом, который я могу себе представить:
<?php
# FILE: my_ext/Classes/Authentication/CustomAuthentication.php
use \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
class CustomAuthentication extends FrontendUserAuthentication {
// FrontendUserAuthentication seems to be hard-coded to gather credentials
// that were submitted via login form, so we have to work around that
protected $_formData = [];
// new method, set credentials normally entered via form
public function setLoginFormData(array $data) {
$this->_formData = $data;
}
// new method, set storage PID where user records are located, set via Extbase Controller/TS
public function setStoragePid(int $pid) {
$this->checkPid_value = $pid;
}
// override, ignore parent logic, simply return custom data
public function getLoginFormData() {
return $this->_formData;
}
}
<?php
# FILE: my_ext/Classes/Controller/SessionController.php
use \TYPO3\CMS\Core\Context\Context;
use \TYPO3\CMS\Core\Context\UserAspect;
use \TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use MyExt\MyVendor\Authentication\CustomAuthentication;
class SessionController extends ActionController {
// controller action
public function someAction() {
/*...*/
$loginData = [ /* assume this is retrieved from somewhere */ ];
$this->login($loginData);
/*...*/
}
// perform login
protected function login($data) {
$feAuth = $this->objectManager->get(CustomAuthentication::class);
// use my new methods to inject data
$feAuth->setLoginFormData($data);
$feAuth->setStoragePid($this->settings['pid']);
// the next part imitates what is going on in
// typo3/sysext/frontend/Classes/Middleware/FrontendUserAuthenticator.php
$feAuth->start();
$feAuth->unpack_uc(); // necessary?
$feAuth->fetchGroupData();
// login successful?
if(is_array($feAuth->user) && !empty($feAuth->groupData['uid']) {
$this->setGlobals($feAuth, $feAuth->groupData['uid']);
$feAuth->updateOnlineTimestamp(); // necessary?
}
}
// perform logout
protected function logout() {
//$feAuth = $GLOBALS['TSFE']->fe_user; // deprecated?
$feAuth = $this->objectManager->get(FrontendUserAuthentication::class);
// 'rehydrate' the pristine object from the existing session
$feAuth->start();
$feAuth->unpack_uc(); // necessary?
$feAuth->logoff();
$feAuth->start(); // create fresh session ID, so we can use flash messages and stuff
$this->setGlobals($feAuth);
}
protected function setGlobals(FrontendUserAuthentication $auth, array $grpData=[]) {
$GLOBALS['TSFE']->fe_user = $feAuth; // TODO remove in Typo3 v10?
$ctx = $this->objectManager->get(Context::class);
$ctx->setAspect('frontend.user', $this->objectManager->get(UserAspect::class, $feAuth, $groupData));
$feAuth->storeSessionData(); // necessary?
}
}
Я думаю, у меня возникнет вопрос: есть ли лучший способ сделать это или кто-нибудь, более знакомый с внутренностями Typo3, мог бы прокомментировать, если это действительно жизнеспособная возможность добиться того, что я хочу сделать с этим. Спасибо!
Обновление (2020-04-07):
Я последовал предложению из принятого ответа, и я публикую свой код, поэтому другие люди могут использовать его, если необходимо (в основном сокращенно).
Ниже приведен класс обслуживания, который будет обрабатывать проверку токена.
# FILE: my_ext/Classes/Service/TokenAuthenticationService.php
<?php
use \TYPO3\CMS\Core\Authentication\AbstractAuthenticationService;
use \TYPO3\CMS\Core\Authentication\LoginType;
use \TYPO3\CMS\Core\Database\ConnectionPool;
use \TYPO3\CMS\Core\Utility\GeneralUtility;
class TokenAuthenticationService extends AbstractAuthenticationService {
protected $timestamp_column = 'tstamp'; // last-changed timestamp
protected $usertoken_column = 'tx_myext_login_token';
public function getUser() {
if($this->login['status'] !== LoginType::LOGIN) {
return false;
}
if((string)$this->login['uname'] === '') {
$this->logger->warning('Attempted token login with empty token', ['remote'=>$this->authInfo['REMOTE_ADDR']]);
return false;
}
// fetch user record, make sure token was set at most 12 hours ago
$qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->db_user['table']);
$where_clause = $qb->expr()->andX(
$qb->expr()->eq($this->usertoken_column, $qb->expr()->literal($this->login['uname'])),
$qb->expr()->gte($this->timestamp_column, (int)strtotime('-12 hour'))
);
// Typo3 v10 API will change here!
$user = $this->fetchUserRecord('', $where_clause);
if(!is_array($user)) {
$this->logger->warning('Attempted token login with unknown or expired token', ['token'=>$this->login['uname']]);
return false;
} else {
$this->logger->info('Successful token found', ['id'=>$user['uid'], 'username'=>$user['username'], 'token'=>$this->login['uname']]);
}
return $user;
}
public function authUser(array $user): int {
// check, if the token that was submitted matches the one from the DB
if($this->login['uname'] === $user[$this->usertoken_column]) {
$this->logger->info('Successful token login', ['id'=>$user['uid'], 'username'=>$user['username'], 'token'=>$this->login['uname']]);
return 200;
}
return 100; // let other auth services try their luck
}
}
Затем зарегистрируйте Службу:
# FILE: my_ext/ext_localconf.php
// Add auth service for token login
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addService(
$_EXTKEY,
'auth',
\MyVendor\MyExt\Service\TokenAuthenticationService::class,
[
'title' => 'Token Auth',
'description' => 'Allows FE user login via one-time token',
'subtype' => 'getUserFE,authUserFE',
'available' => true,
'priority' => 60,
'quality' => 50,
'className' => \MyVendor\MyExt\Service\TokenAuthenticationService::class
]
);
При создании токена пользователь получает ссылку на страницу с добавленным параметром токена, например:
[...]/index.php?id=123&tx_myext_pi[action]=tokenAuth&tx_myext_pi[token]=whatever-was-stored-in-the-db
Поскольку нам нужно несколько Параметры для запуска промежуточного программного обеспечения для входа в систему, мы отображаем в основном скрытую форму на целевой странице, которая предлагает пользователю «подтвердить».
<!-- FILE: my_ext/Resources/Private/Templates/Some/TokenAuth.html -->
<f:if condition="{token}">
<h2>Token Login</h2>
<p>Please confirm</p>
<f:form action="doLogin" fieldNamePrefix="">
<f:form.hidden name="logintype" value="login" />
<f:form.hidden name="pid" value="{settings.membersPid}" />
<f:form.hidden name="user" value="{token}" />
<f:form.button>Confirm</f:form.button>
</f:form>
</f:if>
Этот запрос теперь будет автоматически выполнять вход со всеми необходимыми параметрами , В своем действии контроллера вы можете добавить какое-либо сообщение fla sh или перенаправить туда, где это имеет смысл.
# FILE: my_ext/Classes/Controller/SomeController.php
<?php
use \TYPO3\CMS\Core\Context\Context;
use \TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class SomeController extends ActionController {
public function doLoginAction() {
$ctx = $this->objectManager->get(Context::class);
if($ctx->getPropertyFromAspect('frontend.user', 'isLoggedIn')) {
// success
} else {
// failure
}
}
}