From c3902ffc2f2345af1a3063781bb964b06803bf56 Mon Sep 17 00:00:00 2001 From: jleroy2023 Date: Wed, 16 Jul 2025 11:35:28 +0200 Subject: [PATCH] spring security config v1 --- build.gradle | 6 ++ src/main/java/fr/eni/demo/bll/JwtService.java | 40 ++++++++++++ src/main/java/fr/eni/demo/bo/User.java | 25 ++++++++ .../demo/controller/AddressController.java | 3 - .../controller/AuthenticationController.java | 43 +++++++++++++ .../demo/controller/FactureController.java | 1 - .../demo/controller/GameTypeController.java | 1 - .../demo/controller/LocationController.java | 2 - .../fr/eni/demo/security/JwtAuthFilter.java | 50 +++++++++++++++ .../fr/eni/demo/security/SecurityConfig.java | 63 +++++++++++++++++++ 10 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 src/main/java/fr/eni/demo/bll/JwtService.java create mode 100644 src/main/java/fr/eni/demo/bo/User.java create mode 100644 src/main/java/fr/eni/demo/controller/AuthenticationController.java create mode 100644 src/main/java/fr/eni/demo/security/JwtAuthFilter.java create mode 100644 src/main/java/fr/eni/demo/security/SecurityConfig.java diff --git a/build.gradle b/build.gradle index 5c180c6..fb6c93c 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,12 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation 'org.springframework.security:spring-security-test' + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' diff --git a/src/main/java/fr/eni/demo/bll/JwtService.java b/src/main/java/fr/eni/demo/bll/JwtService.java new file mode 100644 index 0000000..c739dff --- /dev/null +++ b/src/main/java/fr/eni/demo/bll/JwtService.java @@ -0,0 +1,40 @@ +package fr.eni.demo.bll; + +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import org.springframework.stereotype.Service; + +import java.security.Key; +import java.util.Date; + +@Service +public class JwtService { + + private static final long EXPIRATION_TIME = 86400000; // 1j + private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); + + public String generateToken(String username) { + return Jwts.builder() + .setSubject(username) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) + .signWith(key) + .compact(); + } + + public String extractUsername(String token) { + return Jwts.parserBuilder().setSigningKey(key).build() + .parseClaimsJws(token).getBody().getSubject(); + } + + public boolean isTokenValid(String token) { + try { + Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); + return true; + } catch (JwtException e) { + return false; + } + } +} diff --git a/src/main/java/fr/eni/demo/bo/User.java b/src/main/java/fr/eni/demo/bo/User.java new file mode 100644 index 0000000..df73ac9 --- /dev/null +++ b/src/main/java/fr/eni/demo/bo/User.java @@ -0,0 +1,25 @@ +package fr.eni.demo.bo; + +import lombok.*; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Builder +@Document(collection = "stock") +public class User { + + @Id + private String id; + + @Field(name = "USERNAME") + private String username; + + @Field(name = "PASSWORD") + private String pasword; + +} diff --git a/src/main/java/fr/eni/demo/controller/AddressController.java b/src/main/java/fr/eni/demo/controller/AddressController.java index bc02805..0a7d52d 100644 --- a/src/main/java/fr/eni/demo/controller/AddressController.java +++ b/src/main/java/fr/eni/demo/controller/AddressController.java @@ -1,16 +1,13 @@ package fr.eni.demo.controller; import fr.eni.demo.bll.AdresseService; -import fr.eni.demo.bll.StockService; import fr.eni.demo.bo.Adresse; -import fr.eni.demo.bo.Stock; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; -import java.util.Optional; @RestController @RequestMapping("/api/address") diff --git a/src/main/java/fr/eni/demo/controller/AuthenticationController.java b/src/main/java/fr/eni/demo/controller/AuthenticationController.java new file mode 100644 index 0000000..1e1754a --- /dev/null +++ b/src/main/java/fr/eni/demo/controller/AuthenticationController.java @@ -0,0 +1,43 @@ +package fr.eni.demo.controller; + +import fr.eni.demo.bll.JwtService; +import fr.eni.demo.bo.User; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/auth") +public class AuthenticationController { + + private final AuthenticationManager authManager; + private final JwtService jwtService; + + public AuthenticationController(AuthenticationManager authManager, JwtService jwtService) { + this.authManager = authManager; + this.jwtService = jwtService; + } + + @PostMapping("/login") + public ResponseEntity login(@RequestBody User user) { + try { + Authentication auth = authManager.authenticate( + new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPasword()) + ); + + String token = jwtService.generateToken(user.getUsername()); + return ResponseEntity.ok(token); + } catch (AuthenticationException e) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + } +} \ No newline at end of file diff --git a/src/main/java/fr/eni/demo/controller/FactureController.java b/src/main/java/fr/eni/demo/controller/FactureController.java index cc90bb7..264e351 100644 --- a/src/main/java/fr/eni/demo/controller/FactureController.java +++ b/src/main/java/fr/eni/demo/controller/FactureController.java @@ -2,7 +2,6 @@ package fr.eni.demo.controller; import fr.eni.demo.bll.FactureService; import fr.eni.demo.bo.Facture; -import fr.eni.demo.bo.Location; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/fr/eni/demo/controller/GameTypeController.java b/src/main/java/fr/eni/demo/controller/GameTypeController.java index 90e496f..a8e1f3b 100644 --- a/src/main/java/fr/eni/demo/controller/GameTypeController.java +++ b/src/main/java/fr/eni/demo/controller/GameTypeController.java @@ -2,7 +2,6 @@ package fr.eni.demo.controller; import fr.eni.demo.bll.GameTypeService; import fr.eni.demo.bo.GameType; -import fr.eni.demo.bo.Stock; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; diff --git a/src/main/java/fr/eni/demo/controller/LocationController.java b/src/main/java/fr/eni/demo/controller/LocationController.java index 8e5b0b9..bdb51db 100644 --- a/src/main/java/fr/eni/demo/controller/LocationController.java +++ b/src/main/java/fr/eni/demo/controller/LocationController.java @@ -1,8 +1,6 @@ package fr.eni.demo.controller; -import fr.eni.demo.bll.ClientService; import fr.eni.demo.bll.LocationService; -import fr.eni.demo.bo.Client; import fr.eni.demo.bo.Location; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/fr/eni/demo/security/JwtAuthFilter.java b/src/main/java/fr/eni/demo/security/JwtAuthFilter.java new file mode 100644 index 0000000..7374cb7 --- /dev/null +++ b/src/main/java/fr/eni/demo/security/JwtAuthFilter.java @@ -0,0 +1,50 @@ +package fr.eni.demo.security; + +import fr.eni.demo.bll.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.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +public class JwtAuthFilter extends OncePerRequestFilter { + + private final JwtService jwtService; + private final UserDetailsService userDetailsService; + + public JwtAuthFilter(JwtService jwtService, UserDetailsService userDetailsService) { + this.jwtService = jwtService; + this.userDetailsService = userDetailsService; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws IOException, ServletException { + + final String authHeader = request.getHeader("Authorization"); + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + chain.doFilter(request, response); + return; + } + + String token = authHeader.substring(7); + String username = jwtService.extractUsername(token); + + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + if (jwtService.isTokenValid(token)) { + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authToken); + } + } + + chain.doFilter(request, response); + } +} diff --git a/src/main/java/fr/eni/demo/security/SecurityConfig.java b/src/main/java/fr/eni/demo/security/SecurityConfig.java new file mode 100644 index 0000000..5fbec1a --- /dev/null +++ b/src/main/java/fr/eni/demo/security/SecurityConfig.java @@ -0,0 +1,63 @@ +package fr.eni.demo.security; + +import fr.eni.demo.bll.JwtService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + private final JwtService jwtService; + + public SecurityConfig(JwtService jwtService) { + this.jwtService = jwtService; + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/auth").permitAll() + .requestMatchers("/user/**").hasAnyRole("ADMIN") + .anyRequest().denyAll() + ) + .formLogin(Customizer.withDefaults()); + + return http.build(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { + return config.getAuthenticationManager(); + } + + @Bean + public PasswordEncoder encoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public UserDetailsService userDetailsService(PasswordEncoder encoder) { + UserDetails user = User.builder() + .username("user") + .password(encoder.encode("password")) + .roles("USER") + .build(); + + return new InMemoryUserDetailsManager(user); + } + +}