From 1086cd18f72eeb7250c445e7cc6c6ed7aeb52de6 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Wed, 20 Nov 2024 23:35:40 +0800 Subject: [PATCH 01/11] Draft implementation of jwtService --- .../domain/JwtService.java | 32 +++++++++++++++++-- .../domain/JwtServiceTest.java | 22 +++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java index c16a6de..8e50399 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java @@ -1,13 +1,41 @@ package com.leungcheng.spring_simple_backend.domain; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.MacAlgorithm; +import javax.crypto.SecretKey; + public class JwtService { public record UserInfo(String userId) {} + public record Config(SecretKey secretKey, MacAlgorithm algorithm) { + public static Config sample() { + MacAlgorithm alg = Jwts.SIG.HS256; + SecretKey key = alg.key().build(); + return new Config(key, alg); + } + } + + private final Config config; + + public JwtService(Config config) { + this.config = config; + } + public String generateAccessToken(User user) { - throw new UnsupportedOperationException("Not implemented"); + return Jwts.builder() + .subject(user.getId()) + .signWith(config.secretKey(), Jwts.SIG.HS256) + .compact(); } public UserInfo parseAccessToken(String token) { - throw new UnsupportedOperationException("Not implemented"); + String userId = + Jwts.parser() + .verifyWith(config.secretKey()) + .build() + .parseSignedClaims(token) + .getPayload() + .getSubject(); + return new UserInfo(userId); } } diff --git a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java new file mode 100644 index 0000000..8fc57b4 --- /dev/null +++ b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java @@ -0,0 +1,22 @@ +package com.leungcheng.spring_simple_backend.domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class JwtServiceTest { + private static User.Builder userBuilder() { + return new User.Builder().username("default-user").password("password"); + } + + @Test + void shouldGenerateAndParseAccessToken() { + JwtService jwtService = new JwtService(JwtService.Config.sample()); + User user = userBuilder().build(); + + String token = jwtService.generateAccessToken(user); + JwtService.UserInfo userInfo = jwtService.parseAccessToken(token); + + assertEquals(user.getId(), userInfo.userId()); + } +} From 2711c8a27eea96c29daf1b7d2f11e764ce859d99 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Thu, 21 Nov 2024 21:04:43 +0800 Subject: [PATCH 02/11] Refactor JwtService --- .../domain/JwtService.java | 25 +++++++------------ .../domain/JwtServiceTest.java | 11 +++++++- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java index 8e50399..0bbd4c0 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java @@ -1,37 +1,30 @@ package com.leungcheng.spring_simple_backend.domain; import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.MacAlgorithm; +import java.nio.charset.StandardCharsets; import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; public class JwtService { public record UserInfo(String userId) {} - public record Config(SecretKey secretKey, MacAlgorithm algorithm) { - public static Config sample() { - MacAlgorithm alg = Jwts.SIG.HS256; - SecretKey key = alg.key().build(); - return new Config(key, alg); - } - } - - private final Config config; + public record Config(String hs256Key) {} public JwtService(Config config) { - this.config = config; + byte[] keyBytes = config.hs256Key().getBytes(StandardCharsets.UTF_8); + secretKey = new SecretKeySpec(keyBytes, "HmacSHA256"); } + private final SecretKey secretKey; + public String generateAccessToken(User user) { - return Jwts.builder() - .subject(user.getId()) - .signWith(config.secretKey(), Jwts.SIG.HS256) - .compact(); + return Jwts.builder().subject(user.getId()).signWith(this.secretKey, Jwts.SIG.HS256).compact(); } public UserInfo parseAccessToken(String token) { String userId = Jwts.parser() - .verifyWith(config.secretKey()) + .verifyWith(this.secretKey) .build() .parseSignedClaims(token) .getPayload() diff --git a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java index 8fc57b4..2a57390 100644 --- a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java +++ b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import io.jsonwebtoken.Jwts; import org.junit.jupiter.api.Test; public class JwtServiceTest { @@ -9,9 +10,17 @@ private static User.Builder userBuilder() { return new User.Builder().username("default-user").password("password"); } + private static class JwtServiceBuilder { + private final String hs256Key = Jwts.SIG.HS256.key().build().toString(); + + private JwtService build() { + return new JwtService(new JwtService.Config(hs256Key)); + } + } + @Test void shouldGenerateAndParseAccessToken() { - JwtService jwtService = new JwtService(JwtService.Config.sample()); + JwtService jwtService = new JwtServiceBuilder().build(); User user = userBuilder().build(); String token = jwtService.generateAccessToken(user); From bbccc4769ce02004f219b3707ecbc591878e286b Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Thu, 21 Nov 2024 21:57:06 +0800 Subject: [PATCH 03/11] Add expiration --- .../domain/JwtService.java | 48 +++++++++++++++---- .../domain/JwtServiceTest.java | 26 +++++++++- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java index 0bbd4c0..03d1e92 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java @@ -1,34 +1,62 @@ package com.leungcheng.spring_simple_backend.domain; +import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; public class JwtService { public record UserInfo(String userId) {} - public record Config(String hs256Key) {} + public record Config(String hs256Key, Duration expiredDuration) {} + + public static class InvalidTokenException extends RuntimeException { + public InvalidTokenException(String message) { + super(message); + } + } public JwtService(Config config) { byte[] keyBytes = config.hs256Key().getBytes(StandardCharsets.UTF_8); secretKey = new SecretKeySpec(keyBytes, "HmacSHA256"); + expiredDuration = config.expiredDuration(); } private final SecretKey secretKey; + private final Duration expiredDuration; + + private Date getExpirationDate() { + return Date.from(Instant.now().plus(this.expiredDuration)); + } + public String generateAccessToken(User user) { - return Jwts.builder().subject(user.getId()).signWith(this.secretKey, Jwts.SIG.HS256).compact(); + return Jwts.builder() + .subject(user.getId()) + .expiration(getExpirationDate()) + .signWith(this.secretKey, Jwts.SIG.HS256) + .compact(); } public UserInfo parseAccessToken(String token) { - String userId = - Jwts.parser() - .verifyWith(this.secretKey) - .build() - .parseSignedClaims(token) - .getPayload() - .getSubject(); - return new UserInfo(userId); + try { + String userId = + Jwts.parser() + .verifyWith(this.secretKey) + .build() + .parseSignedClaims(token) + .getPayload() + .getSubject(); + return new UserInfo(userId); + } catch (Exception e) { + if (e instanceof ExpiredJwtException) { + throw new InvalidTokenException("Expired token"); + } + throw new InvalidTokenException("Invalid token"); + } } } diff --git a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java index 2a57390..575cd4a 100644 --- a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java +++ b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java @@ -1,8 +1,10 @@ package com.leungcheng.spring_simple_backend.domain; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import io.jsonwebtoken.Jwts; +import java.time.Duration; import org.junit.jupiter.api.Test; public class JwtServiceTest { @@ -12,9 +14,15 @@ private static User.Builder userBuilder() { private static class JwtServiceBuilder { private final String hs256Key = Jwts.SIG.HS256.key().build().toString(); + private Duration expiredDuration = Duration.ofHours(1); + + private JwtServiceBuilder expiredDuration(Duration expiredDuration) { + this.expiredDuration = expiredDuration; + return this; + } private JwtService build() { - return new JwtService(new JwtService.Config(hs256Key)); + return new JwtService(new JwtService.Config(hs256Key, expiredDuration)); } } @@ -28,4 +36,20 @@ void shouldGenerateAndParseAccessToken() { assertEquals(user.getId(), userInfo.userId()); } + + @Test + void shouldThrowExceptionIfTokenExpired() { + JwtService jwtService = new JwtServiceBuilder().expiredDuration(Duration.ofSeconds(-1)).build(); + User user = userBuilder().build(); + + String token = jwtService.generateAccessToken(user); + + JwtService.InvalidTokenException exception = + assertThrows( + JwtService.InvalidTokenException.class, + () -> { + jwtService.parseAccessToken(token); + }); + assertEquals("Expired token", exception.getMessage()); + } } From b643ad670e31101f901a5d8c5b638303c1104f19 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Thu, 21 Nov 2024 22:06:14 +0800 Subject: [PATCH 04/11] Update of parsing token signed by invalid key --- .../domain/JwtService.java | 4 ++++ .../domain/JwtServiceTest.java | 24 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java index 03d1e92..0736e56 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java @@ -2,6 +2,7 @@ import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.SignatureException; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; @@ -56,6 +57,9 @@ public UserInfo parseAccessToken(String token) { if (e instanceof ExpiredJwtException) { throw new InvalidTokenException("Expired token"); } + if (e instanceof SignatureException) { + throw new InvalidTokenException("Invalid signature"); + } throw new InvalidTokenException("Invalid token"); } } diff --git a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java index 575cd4a..d9e664a 100644 --- a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java +++ b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java @@ -13,7 +13,7 @@ private static User.Builder userBuilder() { } private static class JwtServiceBuilder { - private final String hs256Key = Jwts.SIG.HS256.key().build().toString(); + private String hs256Key = Jwts.SIG.HS256.key().build().toString(); private Duration expiredDuration = Duration.ofHours(1); private JwtServiceBuilder expiredDuration(Duration expiredDuration) { @@ -21,6 +21,11 @@ private JwtServiceBuilder expiredDuration(Duration expiredDuration) { return this; } + private JwtServiceBuilder newHs256Key() { + this.hs256Key = Jwts.SIG.HS256.key().build().toString(); + return this; + } + private JwtService build() { return new JwtService(new JwtService.Config(hs256Key, expiredDuration)); } @@ -52,4 +57,21 @@ void shouldThrowExceptionIfTokenExpired() { }); assertEquals("Expired token", exception.getMessage()); } + + @Test + void shouldThrowExceptionIfTokenIsSignedByDifferentKey() { + JwtServiceBuilder builder = new JwtServiceBuilder(); + JwtService jwtService = builder.build(); + User user = userBuilder().build(); + + String token = jwtService.generateAccessToken(user); + + JwtService.InvalidTokenException exception = + assertThrows( + JwtService.InvalidTokenException.class, + () -> { + builder.newHs256Key().build().parseAccessToken(token); + }); + assertEquals("Invalid signature", exception.getMessage()); + } } From 69514d656c319901b3c9e9687b98ebf138503a9f Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Thu, 21 Nov 2024 22:09:59 +0800 Subject: [PATCH 05/11] Add test case of token with invalid format --- .../domain/JwtServiceTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java index d9e664a..8a97bef 100644 --- a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java +++ b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java @@ -74,4 +74,18 @@ void shouldThrowExceptionIfTokenIsSignedByDifferentKey() { }); assertEquals("Invalid signature", exception.getMessage()); } + + @Test + void shouldThrowExceptionIfTokenIsInvalid() { + JwtService jwtService = new JwtServiceBuilder().build(); + User user = userBuilder().build(); + + JwtService.InvalidTokenException exception = + assertThrows( + JwtService.InvalidTokenException.class, + () -> { + jwtService.parseAccessToken("invalid"); + }); + assertEquals("Invalid token", exception.getMessage()); + } } From 3a2d8f7f3f9ce54fcb27050a565d1df49f3a86c9 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Fri, 22 Nov 2024 18:30:29 +0800 Subject: [PATCH 06/11] Inject the env values to JwtService --- .../spring_simple_backend/domain/JwtService.java | 13 ++++++++----- .../domain/JwtServiceTest.java | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java index 0736e56..d76d0cf 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java @@ -9,22 +9,25 @@ import java.util.Date; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +@Component public class JwtService { public record UserInfo(String userId) {} - public record Config(String hs256Key, Duration expiredDuration) {} - public static class InvalidTokenException extends RuntimeException { public InvalidTokenException(String message) { super(message); } } - public JwtService(Config config) { - byte[] keyBytes = config.hs256Key().getBytes(StandardCharsets.UTF_8); + public JwtService( + @Value("${jwt.hs256Key:your-default-key-1234567890abcdef}") String hs256Key, + @Value("${jwt.expiredDuration:1h}") Duration expiredDuration) { + byte[] keyBytes = hs256Key.getBytes(StandardCharsets.UTF_8); secretKey = new SecretKeySpec(keyBytes, "HmacSHA256"); - expiredDuration = config.expiredDuration(); + this.expiredDuration = expiredDuration; } private final SecretKey secretKey; diff --git a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java index 8a97bef..2e9a868 100644 --- a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java +++ b/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java @@ -27,7 +27,7 @@ private JwtServiceBuilder newHs256Key() { } private JwtService build() { - return new JwtService(new JwtService.Config(hs256Key, expiredDuration)); + return new JwtService(hs256Key, expiredDuration); } } From 3b85382e807ba88bdbf53a336207b949eff77e01 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Mon, 25 Nov 2024 21:57:25 +0800 Subject: [PATCH 07/11] Add document to pubic method --- .../leungcheng/spring_simple_backend/domain/JwtService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java index d76d0cf..bb53014 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java @@ -46,6 +46,10 @@ public String generateAccessToken(User user) { .compact(); } + /** + * @throws InvalidTokenException if token is invalid due to expiration, invalid signature, or + * other reasons + */ public UserInfo parseAccessToken(String token) { try { String userId = From fc1ec04a23ddcb4d6c9d30c737ba6dbdc805fac4 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Mon, 25 Nov 2024 21:59:52 +0800 Subject: [PATCH 08/11] Remove mock on jwtService in api test --- .../SpringSimpleBackendApplicationTests.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/test/java/com/leungcheng/spring_simple_backend/SpringSimpleBackendApplicationTests.java b/src/test/java/com/leungcheng/spring_simple_backend/SpringSimpleBackendApplicationTests.java index f0493ef..4d477a5 100644 --- a/src/test/java/com/leungcheng/spring_simple_backend/SpringSimpleBackendApplicationTests.java +++ b/src/test/java/com/leungcheng/spring_simple_backend/SpringSimpleBackendApplicationTests.java @@ -1,13 +1,10 @@ package com.leungcheng.spring_simple_backend; import static org.hamcrest.Matchers.not; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import com.jayway.jsonpath.JsonPath; -import com.leungcheng.spring_simple_backend.domain.JwtService; import com.leungcheng.spring_simple_backend.domain.ProductRepository; import com.leungcheng.spring_simple_backend.domain.User; import com.leungcheng.spring_simple_backend.domain.UserRepository; @@ -17,7 +14,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; @@ -31,8 +27,6 @@ class SpringSimpleBackendApplicationTests { @Autowired private ProductRepository productRepository; @Autowired private UserRepository userRepository; - @MockBean private JwtService jwtService; - private Optional accessToken = Optional.empty(); @BeforeEach @@ -55,11 +49,6 @@ private String useNewUserAccessToken() throws Exception { signup(userCredentials).andExpect(status().isCreated()); User user = userRepository.findByUsername(userCredentials.username).orElseThrow(); - when(jwtService.generateAccessToken(argThat(argument -> argument.getId().equals(user.getId())))) - .thenReturn("dummy-token"); - when(jwtService.parseAccessToken("dummy-token")) - .thenReturn(new JwtService.UserInfo(user.getId())); - MvcResult result = login(userCredentials).andExpect(status().isOk()).andReturn(); String token = JsonPath.read(result.getResponse().getContentAsString(), "$.accessToken"); setAccessToken(token); From 386af0bf1069f0218fd644966f2910b5ebe59863 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Mon, 25 Nov 2024 22:06:47 +0800 Subject: [PATCH 09/11] Handle InvalidTokenException --- .../spring_simple_backend/auth/JwtAuthFilter.java | 14 +++++++++----- .../SpringSimpleBackendApplicationTests.java | 6 ++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/leungcheng/spring_simple_backend/auth/JwtAuthFilter.java b/src/main/java/com/leungcheng/spring_simple_backend/auth/JwtAuthFilter.java index 862495c..ac54a09 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/auth/JwtAuthFilter.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/auth/JwtAuthFilter.java @@ -28,11 +28,15 @@ protected void doFilterInternal( .ifPresent( accessToken -> { if (SecurityContextHolder.getContext().getAuthentication() == null) { - UserInfoAuthenticationToken authToken = - new UserInfoAuthenticationToken(jwtService.parseAccessToken(accessToken)); - SecurityContext context = SecurityContextHolder.createEmptyContext(); - context.setAuthentication(authToken); - SecurityContextHolder.setContext(context); + try { + UserInfoAuthenticationToken authToken = + new UserInfoAuthenticationToken(jwtService.parseAccessToken(accessToken)); + SecurityContext context = SecurityContextHolder.createEmptyContext(); + context.setAuthentication(authToken); + SecurityContextHolder.setContext(context); + } catch (JwtService.InvalidTokenException e) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } } }); diff --git a/src/test/java/com/leungcheng/spring_simple_backend/SpringSimpleBackendApplicationTests.java b/src/test/java/com/leungcheng/spring_simple_backend/SpringSimpleBackendApplicationTests.java index 4d477a5..b685a22 100644 --- a/src/test/java/com/leungcheng/spring_simple_backend/SpringSimpleBackendApplicationTests.java +++ b/src/test/java/com/leungcheng/spring_simple_backend/SpringSimpleBackendApplicationTests.java @@ -142,6 +142,12 @@ public void shouldRejectNonAuthApiCallWithoutToken() throws Exception { createProduct(CreateProductParams.sample()).andExpect(status().isForbidden()); } + @Test + public void shouldRejectIfApiCallWithInvalidToken() throws Exception { + setAccessToken("invalid-token"); + createProduct(CreateProductParams.sample()).andExpect(status().isForbidden()); + } + @Test public void shouldRejectIfAuthHeaderIsNotSetCorrectly() throws Exception { useNewUserAccessToken(); From a6b1af15e403f31f7ae0e750518b7c0c3ad21151 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Mon, 25 Nov 2024 22:34:26 +0800 Subject: [PATCH 10/11] Specify in application.properties --- .../leungcheng/spring_simple_backend/domain/JwtService.java | 4 ++-- src/main/resources/application.properties | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java index bb53014..be6523a 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java @@ -23,8 +23,8 @@ public InvalidTokenException(String message) { } public JwtService( - @Value("${jwt.hs256Key:your-default-key-1234567890abcdef}") String hs256Key, - @Value("${jwt.expiredDuration:1h}") Duration expiredDuration) { + @Value("${jwt.hs256Key}") String hs256Key, + @Value("${jwt.expiredDuration}") Duration expiredDuration) { byte[] keyBytes = hs256Key.getBytes(StandardCharsets.UTF_8); secretKey = new SecretKeySpec(keyBytes, "HmacSHA256"); this.expiredDuration = expiredDuration; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 47ebe04..f8bd7ff 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,3 @@ spring.application.name=spring-simple-backend +jwt.hs256Key=${JWT_HS256_KEY:your-default-key-1234567890abcdef} +jwt.expiredDuration=1h \ No newline at end of file From 32681d3adf881680d98edea90723a910b3d67df2 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Mon, 25 Nov 2024 22:44:13 +0800 Subject: [PATCH 11/11] Move auth related to auth package --- .../{domain => auth}/CustomUserDetailService.java | 3 ++- .../leungcheng/spring_simple_backend/auth/JwtAuthFilter.java | 1 - .../spring_simple_backend/{domain => auth}/JwtService.java | 3 ++- .../leungcheng/spring_simple_backend/auth/SecurityConfig.java | 1 - .../auth/UserInfoAuthenticationToken.java | 2 +- .../spring_simple_backend/controller/AuthController.java | 2 +- .../spring_simple_backend/{domain => auth}/JwtServiceTest.java | 3 ++- 7 files changed, 8 insertions(+), 7 deletions(-) rename src/main/java/com/leungcheng/spring_simple_backend/{domain => auth}/CustomUserDetailService.java (84%) rename src/main/java/com/leungcheng/spring_simple_backend/{domain => auth}/JwtService.java (95%) rename src/test/java/com/leungcheng/spring_simple_backend/{domain => auth}/JwtServiceTest.java (96%) diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/CustomUserDetailService.java b/src/main/java/com/leungcheng/spring_simple_backend/auth/CustomUserDetailService.java similarity index 84% rename from src/main/java/com/leungcheng/spring_simple_backend/domain/CustomUserDetailService.java rename to src/main/java/com/leungcheng/spring_simple_backend/auth/CustomUserDetailService.java index 7a7f571..a5b38d8 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/CustomUserDetailService.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/auth/CustomUserDetailService.java @@ -1,5 +1,6 @@ -package com.leungcheng.spring_simple_backend.domain; +package com.leungcheng.spring_simple_backend.auth; +import com.leungcheng.spring_simple_backend.domain.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; diff --git a/src/main/java/com/leungcheng/spring_simple_backend/auth/JwtAuthFilter.java b/src/main/java/com/leungcheng/spring_simple_backend/auth/JwtAuthFilter.java index ac54a09..8c12773 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/auth/JwtAuthFilter.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/auth/JwtAuthFilter.java @@ -1,6 +1,5 @@ package com.leungcheng.spring_simple_backend.auth; -import com.leungcheng.spring_simple_backend.domain.JwtService; import com.leungcheng.spring_simple_backend.domain.UserRepository; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java b/src/main/java/com/leungcheng/spring_simple_backend/auth/JwtService.java similarity index 95% rename from src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java rename to src/main/java/com/leungcheng/spring_simple_backend/auth/JwtService.java index be6523a..15441f2 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/JwtService.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/auth/JwtService.java @@ -1,5 +1,6 @@ -package com.leungcheng.spring_simple_backend.domain; +package com.leungcheng.spring_simple_backend.auth; +import com.leungcheng.spring_simple_backend.domain.User; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.SignatureException; diff --git a/src/main/java/com/leungcheng/spring_simple_backend/auth/SecurityConfig.java b/src/main/java/com/leungcheng/spring_simple_backend/auth/SecurityConfig.java index 01e97fe..cb63c84 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/auth/SecurityConfig.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/auth/SecurityConfig.java @@ -1,6 +1,5 @@ package com.leungcheng.spring_simple_backend.auth; -import com.leungcheng.spring_simple_backend.domain.CustomUserDetailService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/com/leungcheng/spring_simple_backend/auth/UserInfoAuthenticationToken.java b/src/main/java/com/leungcheng/spring_simple_backend/auth/UserInfoAuthenticationToken.java index 121d1ef..19e5df8 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/auth/UserInfoAuthenticationToken.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/auth/UserInfoAuthenticationToken.java @@ -1,6 +1,6 @@ package com.leungcheng.spring_simple_backend.auth; -import com.leungcheng.spring_simple_backend.domain.JwtService.UserInfo; +import com.leungcheng.spring_simple_backend.auth.JwtService.UserInfo; import org.springframework.security.authentication.AbstractAuthenticationToken; public class UserInfoAuthenticationToken extends AbstractAuthenticationToken { diff --git a/src/main/java/com/leungcheng/spring_simple_backend/controller/AuthController.java b/src/main/java/com/leungcheng/spring_simple_backend/controller/AuthController.java index 526288f..40b0501 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/controller/AuthController.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/controller/AuthController.java @@ -1,6 +1,6 @@ package com.leungcheng.spring_simple_backend.controller; -import com.leungcheng.spring_simple_backend.domain.JwtService; +import com.leungcheng.spring_simple_backend.auth.JwtService; import com.leungcheng.spring_simple_backend.domain.User; import com.leungcheng.spring_simple_backend.domain.UserRepository; import jakarta.validation.Valid; diff --git a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java b/src/test/java/com/leungcheng/spring_simple_backend/auth/JwtServiceTest.java similarity index 96% rename from src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java rename to src/test/java/com/leungcheng/spring_simple_backend/auth/JwtServiceTest.java index 2e9a868..7568ae3 100644 --- a/src/test/java/com/leungcheng/spring_simple_backend/domain/JwtServiceTest.java +++ b/src/test/java/com/leungcheng/spring_simple_backend/auth/JwtServiceTest.java @@ -1,8 +1,9 @@ -package com.leungcheng.spring_simple_backend.domain; +package com.leungcheng.spring_simple_backend.auth; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import com.leungcheng.spring_simple_backend.domain.User; import io.jsonwebtoken.Jwts; import java.time.Duration; import org.junit.jupiter.api.Test;