finished login
This commit is contained in:
parent
e25bae9700
commit
9bb354a805
@ -1,5 +1,6 @@
|
||||
package at.mueller.eeg.backend.iam.config;
|
||||
|
||||
import at.mueller.eeg.backend.iam.repository.UserRepository;
|
||||
import com.nimbusds.jose.jwk.source.ImmutableSecret;
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@ -12,6 +13,8 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
@ -143,4 +146,10 @@ public class SecurityConfig {
|
||||
SecretKeySpec secretKey = new SecretKeySpec(jwtSecret.getBytes(), "HmacSHA256");
|
||||
return new NimbusJwtEncoder(new ImmutableSecret<>(secretKey));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserDetailsService userDetailsService(UserRepository userRepository) {
|
||||
return username -> userRepository.findByEmail(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User mit E-Mail " + username + " nicht gefunden."));
|
||||
}
|
||||
}
|
||||
|
||||
46
eeg_frontend/src/app/layout/header/header.html
Normal file
46
eeg_frontend/src/app/layout/header/header.html
Normal file
@ -0,0 +1,46 @@
|
||||
<nav class="bg-white border-b border-gray-200 sticky top-0 z-50 shadow-sm">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between items-center h-16">
|
||||
|
||||
<div class="flex items-center">
|
||||
<a routerLink="/" class="flex items-center space-x-2 group">
|
||||
<svg class="h-8 w-8 text-emerald-600 group-hover:text-emerald-700 transition" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
<span class="font-bold text-xl text-gray-900 tracking-tight">
|
||||
eeg<span class="text-emerald-600">portal</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="hidden md:flex absolute left-1/2 transform -translate-x-1/2">
|
||||
<nav class="flex gap-6">
|
||||
<a href="#vorteile" class="text-slate-600 hover:text-slate-900 font-medium">Vorteile</a>
|
||||
<a href="#so-gehts" class="text-slate-600 hover:text-slate-900 font-medium">So funktioniert's</a>
|
||||
<a href="#faq" class="text-slate-600 hover:text-slate-900 font-medium">FAQ</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
@if (authService.isLoggedIn$ | async) {
|
||||
|
||||
<span class="text-sm text-gray-500 hidden sm:inline">
|
||||
Rolle: <span class="font-semibold text-gray-700">{{ authService.getUserRole() }}</span>
|
||||
</span>
|
||||
<button (click)="onLogout()" class="text-sm font-medium text-red-600 hover:text-red-700 border border-red-200 hover:border-red-300 px-3 py-1.5 rounded-md transition duration-150">
|
||||
Abmelden
|
||||
</button>
|
||||
|
||||
} @else {
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<button mat-button class="hidden sm:inline-flex" routerLink="/login">Login</button>
|
||||
<button mat-flat-button color="primary" routerLink="/register">Mitglied werden</button>
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
0
eeg_frontend/src/app/layout/header/header.scss
Normal file
0
eeg_frontend/src/app/layout/header/header.scss
Normal file
22
eeg_frontend/src/app/layout/header/header.spec.ts
Normal file
22
eeg_frontend/src/app/layout/header/header.spec.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Header } from './header';
|
||||
|
||||
describe('Header', () => {
|
||||
let component: Header;
|
||||
let fixture: ComponentFixture<Header>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Header],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(Header);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
27
eeg_frontend/src/app/layout/header/header.ts
Normal file
27
eeg_frontend/src/app/layout/header/header.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {Router, RouterLink} from '@angular/router';
|
||||
import {AuthService} from '../../services/auth';
|
||||
import {AsyncPipe} from '@angular/common';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
imports: [
|
||||
RouterLink,
|
||||
AsyncPipe,
|
||||
MatButton
|
||||
],
|
||||
templateUrl: './header.html'
|
||||
})
|
||||
export class HeaderComponent {
|
||||
|
||||
constructor(
|
||||
public authService: AuthService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
onLogout() {
|
||||
this.authService.logout();
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
}
|
||||
47
eeg_frontend/src/app/pages/login/login.html
Normal file
47
eeg_frontend/src/app/pages/login/login.html
Normal file
@ -0,0 +1,47 @@
|
||||
<div class="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-md w-full space-y-8 bg-white p-8 rounded-xl shadow-lg">
|
||||
<div>
|
||||
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
|
||||
In Ihr EEG-Portal einloggen
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div *ngIf="errorMessage" class="bg-red-50 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
|
||||
<span class="block sm:inline text-sm">{{ errorMessage }}</span>
|
||||
</div>
|
||||
|
||||
<form class="mt-8 space-y-6" [formGroup]="loginForm" (ngSubmit)="onSubmit()">
|
||||
<div class="rounded-md shadow-sm -space-y-px">
|
||||
<div class="mb-4">
|
||||
<label for="username" class="block text-sm font-medium text-gray-700 mb-1">Benutzername oder E-Mail</label>
|
||||
<input
|
||||
id="username"
|
||||
formControlName="email"
|
||||
type="text"
|
||||
required
|
||||
class="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-emerald-500 focus:border-emerald-500 sm:text-sm"
|
||||
placeholder="ihr.name@beispiel.at">
|
||||
</div>
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Passwort</label>
|
||||
<input
|
||||
id="password"
|
||||
formControlName="password"
|
||||
type="password"
|
||||
required
|
||||
class="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-emerald-500 focus:border-emerald-500 sm:text-sm"
|
||||
placeholder="********">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
[disabled]="loginForm.invalid"
|
||||
class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-emerald-600 hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-emerald-500 transition duration-150 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
Anmelden
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
0
eeg_frontend/src/app/pages/login/login.scss
Normal file
0
eeg_frontend/src/app/pages/login/login.scss
Normal file
22
eeg_frontend/src/app/pages/login/login.spec.ts
Normal file
22
eeg_frontend/src/app/pages/login/login.spec.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Login } from './login';
|
||||
|
||||
describe('Login', () => {
|
||||
let component: Login;
|
||||
let fixture: ComponentFixture<Login>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Login],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(Login);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
56
eeg_frontend/src/app/pages/login/login.ts
Normal file
56
eeg_frontend/src/app/pages/login/login.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {AuthService} from '../../services/auth';
|
||||
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {CommonModule} from '@angular/common'; // 1. Hier importieren
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
standalone: true,
|
||||
imports: [CommonModule, ReactiveFormsModule],
|
||||
templateUrl: './login.html'
|
||||
})
|
||||
export class LoginComponent {
|
||||
loginForm: FormGroup;
|
||||
errorMessage: string = '';
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private authService: AuthService,
|
||||
private router: Router
|
||||
) {
|
||||
this.loginForm = this.fb.group({
|
||||
email: ['', [Validators.required]],
|
||||
password: ['', [Validators.required]]
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.loginForm.invalid) return;
|
||||
|
||||
this.authService.login(this.loginForm.value).subscribe({
|
||||
next: (response) => {
|
||||
// Hier passiert die bedingte Weiterleitung anhand der Rolle
|
||||
this.redirectBasedOnRole(response.role!);
|
||||
},
|
||||
error: (err) => {
|
||||
this.errorMessage = err.error?.message || 'Ein unbekannter Fehler ist aufgetreten.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private redirectBasedOnRole(role: string) {
|
||||
switch (role) {
|
||||
case 'ADMIN':
|
||||
void this.router.navigate(['/admin/approvals']);
|
||||
break;
|
||||
case 'MEMBER':
|
||||
void this.router.navigate(['/dashboard']);
|
||||
break;
|
||||
default:
|
||||
// Fallback, falls die Rolle unbekannt ist
|
||||
void this.router.navigate(['/']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user