Title Text
LOGIN
Jeden login vládne všem
Symfony aplikacím
Jsem Tomáš :)
programátor
školitel
rodič
Apicart
Jak problém vznikl?
Nákupní košík
heslo
Přihlásit
- vlastní databáze
- vlastní aplikace
- vlastní logika
- vlastní subdoména
- samostatný produkt
- vlastní autentizace
Máme další produkt...
Vyhledávání
heslo
Přihlásit
- vlastní databáze
- vlastní aplikace
- vlastní logika
- vlastní subdoména
- samostatný produkt
- vlastní autentizace
Jaký je problém?
Nákupní košík
heslo
Přihlásit
Vyhledávání
heslo
Přihlásit
...
heslo
Přihlásit
Jaké je řešení?
Nákupní košík
Vyhledávání
...
Accounts
heslo
Přihlásit
Jak na to?
- zrefaktorujte autentizaci/registraci do nové aplikace
- vytvořte API pro získání informací o přihlášeném uživateli
- vytvořte si privátní PHP balíček pro composer
- zvolte si jedno centrální úložiště pro session
- nastavte si správně cookie domain
- implementujte redirecty z a do aplikací
- obrňte se trpělivostí... ;)
Redis pro sessions
framework:
session:
cookie_domain: .apicart.net
cookie_lifetime: 1209600 # 14 days
gc_maxlifetime: 1209600 # 14 days
cookie_samesite: lax
cookie_secure: auto
handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler
name: PHPSESSID
services:
SessionRedis:
class: Redis
calls:
- method: connect
arguments:
- '%env(SESSION_REDIS_HOST)%'
- '%env(int:SESSION_REDIS_PORT)%'
- method: select
arguments:
- '%env(int:SESSION_REDIS_DATABASE)%'
Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler:
arguments:
- '@SessionRedis'
Symfony security
security:
providers:
apicart_accounts_provider:
id: Apicart\Accounts\Security\UserProvider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
provider: apicart_accounts_provider
user_checker: Apicart\Accounts\Security\UserChecker
anonymous: ~
UserProvider
final class UserProvider implements UserProviderInterface
{
private $userApi;
public function __construct(UserApi $userApi)
{
$this->userApi = $userApi;
}
public function loadUserByUsername($username)
{
$user = $this->userApi->findByUsername($username);
if ($user === null) {
throw new UsernameNotFoundException;
}
return $user;
}
public function refreshUser(UserInterface $user)
{ /** ... **/ }
public function supportsClass($class)
{ /** ... **/ }
}
User
class User implements UserInterface, Serializable
{
//...
public function serialize(): string
{
return serialize([
$this->username,
$this->password,
$this->salt,
]);
}
public function unserialize($serialized): void
{
[
$this->username,
$this->password,
$this->salt,
] = unserialize($serialized, ['allowed_classes' => false]);
}
}
AbstractToken::hasUserChanged
private function hasUserChanged(UserInterface $user)
{
if ($this->user instanceof EquatableInterface) {
return !(bool) $this->user->isEqualTo($user);
}
if ($this->user->getPassword() !== $user->getPassword()) {
return true;
}
if ($this->user->getSalt() !== $user->getSalt()) {
return true;
}
if ($this->user->getUsername() !== $user->getUsername()) {
return true;
}
// AdvancedUserInterface is deprecated since Symfony 4.1
return false;
}
UserChecker
final class UserChecker implements UserCheckerInterface
{
public function checkPreAuth(UserInterface $user): void
{ /** ... **/ }
public function checkPostAuth(UserInterface $user): void
{
if (! $user instanceof User) {
return;
}
if (! $user->isActive()) {
throw new AccountDeactivatedException;
}
}
}
Redirect z Controlleru
trait AccountsRedirectTrait
{
private $host = 'https://accounts.apicart.net';
public function redirectToLogin(): RedirectResponse
{
$request = $this->getRequest();
return new RedirectResponse(
$this->host . '?backlink=' . $this->getBacklink($request)
);
}
public function redirectToLogout(): RedirectResponse
{
$request = $this->getRequest();
return new RedirectResponse(
$this->host . '/logout?backlink=' . $this->getBacklink($request)
);
}
private function getBacklink(Request $request): string
{
return $this->urlGenerator->generate(
$request->get('_route'),
$request->get('_route_params'),
UrlGeneratorInterface::ABSOLUTE_URL
);
}
}
Finální struktura
Nákupní košík
Vyhledávání
...
Accounts
heslo
Přihlásit
Shrnutí...
+ máme jednotný login do všech aplikací
− máme jednotný login do všech aplikací
Co dál?
- zbavit se sdílení session mezi aplikacemi
- implementovat dvoufázové ověření
- implementovat přepínání účtů
Díky za pozornost!
Máte otázky?
Apicart
Jeden login vládne všem (Symfony aplikacím)
By tomaspilar
Jeden login vládne všem (Symfony aplikacím)
- 734