302 lines
7.0 KiB
Markdown
302 lines
7.0 KiB
Markdown
# Login & Authentifizierung
|
|
|
|
## Übersicht
|
|
|
|
myCRM verwendet **Symfony Security** mit form-based Login und optionaler "Remember Me" Funktionalität.
|
|
|
|
## Features
|
|
|
|
✅ **Email-basierter Login** - Benutzer melden sich mit ihrer Email-Adresse an
|
|
✅ **Sicheres Password Hashing** - Automatisch mit Symfony Password Hasher
|
|
✅ **Remember Me** - "Angemeldet bleiben" für 7 Tage
|
|
✅ **CSRF Protection** - Schutz vor Cross-Site Request Forgery
|
|
✅ **Automatische Weiterleitung** - Nach Login zum Dashboard
|
|
✅ **Last Login Tracking** - Speichert Zeitpunkt des letzten Logins
|
|
✅ **Inaktive User Blocking** - User mit `isActive = false` können sich nicht einloggen
|
|
✅ **Vue.js Integration** - User-Daten werden an Frontend übergeben
|
|
|
|
## URLs
|
|
|
|
| Route | Zweck |
|
|
|-------|-------|
|
|
| `/login` | Login-Seite |
|
|
| `/logout` | Logout (POST) |
|
|
| `/` | Dashboard (nach Login) |
|
|
|
|
## Test-Benutzer
|
|
|
|
Nach `doctrine:fixtures:load` verfügbar:
|
|
|
|
```
|
|
Administrator:
|
|
Email: admin@mycrm.local
|
|
Passwort: admin123
|
|
Rechte: Vollzugriff auf alle Module
|
|
|
|
Vertriebsmitarbeiter:
|
|
Email: sales@mycrm.local
|
|
Passwort: sales123
|
|
Rechte: Kontakte, Deals, Aktivitäten (ohne Löschrechte)
|
|
```
|
|
|
|
⚠️ **Wichtig:** Diese Passwörter nur für Development verwenden!
|
|
|
|
## Security Configuration
|
|
|
|
```yaml
|
|
# config/packages/security.yaml
|
|
security:
|
|
password_hashers:
|
|
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
|
|
|
|
providers:
|
|
app_user_provider:
|
|
entity:
|
|
class: App\Entity\User
|
|
property: email
|
|
|
|
firewalls:
|
|
main:
|
|
form_login:
|
|
login_path: app_login
|
|
check_path: app_login
|
|
default_target_path: /
|
|
logout:
|
|
path: app_logout
|
|
target: app_login
|
|
remember_me:
|
|
secret: '%kernel.secret%'
|
|
lifetime: 604800 # 1 week
|
|
|
|
access_control:
|
|
- { path: ^/login, roles: PUBLIC_ACCESS }
|
|
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
|
|
- { path: ^/, roles: ROLE_USER }
|
|
```
|
|
|
|
## Backend Usage
|
|
|
|
### Controller: Aktuellen User abrufen
|
|
|
|
```php
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
|
|
class MyController extends AbstractController
|
|
{
|
|
public function index(): Response
|
|
{
|
|
// Get current user
|
|
$user = $this->getUser();
|
|
|
|
if (!$user) {
|
|
throw $this->createAccessDeniedException();
|
|
}
|
|
|
|
// User properties
|
|
$email = $user->getEmail();
|
|
$fullName = $user->getFullName();
|
|
$isActive = $user->isActive();
|
|
$lastLogin = $user->getLastLoginAt();
|
|
|
|
// Check roles
|
|
if ($this->isGranted('ROLE_ADMIN')) {
|
|
// Admin stuff
|
|
}
|
|
|
|
return $this->render('template.html.twig', [
|
|
'user' => $user
|
|
]);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Service: User von Security Token
|
|
|
|
```php
|
|
use Symfony\Bundle\SecurityBundle\Security;
|
|
|
|
class MyService
|
|
{
|
|
public function __construct(
|
|
private Security $security
|
|
) {}
|
|
|
|
public function doSomething(): void
|
|
{
|
|
$user = $this->security->getUser();
|
|
|
|
if ($user instanceof \App\Entity\User) {
|
|
// Work with user
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Twig: User-Informationen
|
|
|
|
```twig
|
|
{% if app.user %}
|
|
<p>Angemeldet als: {{ app.user.fullName }}</p>
|
|
<p>Email: {{ app.user.email }}</p>
|
|
|
|
{% if is_granted('ROLE_ADMIN') %}
|
|
<a href="/admin">Admin-Bereich</a>
|
|
{% endif %}
|
|
{% else %}
|
|
<a href="{{ path('app_login') }}">Login</a>
|
|
{% endif %}
|
|
```
|
|
|
|
## Frontend Usage (Vue.js)
|
|
|
|
### Auth Store (Pinia)
|
|
|
|
```javascript
|
|
import { useAuthStore } from '@/stores/auth';
|
|
|
|
// In Component
|
|
const authStore = useAuthStore();
|
|
|
|
// Check authentication
|
|
if (authStore.isAuthenticated) {
|
|
console.log('User:', authStore.user);
|
|
console.log('Full Name:', authStore.fullName);
|
|
}
|
|
|
|
// Check roles
|
|
if (authStore.hasRole('ROLE_ADMIN')) {
|
|
// Show admin features
|
|
}
|
|
|
|
if (authStore.isAdmin) {
|
|
// Shortcut for ROLE_ADMIN check
|
|
}
|
|
|
|
// Logout
|
|
authStore.logout();
|
|
```
|
|
|
|
### In Vue Components
|
|
|
|
```vue
|
|
<template>
|
|
<div>
|
|
<p v-if="authStore.isAuthenticated">
|
|
Willkommen, {{ authStore.fullName }}!
|
|
</p>
|
|
|
|
<button @click="authStore.logout()">
|
|
Abmelden
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { useAuthStore } from '@/stores/auth';
|
|
|
|
const authStore = useAuthStore();
|
|
</script>
|
|
```
|
|
|
|
## Event Listeners
|
|
|
|
### LoginSuccessListener
|
|
|
|
Wird automatisch nach erfolgreichem Login ausgeführt:
|
|
|
|
```php
|
|
// src/EventListener/LoginSuccessListener.php
|
|
#[AsEventListener(event: LoginSuccessEvent::class)]
|
|
class LoginSuccessListener
|
|
{
|
|
public function __invoke(LoginSuccessEvent $event): void
|
|
{
|
|
$user = $event->getUser();
|
|
|
|
// Update last login timestamp
|
|
$user->setLastLoginAt(new \DateTimeImmutable());
|
|
$this->entityManager->flush();
|
|
}
|
|
}
|
|
```
|
|
|
|
Du kannst weitere Listener hinzufügen für:
|
|
- Audit Logging
|
|
- Login-Benachrichtigungen
|
|
- Session-Tracking
|
|
- etc.
|
|
|
|
## Password Management
|
|
|
|
### Passwort ändern
|
|
|
|
```php
|
|
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
|
|
|
public function changePassword(
|
|
User $user,
|
|
string $newPassword,
|
|
UserPasswordHasherInterface $passwordHasher
|
|
): void {
|
|
$hashedPassword = $passwordHasher->hashPassword($user, $newPassword);
|
|
$user->setPassword($hashedPassword);
|
|
$this->entityManager->flush();
|
|
}
|
|
```
|
|
|
|
### Neuen User erstellen
|
|
|
|
```php
|
|
$user = new User();
|
|
$user->setEmail('neuer@user.de');
|
|
$user->setFirstName('Max');
|
|
$user->setLastName('Mustermann');
|
|
$user->setIsActive(true);
|
|
$user->setRoles(['ROLE_USER']);
|
|
|
|
$hashedPassword = $passwordHasher->hashPassword($user, 'passwort123');
|
|
$user->setPassword($hashedPassword);
|
|
|
|
$entityManager->persist($user);
|
|
$entityManager->flush();
|
|
```
|
|
|
|
## Security Best Practices
|
|
|
|
1. ✅ **Password Hashing** - Automatisch mit bcrypt/argon2
|
|
2. ✅ **CSRF Protection** - Aktiviert für Login-Form
|
|
3. ✅ **Remember Me Cookie** - Sicher mit Secret Key
|
|
4. ✅ **Inactive User Check** - User mit `isActive = false` blockiert
|
|
5. ✅ **Access Control** - Alle Routen außer `/login` erfordern Authentication
|
|
6. ✅ **HTTPS Recommended** - In Production immer HTTPS verwenden
|
|
7. ✅ **Session Security** - Symfony Session-Handling
|
|
|
|
## Troubleshooting
|
|
|
|
### "Access Denied" nach Login
|
|
- Prüfe `User::getRoles()` - muss mindestens `['ROLE_USER']` zurückgeben
|
|
- Prüfe `access_control` in `security.yaml`
|
|
|
|
### "Bad credentials"
|
|
- Passwort falsch eingegeben
|
|
- User existiert nicht
|
|
- User ist inaktiv (`isActive = false`)
|
|
|
|
### Remember Me funktioniert nicht
|
|
- Secret Key in `.env` gesetzt?
|
|
- Cookie wird vom Browser blockiert?
|
|
- Lifetime abgelaufen?
|
|
|
|
### Logout funktioniert nicht
|
|
- Sicherstellen dass `/logout` als POST Route konfiguriert ist
|
|
- CSRF-Token prüfen
|
|
|
|
## Next Steps
|
|
|
|
- [ ] Password Reset Funktionalität
|
|
- [ ] Two-Factor Authentication (2FA)
|
|
- [ ] OAuth Integration (Google, Microsoft)
|
|
- [ ] Rate Limiting für Login-Versuche
|
|
- [ ] Login History/Audit Log
|
|
- [ ] Email-Verifizierung bei Registrierung
|