更新(2020-04-07):基于以下行的可接受答案的解决方案代码。
我正在使用extbase构建Typo3 v9.5的扩展,在该扩展中,我试图实现用于登录前端用户的其他入口点(请考虑:通过电子邮件的一次性令牌)。因此,将绕过登录表单,并从数据库检索凭据以通过代码登录用户。话虽如此,我想尽可能多地重用已经存在的登录和会话逻辑。
我发现了一个表面上似乎可行的可行解决方案,但是我不确定这是否可以在所有更新($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
]
);
创建令牌时,用户会获得带有添加的token参数的页面链接,例如:
[...]/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>
现在,该请求将使用所有正确的参数自动执行登录。然后,在控制器操作中,您可以添加某种即时消息或重定向到有意义的任何地方。
# 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
}
}
}
您应该实现一个身份验证服务,该服务仅需要实现“ authUser()”函数。因此,其他所有事情都可能由核心身份验证处理。
详情请参阅此处https://docs.typo3.org/m/typo3/reference-services/8.7/en-us/Authentication/Index.html
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句