Compare commits

..

3 Commits

Author SHA1 Message Date
9d9d6d8a60 Funktionierende SecurityConfig 2025-03-18 15:52:06 +01:00
be982193e4 Password hashing und jft 2025-02-28 21:25:24 +01:00
75644d012d Password hashing und jft 2025-02-28 21:24:59 +01:00
18 changed files with 268 additions and 21 deletions

45
pom.xml
View File

@ -55,6 +55,51 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Security für Passwort-Hashing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JJWT API -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<!-- JJWT Implementierung (zur Laufzeit) -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- JJWT Jackson für JSON-Verarbeitung (zur Laufzeit) -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot Validation für Request-Validierung -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Lombok (Optional, aber erleichtert die Code-Generierung) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,45 @@
package com.example.hangry.controller;
import com.example.hangry.User;
import com.example.hangry.services.UserService;
import com.example.hangry.services.JwtService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
@CrossOrigin (origins = "*")
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final UserService userService;
private final JwtService jwtService;
public AuthController(UserService userService, JwtService jwtService) {
this.userService = userService;
this.jwtService = jwtService;
}
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody User user) {
if (userService.getUserByEmail(user.getEmail()) != null) {
return ResponseEntity.badRequest().body("Email bereits vergeben!");
}
if (userService.getUserByUsername(user.getUsername()) != null) {
return ResponseEntity.badRequest().body("Username bereits vergeben!");
}
User newUser = userService.createUser(user);
return ResponseEntity.ok(newUser);
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody User loginRequest) {
User user = userService.getUserByEmail(loginRequest.getEmail());
if (user != null && userService.checkPassword(loginRequest.getPassword(), user.getPassword())) {
String token = jwtService.generateToken(user); // JWT erstellen
return ResponseEntity.ok(Collections.singletonMap("token",token)); // Token zurückgeben
}
return ResponseEntity.status(401).body(Collections.singletonMap("error","Login Fehlgeschlagen!"));
}
}

View File

@ -1,9 +1,10 @@
package com.example.hangry;
package com.example.hangry.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Set;
import com.example.hangry.*;
import com.example.hangry.services.GroupService;
@RestController
@RequestMapping("/api/groups")
@CrossOrigin(origins = "*")

View File

@ -1,4 +1,4 @@
package com.example.hangry;
package com.example.hangry.controller;
@ -7,6 +7,8 @@ import org.springframework.web.bind.annotation.*;
import java.util.Set;
import com.example.hangry.services.GroupRecipeService;
import com.example.hangry.*;
@RestController
@RequestMapping("/api/group-recipes")

View File

@ -1,10 +1,12 @@
package com.example.hangry;
package com.example.hangry.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import com.example.hangry.*;
import com.example.hangry.services.IngredientService;
@RestController
@RequestMapping("/api/ingredients")

View File

@ -1,9 +1,10 @@
package com.example.hangry;
package com.example.hangry.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import com.example.hangry.*;
import com.example.hangry.services.LikeService;
@RestController
@RequestMapping("/api/likes")
@CrossOrigin(origins = "*")

View File

@ -1,4 +1,4 @@
package com.example.hangry;
package com.example.hangry.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -7,6 +7,8 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.Optional;
import com.example.hangry.*;
import com.example.hangry.services.RecipeService;
@RestController
@RequestMapping("/api/recipes")
@CrossOrigin(origins = "*") // Erlaubt CORS für alle Domains, kann angepasst werden

View File

@ -1,8 +1,9 @@
package com.example.hangry;
package com.example.hangry.controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import com.example.hangry.*;
import com.example.hangry.services.UserService;
@RestController
@RequestMapping("/users")
public class UserController {

View File

@ -0,0 +1,52 @@
package com.example.hangry.security;
import com.example.hangry.User;
import com.example.hangry.UserRepository;
import com.example.hangry.services.JwtService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.ArrayList;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserRepository userRepository;
public JwtAuthenticationFilter(JwtService jwtService, UserRepository userRepository) {
this.jwtService = jwtService;
this.userRepository = userRepository;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
String email = jwtService.extractEmail(token);
if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) {
User user = userRepository.findByEmail(email);
if (user != null && jwtService.validateToken(token, user)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
}
}

View File

@ -0,0 +1,30 @@
package com.example.hangry.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.web.SecurityFilterChain;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity // Aktiviert Spring Security
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
http
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.csrf(AbstractHttpConfigurer::disable) // Neue Syntax für CSRF-Disable
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/register", "/api/auth/login").permitAll() // Registrierung & Login erlauben
.anyRequest().authenticated() // Alles andere erfordert Authentifizierung
)
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // JWT: Keine Session
return http.build();
}
}

View File

@ -1,9 +1,9 @@
package com.example.hangry;
package com.example.hangry.services;
import org.springframework.stereotype.Service;
import java.util.Set;
import java.util.stream.Collectors;
import com.example.hangry.*;
@Service
public class GroupRecipeService {

View File

@ -1,9 +1,8 @@
package com.example.hangry;
package com.example.hangry.services;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.Set;
import com.example.hangry.*;
@Service
public class GroupService {

View File

@ -1,9 +1,9 @@
package com.example.hangry;
package com.example.hangry.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import com.example.hangry.*;
@Service
public class IngredientService {

View File

@ -0,0 +1,53 @@
package com.example.hangry.services;
import com.example.hangry.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.cglib.core.internal.Function;
import org.springframework.stereotype.Service;
import java.security.Key;
import java.util.Date;
@Service
public class JwtService {
private static final String SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256).toString();
private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 24; // 24 Stunden
public String generateToken(User user) {
return Jwts.builder()
.setSubject(user.getEmail())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
}
private Key getSigningKey() {
return Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
}
public String extractEmail(String token) {
return extractClaim(token, Claims::getSubject); // Subject = E-Mail
}
public boolean validateToken(String token, User user) {
final String email = extractEmail(token);
return email.equals(user.getEmail()) && !isTokenExpired(token);
}
private boolean isTokenExpired(String token) {
return extractClaim(token, Claims::getExpiration).before(new Date());
}
}

View File

@ -1,8 +1,9 @@
package com.example.hangry;
package com.example.hangry.services;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import com.example.hangry.*;
@Service
public class LikeService {

View File

@ -1,11 +1,11 @@
package com.example.hangry;
package com.example.hangry.services;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.Optional;
import com.example.hangry.*;
@Service
public class RecipeService {

View File

@ -1,19 +1,25 @@
package com.example.hangry;
package com.example.hangry.services;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.example.hangry.*;
@Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
// Dependency Injection (Spring kümmert sich darum)
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
this.passwordEncoder = new BCryptPasswordEncoder(); // Initialisierung von BCrypt
}
// Einen neuen Benutzer erstellen
// Neuen Benutzer erstellen (mit Passwort-Hashing)
public User createUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword())); // Passwort hashen
return userRepository.save(user);
}
@ -26,4 +32,9 @@ public class UserService {
public User getUserByEmail(String email) {
return userRepository.findByEmail(email);
}
// Passwort überprüfen
public boolean checkPassword(String rawPassword, String hashedPassword) {
return passwordEncoder.matches(rawPassword, hashedPassword);
}
}

View File

@ -10,3 +10,5 @@ spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.hibernate.ddl-auto=update
server.port=8080
server.address=0.0.0.0