From a93288e31c180d9ad25e6875c715e7cf9c2a2336 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 7 Apr 2024 18:25:39 +0200 Subject: [PATCH] - Added sample JWT authentication service - Added jwt secret and duration to properties - Added jwt dependencies - Added SecureApiRestController - Added UserEntity - Added UserRepository.java --- build.gradle | 4 ++ .../sharepulse/config/SecurityConfig.java | 4 +- .../sharepulse/db/repo/UserRepository.java | 4 ++ .../java/de/w665/sharepulse/model/User.java | 17 +++++++ .../rest/SecureApiRestController.java | 9 ++++ .../mappings/AuthenticationController.java | 33 ++++++++++++ .../service/AuthenticationService.java | 51 +++++++++++++++++++ src/main/resources/application.properties | 4 +- 8 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 src/main/java/de/w665/sharepulse/db/repo/UserRepository.java create mode 100644 src/main/java/de/w665/sharepulse/model/User.java create mode 100644 src/main/java/de/w665/sharepulse/rest/SecureApiRestController.java create mode 100644 src/main/java/de/w665/sharepulse/rest/mappings/AuthenticationController.java create mode 100644 src/main/java/de/w665/sharepulse/service/AuthenticationService.java diff --git a/build.gradle b/build.gradle index e75d748..a8d6cb7 100644 --- a/build.gradle +++ b/build.gradle @@ -40,6 +40,10 @@ dependencies { implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '3.2.4' // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.12.5' + // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl + runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.12.5' + // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-orgjson + runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-orgjson', version: '0.12.5' } diff --git a/src/main/java/de/w665/sharepulse/config/SecurityConfig.java b/src/main/java/de/w665/sharepulse/config/SecurityConfig.java index 610f085..75999f4 100644 --- a/src/main/java/de/w665/sharepulse/config/SecurityConfig.java +++ b/src/main/java/de/w665/sharepulse/config/SecurityConfig.java @@ -15,11 +15,11 @@ public class SecurityConfig { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize - .requestMatchers("/admin/**").authenticated() + .requestMatchers("/api/v1/secure/**").authenticated() .anyRequest().permitAll() ) .formLogin(formLogin -> formLogin - .loginPage("/login") + .loginPage("/management/login") .permitAll() ) .logout(LogoutConfigurer::permitAll) diff --git a/src/main/java/de/w665/sharepulse/db/repo/UserRepository.java b/src/main/java/de/w665/sharepulse/db/repo/UserRepository.java new file mode 100644 index 0000000..d7ec26b --- /dev/null +++ b/src/main/java/de/w665/sharepulse/db/repo/UserRepository.java @@ -0,0 +1,4 @@ +package de.w665.sharepulse.db.repo; + +public class UserRepository { +} diff --git a/src/main/java/de/w665/sharepulse/model/User.java b/src/main/java/de/w665/sharepulse/model/User.java new file mode 100644 index 0000000..e0d821d --- /dev/null +++ b/src/main/java/de/w665/sharepulse/model/User.java @@ -0,0 +1,17 @@ +package de.w665.sharepulse.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class User { + private String username; + private String password; + private String email; + private String role; +} diff --git a/src/main/java/de/w665/sharepulse/rest/SecureApiRestController.java b/src/main/java/de/w665/sharepulse/rest/SecureApiRestController.java new file mode 100644 index 0000000..3b798e3 --- /dev/null +++ b/src/main/java/de/w665/sharepulse/rest/SecureApiRestController.java @@ -0,0 +1,9 @@ +package de.w665.sharepulse.rest; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/secure") +public abstract class SecureApiRestController { +} diff --git a/src/main/java/de/w665/sharepulse/rest/mappings/AuthenticationController.java b/src/main/java/de/w665/sharepulse/rest/mappings/AuthenticationController.java new file mode 100644 index 0000000..9e5d1c4 --- /dev/null +++ b/src/main/java/de/w665/sharepulse/rest/mappings/AuthenticationController.java @@ -0,0 +1,33 @@ +package de.w665.sharepulse.rest.mappings; + +import de.w665.sharepulse.service.AuthenticationService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController +@RequestMapping("/api/v1/auth") +public class AuthenticationController { + + private final AuthenticationService authenticationService; + + public AuthenticationController(AuthenticationService authenticationService) { + this.authenticationService = authenticationService; + } + + @PostMapping("/login") + public ResponseEntity createAuthenticationToken() { + String token = authenticationService.authenticate("test", "test"); + + Map response = new HashMap<>(); + response.put("token", token); + + return new ResponseEntity<>(response, HttpStatus.OK); + } +} diff --git a/src/main/java/de/w665/sharepulse/service/AuthenticationService.java b/src/main/java/de/w665/sharepulse/service/AuthenticationService.java new file mode 100644 index 0000000..2107c10 --- /dev/null +++ b/src/main/java/de/w665/sharepulse/service/AuthenticationService.java @@ -0,0 +1,51 @@ +package de.w665.sharepulse.service; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.crypto.SecretKey; +import java.util.Base64; +import java.util.Date; + +@Slf4j +@Service +public class AuthenticationService { + @Value("${secureapi.jwt.secret}") + private String secretString; + + @Value("${secureapi.jwt.expiration}") + private long expirationTime; // in milliseconds + + private SecretKey secretKey; + + @PostConstruct + public void init() { + System.out.println("Initializing secret key"); + byte[] encodedKey = Base64.getEncoder().encode(secretString.getBytes()); // encode the secret key + this.secretKey = Keys.hmacShaKeyFor(encodedKey); + } + + public String authenticate(String username, String password) { + // validate user credentials with repository and password hash algorithm + + return generateToken(username); + // throw new RuntimeException("User authentication failed"); + } + + private String generateToken(String username) { + long nowMillis = System.currentTimeMillis(); + Date now = new Date(nowMillis); + Date expiryDate = new Date(nowMillis + expirationTime); + + return Jwts.builder() + .subject(username) + .issuedAt(now) + .expiration(expiryDate) + .signWith(secretKey) + .compact(); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3e5ad1a..08636ac 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -25,4 +25,6 @@ spring.web.resources.static-locations=classpath:/static/browser/ spring.data.rest.base-path=/api/v1 # Miscellaneous server.port=80 -spring.application.name=sharepulse \ No newline at end of file +spring.application.name=sharepulse +secureapi.jwt.secret=sampleKeyToChangeInProduction +secureapi.jwt.expiration=3600000 \ No newline at end of file