From 0f4de8f859c80d7388a84b3c3dd5eca366c1c6fd Mon Sep 17 00:00:00 2001 From: Romain Mallard Date: Wed, 10 Jun 2026 17:07:42 +0200 Subject: [PATCH] FEAT: simple JWT | hardcoded usrname/password --- data/hotel.sqlite | Bin 4927488 -> 4927488 bytes data/hotel.sqlite-shm | Bin 32768 -> 0 bytes data/hotel.sqlite-wal | Bin 37112 -> 0 bytes pom.xml | 26 ++++++ .../mallardromain/hotel/DTO/LoginRequest.java | 23 ++++++ .../hotel/config/SecurityConfig.java | 42 ++++++++++ .../hotel/controller/AuthController.java | 54 +++++++++++++ .../hotel/controller/RoomController.java | 2 +- .../com/mallardromain/hotel/model/JWT.java | 6 ++ .../com/mallardromain/hotel/model/Room.java | 3 +- .../hotel/security/JWTFilter.java | 75 ++++++++++++++++++ .../hotel/service/JWTService.java | 58 ++++++++++++++ .../{repository => service}/RoomService.java | 4 +- 13 files changed, 290 insertions(+), 3 deletions(-) delete mode 100644 data/hotel.sqlite-shm delete mode 100644 data/hotel.sqlite-wal create mode 100644 src/main/java/com/mallardromain/hotel/DTO/LoginRequest.java create mode 100644 src/main/java/com/mallardromain/hotel/config/SecurityConfig.java create mode 100644 src/main/java/com/mallardromain/hotel/controller/AuthController.java create mode 100644 src/main/java/com/mallardromain/hotel/model/JWT.java create mode 100644 src/main/java/com/mallardromain/hotel/security/JWTFilter.java create mode 100644 src/main/java/com/mallardromain/hotel/service/JWTService.java rename src/main/java/com/mallardromain/hotel/{repository => service}/RoomService.java (91%) diff --git a/data/hotel.sqlite b/data/hotel.sqlite index d841a55faa08759f93e5331cf6240466aa9a64f0..edcf485608dd5eaf6515153d7dc1817baf9f2da1 100644 GIT binary patch delta 1877 zcmeIyyD|e|7=U4WV3VI+i>$E9^IXbLiFzXxnv|eXrT@=rv4(- zHY*!m-h>xd*WqnF<@`#DV>D`7-jj_+^yd1-r23@#7OMV#zxd;sA<~K^H&4VC_0Q6W zDcj;c4j$q_f(!*J+R*_GDWoCLi7x0c&GQf4fy9ST-pN8t=Jp=JlPOd6Kp z0xW#phVvKC>CZRcIqqtrxcE{R0<8(in6<_X8cc)1AJ}Xf! zR=P&ao3W*Lyz|eovRz7m009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+0D)QyWU<;xJ9KOP2ahN4 zn?NJvp;Oi+`t7_^1nMi0YHbMu1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ efB*pk1PBlyK!5-N0t5&UAV7cs0Rja6Ti^vo+bNv@ diff --git a/data/hotel.sqlite-wal b/data/hotel.sqlite-wal deleted file mode 100644 index b88b146fed14fe1bfbf37622afbc93d6b0d135d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37112 zcmeI*TWB0r7zgm#oxNox=z8T=#re&u@iAy&gS1h21Hmmr30C1Sl-TEkO`OQ;M*Q*X(+MuU|uitD**l z7|-&9VHvg)jixLgu6mlpb|yMuC8AHz;HsJ|KHs#i`nuOjMHejao~%qXCar|NeDSNy z8FrzlF6Oh9>XvfVO6pEMRJ|WN3tjGzd*4QNrB%)3cW5(SaPrRavyZzk-jt^kM!)#p zpauGqX6Z-zj;856y+NPQ9&wI#&^G#9{3`wum*{C4qmqgjJWPYMj_y)%gxe@XUFu*w zAOHafKmY;|fB*y_009U<00LK9pu;RCIndFUx6D#9j%>&ydDBToF%5Z`M{u-4ew;H4 zsx&MQac>mCkRkVS>_Sd+iK7EQllyeZ^W_Iz z;$Qvc`?{ps-s2L-ALP6G3|zUl)9mGwRWS#-CoZdKgM5e2EQ&M8x1%CQ802n+n&F1t zgbpiYb7nE2f)PO`YZh}HO5g@ptumc83kfxGDtaFBP5ta8GiISf1qakjDfM-%85cOR z;frr}_55?%rfDNX+YOZ^aE7Mn1Z}6EY4S>6OuQZhAOHafKmY;|fB*y_009U?=^rp!`eyj-u3Z7C1Pyc)c@re15JKGvwzs;d-fXLNWytBT9^5sc25_Z+@= zUfOW8^D6ccw8rNgtAGFmAOHafKmY;|fB*y_&~^fxqmO+AE7?b|EMMT2gByP@ zoSPp(zChc3ao3GUtrXF&AI#3$&1JrXq68H)&T(s zKmY;|fB*y_009U<;PL`X@&#~z0sU_QkuRX~1-?9=e(>}!TYcmUT>f)|RtP`<0uX=z X1Rwwb2tWV=5V*<(kT0;Je1U%fj&!xd diff --git a/pom.xml b/pom.xml index fdac999..a0c4b5a 100644 --- a/pom.xml +++ b/pom.xml @@ -63,6 +63,32 @@ hibernate-community-dialects + + org.springframework.boot + spring-boot-starter-security + + + + + io.jsonwebtoken + jjwt-api + 0.12.5 + + + + io.jsonwebtoken + jjwt-impl + 0.12.5 + runtime + + + + io.jsonwebtoken + jjwt-jackson + 0.12.5 + runtime + + diff --git a/src/main/java/com/mallardromain/hotel/DTO/LoginRequest.java b/src/main/java/com/mallardromain/hotel/DTO/LoginRequest.java new file mode 100644 index 0000000..ddddb67 --- /dev/null +++ b/src/main/java/com/mallardromain/hotel/DTO/LoginRequest.java @@ -0,0 +1,23 @@ +package com.mallardromain.hotel.DTO; + +public class LoginRequest { + + private String username; + private String password; + + public String getUsername(){ + return username; + } + + public void setUsername(String username){ + this.username = username; + } + + public String getPassword(){ + return password; + } + + public void setPassword(String password){ + this.password = password; + } +} diff --git a/src/main/java/com/mallardromain/hotel/config/SecurityConfig.java b/src/main/java/com/mallardromain/hotel/config/SecurityConfig.java new file mode 100644 index 0000000..14877df --- /dev/null +++ b/src/main/java/com/mallardromain/hotel/config/SecurityConfig.java @@ -0,0 +1,42 @@ +package com.mallardromain.hotel.config; + +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.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import com.mallardromain.hotel.security.JWTFilter; + +@Configuration +public class SecurityConfig { + private final JWTFilter jwtFilter; + + public SecurityConfig(JWTFilter jwtFilter) { + this.jwtFilter = jwtFilter; + } + + @Bean + public SecurityFilterChain securityFilterChain( + HttpSecurity http + ) throws Exception { + + http.csrf(csrf -> csrf.disable()) + .sessionManagement(session -> + session.sessionCreationPolicy( + SessionCreationPolicy.STATELESS + ) + ) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/auth/**").permitAll() + .anyRequest().authenticated() + ) + .addFilterBefore( + jwtFilter, + UsernamePasswordAuthenticationFilter.class + ); + return http.build(); + } + +} diff --git a/src/main/java/com/mallardromain/hotel/controller/AuthController.java b/src/main/java/com/mallardromain/hotel/controller/AuthController.java new file mode 100644 index 0000000..07db939 --- /dev/null +++ b/src/main/java/com/mallardromain/hotel/controller/AuthController.java @@ -0,0 +1,54 @@ +package com.mallardromain.hotel.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +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; + +import com.mallardromain.hotel.DTO.LoginRequest; +import com.mallardromain.hotel.service.JWTService; + +@RestController +@RequestMapping("/auth") +public class AuthController { + + private final JWTService jwtService; + + public AuthController(JWTService jwtService){ + this.jwtService = jwtService; + } + + @PostMapping("/login") + public ResponseEntity login( + @RequestBody LoginRequest request + ){ + //temps test1/2 + if ( + request.getUsername().equals("test1") + && + request.getPassword().equals("password1") + ) { + String token = jwtService + .generateToken(request.getUsername()); + + return ResponseEntity.ok(token); + } + + if ( + request.getUsername().equals("test2") + && + request.getPassword().equals("password2") + ) { + String token = jwtService + .generateToken(request.getUsername()); + + return ResponseEntity.ok(token); + } + + return ResponseEntity.status(401).body("Invalid username/password"); + } + + +} diff --git a/src/main/java/com/mallardromain/hotel/controller/RoomController.java b/src/main/java/com/mallardromain/hotel/controller/RoomController.java index f0b0a90..9350a76 100644 --- a/src/main/java/com/mallardromain/hotel/controller/RoomController.java +++ b/src/main/java/com/mallardromain/hotel/controller/RoomController.java @@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.*; import com.mallardromain.hotel.model.Room; import com.mallardromain.hotel.repository.RoomRepository; -import com.mallardromain.hotel.repository.RoomService; +import com.mallardromain.hotel.service.RoomService; @RestController @RequestMapping("/rooms") diff --git a/src/main/java/com/mallardromain/hotel/model/JWT.java b/src/main/java/com/mallardromain/hotel/model/JWT.java new file mode 100644 index 0000000..b9142e4 --- /dev/null +++ b/src/main/java/com/mallardromain/hotel/model/JWT.java @@ -0,0 +1,6 @@ +package com.mallardromain.hotel.model; + +public class JWT { + + +} diff --git a/src/main/java/com/mallardromain/hotel/model/Room.java b/src/main/java/com/mallardromain/hotel/model/Room.java index 22e2bb6..3ccd823 100644 --- a/src/main/java/com/mallardromain/hotel/model/Room.java +++ b/src/main/java/com/mallardromain/hotel/model/Room.java @@ -7,6 +7,7 @@ import jakarta.persistence.*; public class Room { @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String number; @@ -19,7 +20,7 @@ public class Room { } public Room(Integer id, String number, String status){ - this.id = id; + //this.id = id; id is/should be generated by the DB this.number = number; this.status = status; } diff --git a/src/main/java/com/mallardromain/hotel/security/JWTFilter.java b/src/main/java/com/mallardromain/hotel/security/JWTFilter.java new file mode 100644 index 0000000..76ff351 --- /dev/null +++ b/src/main/java/com/mallardromain/hotel/security/JWTFilter.java @@ -0,0 +1,75 @@ +package com.mallardromain.hotel.security; + +import java.io.IOException; +import java.util.Collections; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.WebAuthenticationDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.mallardromain.hotel.service.JWTService; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@Component +public class JWTFilter extends OncePerRequestFilter { + + private final JWTService jwtService; + + public JWTFilter(JWTService jwtService){ + this.jwtService = jwtService; + } + + @Override + protected void doFilterInternal( + HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain + ) throws ServletException, IOException { + + String authHeader = request + .getHeader("Authorization"); + + if ( authHeader == null + || + !authHeader.startsWith("Bearer ") + ) { + + filterChain.doFilter(request, response); + return; + } + + String token = authHeader.substring(7); + + if (jwtService.isTockenValid(token)){ + + String username = jwtService + .extractUsername(token); + + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + username, + null, + Collections.emptyList() + ); + + auth.setDetails( + new WebAuthenticationDetailsSource() + .buildDetails(request) + ); + + SecurityContextHolder.getContext() + .setAuthentication(auth); + } + + filterChain.doFilter(request, response); + } +} diff --git a/src/main/java/com/mallardromain/hotel/service/JWTService.java b/src/main/java/com/mallardromain/hotel/service/JWTService.java new file mode 100644 index 0000000..6749784 --- /dev/null +++ b/src/main/java/com/mallardromain/hotel/service/JWTService.java @@ -0,0 +1,58 @@ +package com.mallardromain.hotel.service; + +import java.security.Key; +import java.util.Date; + +import javax.crypto.SecretKey; + +import org.springframework.stereotype.Service; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; + +@Service +public class JWTService { + + private static final String SECRET = "NotHardCodedKeyThatIsAtLeast32CharactersLong!"; + + private final Key key = Keys.hmacShaKeyFor(SECRET.getBytes()); + + public String generateToken(String username){ + + return Jwts.builder() + .subject(username) + .issuedAt(new Date()) + .expiration( + new Date(System.currentTimeMillis() + 1000 * 60 * 60) + ) + .signWith(key) + .compact(); + } + + public boolean isTockenValid(String token){ + + try { + Jwts.parser() + .verifyWith((SecretKey) key) + .build() + .parseSignedClaims(token); + return true; + } catch (Exception e) { + return false; + } + } + + public String extractUsername(String token){ + + Claims claims = Jwts.parser() + .verifyWith((SecretKey) key) + .build() + .parseSignedClaims(token) + .getPayload(); + + return claims.getSubject(); + } + +} diff --git a/src/main/java/com/mallardromain/hotel/repository/RoomService.java b/src/main/java/com/mallardromain/hotel/service/RoomService.java similarity index 91% rename from src/main/java/com/mallardromain/hotel/repository/RoomService.java rename to src/main/java/com/mallardromain/hotel/service/RoomService.java index 40cb955..d4b13ea 100644 --- a/src/main/java/com/mallardromain/hotel/repository/RoomService.java +++ b/src/main/java/com/mallardromain/hotel/service/RoomService.java @@ -1,10 +1,12 @@ -package com.mallardromain.hotel.repository; +package com.mallardromain.hotel.service; import java.util.List; import org.springframework.stereotype.Service; import com.mallardromain.hotel.model.Room; +import com.mallardromain.hotel.repository.RoomRepository; + import jakarta.persistence.*; @Service