Typo3自定义FE登录(无表单)

我自己和我

更新(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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章