diff --git a/src/main/java/io/wwan13/wintersecurity/auth/processor/InterceptorAuthProcessor.java b/src/main/java/io/wwan13/wintersecurity/auth/processor/InterceptorAuthProcessor.java index 2472571..ec9723b 100644 --- a/src/main/java/io/wwan13/wintersecurity/auth/processor/InterceptorAuthProcessor.java +++ b/src/main/java/io/wwan13/wintersecurity/auth/processor/InterceptorAuthProcessor.java @@ -20,12 +20,11 @@ import io.wwan13.wintersecurity.auth.RequestStorage; import io.wwan13.wintersecurity.auth.TokenExtractor; import io.wwan13.wintersecurity.constant.Constants; +import io.wwan13.wintersecurity.jwt.TokenClaims; import io.wwan13.wintersecurity.jwt.TokenDecoder; -import io.wwan13.wintersecurity.jwt.payload.util.RoleSerializer; import org.springframework.http.HttpMethod; import javax.servlet.http.HttpServletRequest; -import java.util.Map; public class InterceptorAuthProcessor extends AbstractInterceptorAuthProcessor { @@ -57,16 +56,15 @@ private void actionIfTokenPresent( HttpServletRequest request, RequestStorage storage ) { - Map claims = tokenDecoder.decode(token); - String rawRoles = (String) claims.get(Constants.PAYLOAD_KEY_USER_ROLE); + TokenClaims claims = tokenDecoder.decode(token); accessManager.manageWithAuthentication( HttpMethod.resolve(request.getMethod()), request.getRequestURI(), - RoleSerializer.deserialize(rawRoles) + claims.getRoles() ); - storage.saveAll(claims); + storage.save(Constants.ATTRIBUTE_CLAIMS_KEY, claims); } private void actionIfTokenAbsent(HttpServletRequest request) { diff --git a/src/main/java/io/wwan13/wintersecurity/constant/Constants.java b/src/main/java/io/wwan13/wintersecurity/constant/Constants.java index 9054786..6c03b9e 100644 --- a/src/main/java/io/wwan13/wintersecurity/constant/Constants.java +++ b/src/main/java/io/wwan13/wintersecurity/constant/Constants.java @@ -29,6 +29,9 @@ public class Constants { public static final String PAYLOAD_KEY_USER_ROLE = "roles"; public static final String DEFAULT_SUBJECT_KEY = "sub"; + // Attribute + public static final String ATTRIBUTE_CLAIMS_KEY = "claims"; + // Token public static final String TOKEN_TYPE_ACCESS = "access_token"; public static final String TOKEN_TYPE_REFRESH = "refresh_token"; diff --git a/src/main/java/io/wwan13/wintersecurity/jwt/TokenClaims.java b/src/main/java/io/wwan13/wintersecurity/jwt/TokenClaims.java new file mode 100644 index 0000000..280f906 --- /dev/null +++ b/src/main/java/io/wwan13/wintersecurity/jwt/TokenClaims.java @@ -0,0 +1,65 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.wwan13.wintersecurity.jwt; + +import io.wwan13.wintersecurity.constant.Constants; +import io.wwan13.wintersecurity.jwt.payload.util.RoleSerializer; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public record TokenClaims( + Map claims +) { + + public Object getSubject() { + return getValueWithNullChecking(Constants.DEFAULT_SUBJECT_KEY); + } + + public Set getRoles() { + String rawRoles = (String) getValueWithNullChecking(Constants.PAYLOAD_KEY_USER_ROLE); + return RoleSerializer.deserialize(rawRoles); + } + + public boolean isAccessToken() { + String tokenType = (String) getValueWithNullChecking(Constants.PAYLOAD_KEY_TOKEN_TYPE); + return Constants.TOKEN_TYPE_ACCESS.equals(tokenType); + } + + public boolean isRefreshToken() { + String tokenType = (String) getValueWithNullChecking(Constants.PAYLOAD_KEY_TOKEN_TYPE); + return Constants.TOKEN_TYPE_REFRESH.equals(tokenType); + } + + public Object get(String key) { + return getValueWithNullChecking(key); + } + + public Map toMap() { + return new HashMap<>(claims); + } + + private Object getValueWithNullChecking(String key) { + Object value = claims.get(key); + if (Objects.isNull(value)) { + throw new IllegalArgumentException(key + "not exists at token claims"); + } + return value; + } +} diff --git a/src/main/java/io/wwan13/wintersecurity/jwt/TokenDecoder.java b/src/main/java/io/wwan13/wintersecurity/jwt/TokenDecoder.java index b72dc33..daa483f 100644 --- a/src/main/java/io/wwan13/wintersecurity/jwt/TokenDecoder.java +++ b/src/main/java/io/wwan13/wintersecurity/jwt/TokenDecoder.java @@ -16,8 +16,6 @@ package io.wwan13.wintersecurity.jwt; -import java.util.Map; - public interface TokenDecoder { - Map decode(String token); + TokenClaims decode(String token); } diff --git a/src/main/java/io/wwan13/wintersecurity/jwt/provider/JwtTokenDecoder.java b/src/main/java/io/wwan13/wintersecurity/jwt/provider/JwtTokenDecoder.java index d0cefe4..9e58949 100644 --- a/src/main/java/io/wwan13/wintersecurity/jwt/provider/JwtTokenDecoder.java +++ b/src/main/java/io/wwan13/wintersecurity/jwt/provider/JwtTokenDecoder.java @@ -19,6 +19,7 @@ import io.jsonwebtoken.*; import io.wwan13.wintersecurity.exception.unauthirized.ExpiredJwtTokenException; import io.wwan13.wintersecurity.exception.unauthirized.InvalidJwtTokenException; +import io.wwan13.wintersecurity.jwt.TokenClaims; import io.wwan13.wintersecurity.jwt.TokenDecoder; import io.wwan13.wintersecurity.secretkey.SecretKey; @@ -34,8 +35,9 @@ public JwtTokenDecoder(SecretKey secretKey) { } @Override - public Map decode(String token) { - return parseClaimsWithExceptionHandling(token); + public TokenClaims decode(String token) { + Map claims = parseClaimsWithExceptionHandling(token); + return new TokenClaims(claims); } public Claims parseClaimsWithExceptionHandling(String token) { diff --git a/src/main/java/io/wwan13/wintersecurity/resolve/RequestUserClaim.java b/src/main/java/io/wwan13/wintersecurity/resolve/RequestUserClaim.java deleted file mode 100644 index 1b6954f..0000000 --- a/src/main/java/io/wwan13/wintersecurity/resolve/RequestUserClaim.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.wwan13.wintersecurity.resolve; - -import java.lang.annotation.*; - -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface RequestUserClaim { - String value() default ""; -} diff --git a/src/main/java/io/wwan13/wintersecurity/resolve/RequestUserClaims.java b/src/main/java/io/wwan13/wintersecurity/resolve/RequestUserClaims.java deleted file mode 100644 index fc83f1b..0000000 --- a/src/main/java/io/wwan13/wintersecurity/resolve/RequestUserClaims.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.wwan13.wintersecurity.resolve; - -import java.lang.annotation.*; - -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface RequestUserClaims { -} diff --git a/src/main/java/io/wwan13/wintersecurity/resolve/RequestUserPayload.java b/src/main/java/io/wwan13/wintersecurity/resolve/RequestUserPayload.java deleted file mode 100644 index 742ce70..0000000 --- a/src/main/java/io/wwan13/wintersecurity/resolve/RequestUserPayload.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.wwan13.wintersecurity.resolve; - -import java.lang.annotation.*; - -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface RequestUserPayload { -} diff --git a/src/main/java/io/wwan13/wintersecurity/resolve/TargetAnnotations.java b/src/main/java/io/wwan13/wintersecurity/resolve/TargetAnnotations.java index 2eea4bf..cad2077 100644 --- a/src/main/java/io/wwan13/wintersecurity/resolve/TargetAnnotations.java +++ b/src/main/java/io/wwan13/wintersecurity/resolve/TargetAnnotations.java @@ -21,9 +21,6 @@ public record TargetAnnotations( Set> forSubject, - Set> forRoles, - Set> forClaims, - Set> forClaim, - Set> forPayload + Set> forRoles ) { } diff --git a/src/main/java/io/wwan13/wintersecurity/resolve/support/TargetAnnotationsRegistry.java b/src/main/java/io/wwan13/wintersecurity/resolve/support/TargetAnnotationsRegistry.java index 5ff1e73..290458b 100644 --- a/src/main/java/io/wwan13/wintersecurity/resolve/support/TargetAnnotationsRegistry.java +++ b/src/main/java/io/wwan13/wintersecurity/resolve/support/TargetAnnotationsRegistry.java @@ -28,25 +28,13 @@ public class TargetAnnotationsRegistry { = Set.of(RequestUserSubject.class, RequestUserId.class); private final static Set> DEFAULT_ROLES_TARGETS = Set.of(RequestUserRoles.class); - private final static Set> DEFAULT_CLAIMS_TARGETS - = Set.of(RequestUserClaims.class); - private final static Set> DEFAULT_CLAIM_TARGETS - = Set.of(RequestUserClaim.class); - private final static Set> DEFAULT_PAYLOAD_TARGETS - = Set.of(RequestUserPayload.class); private final Set> forSubject; private final Set> forRoles; - private final Set> forClaims; - private final Set> forClaim; - private final Set> forPayload; public TargetAnnotationsRegistry() { this.forSubject = new HashSet<>(DEFAULT_SUBJECT_TARGETS); this.forRoles = new HashSet<>(DEFAULT_ROLES_TARGETS); - this.forClaims = new HashSet<>(DEFAULT_CLAIMS_TARGETS); - this.forClaim = new HashSet<>(DEFAULT_CLAIM_TARGETS); - this.forPayload = new HashSet<>(DEFAULT_PAYLOAD_TARGETS); } public TargetAnnotationsRegistry addSubjectResolveAnnotation( @@ -63,28 +51,7 @@ public TargetAnnotationsRegistry addRolesResolveAnnotation( return this; } - public TargetAnnotationsRegistry addClaimsResolveAnnotation( - Class target - ) { - forClaims.add(target); - return this; - } - - public TargetAnnotationsRegistry addClaimResolveAnnotation( - Class target - ) { - forClaim.add(target); - return this; - } - - public TargetAnnotationsRegistry addPayloadResolveAnnotation( - Class target - ) { - forPayload.add(target); - return this; - } - protected TargetAnnotations apply() { - return new TargetAnnotations(forSubject, forRoles, forClaims, forClaim, forPayload); + return new TargetAnnotations(forSubject, forRoles); } } diff --git a/src/test/java/io/wwan13/wintersecurity/jwt/TokenClaimsTest.java b/src/test/java/io/wwan13/wintersecurity/jwt/TokenClaimsTest.java new file mode 100644 index 0000000..d005d8f --- /dev/null +++ b/src/test/java/io/wwan13/wintersecurity/jwt/TokenClaimsTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.wwan13.wintersecurity.jwt; + +import io.wwan13.wintersecurity.UnitTest; +import org.junit.jupiter.api.Test; + +import java.util.Map; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class TokenClaimsTest extends UnitTest { + + static final Map defaultClaims = Map.of( + "sub", "subject", + "roles", "ROLE_USER&ROLE_ADMIN", + "token_type", "refresh_token" + ); + + static final Map emptyClaims = Map.of(); + + @Test + void should_ContainsSubject() { + // given + final TokenClaims tokenClaims = new TokenClaims(defaultClaims); + + // when + Object subject = tokenClaims.getSubject(); + + // then + assertThat((String) subject).isEqualTo(defaultClaims.get("sub")); + } + + @Test + void should_ThrowException_when_SubjectNotExists() { + // given + final TokenClaims tokenClaims = new TokenClaims(emptyClaims); + + // when, then + assertThatThrownBy(tokenClaims::getSubject) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void should_ContainsRoles() { + // given + final TokenClaims tokenClaims = new TokenClaims(defaultClaims); + + // when + Set subject = tokenClaims.getRoles(); + + // then + assertThat(subject).contains("ROLE_ADMIN", "ROLE_USER"); + } + + @Test + void should_ThrowException_when_RolesNotExists() { + // given + final TokenClaims tokenClaims = new TokenClaims(emptyClaims); + + // when, then + assertThatThrownBy(tokenClaims::getRoles) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void should_JudgeTokenType() { + // given + final TokenClaims tokenClaims = new TokenClaims(defaultClaims); + + // when + boolean isAccessToken = tokenClaims.isAccessToken(); + boolean isRefreshToken = tokenClaims.isRefreshToken(); + + // then + assertThat(isAccessToken).isFalse(); + assertThat(isRefreshToken).isTrue(); + } +} \ No newline at end of file diff --git a/src/test/java/io/wwan13/wintersecurity/jwt/provider/JwtTokenDecoderTest.java b/src/test/java/io/wwan13/wintersecurity/jwt/provider/JwtTokenDecoderTest.java index 4957db4..302a6da 100644 --- a/src/test/java/io/wwan13/wintersecurity/jwt/provider/JwtTokenDecoderTest.java +++ b/src/test/java/io/wwan13/wintersecurity/jwt/provider/JwtTokenDecoderTest.java @@ -19,6 +19,7 @@ import io.wwan13.wintersecurity.exception.unauthirized.ExpiredJwtTokenException; import io.wwan13.wintersecurity.exception.unauthirized.InvalidJwtTokenException; import io.wwan13.wintersecurity.jwt.JwtProperties; +import io.wwan13.wintersecurity.jwt.TokenClaims; import io.wwan13.wintersecurity.jwt.TokenDecoder; import io.wwan13.wintersecurity.jwt.TokenGenerator; import io.wwan13.wintersecurity.jwt.payload.util.RoleSerializer; @@ -27,7 +28,6 @@ import io.wwan13.wintersecurity.secretkey.SecretKey; import org.junit.jupiter.api.Test; -import java.util.Map; import java.util.Objects; import java.util.Set; @@ -49,7 +49,7 @@ void should_DecodeToken() { String accessToken = tokenGenerator.accessToken(payload); // when - Map decodedClaims = tokenDecoder.decode(accessToken); + TokenClaims decodedClaims = tokenDecoder.decode(accessToken); // then assertThat(decodedClaims.get("sub")).isEqualTo(Objects.toString(id)); diff --git a/src/test/java/io/wwan13/wintersecurity/resolve/TargetAnnotationsTest.java b/src/test/java/io/wwan13/wintersecurity/resolve/TargetAnnotationsTest.java index fbe30d5..530bf48 100644 --- a/src/test/java/io/wwan13/wintersecurity/resolve/TargetAnnotationsTest.java +++ b/src/test/java/io/wwan13/wintersecurity/resolve/TargetAnnotationsTest.java @@ -31,25 +31,16 @@ void should_ContainsTargetAnnotationInfo() { // given final Class forSubject = RequestUserSubject.class; final Class forRoles = RequestUserRoles.class; - final Class forClaims = RequestUserClaims.class; - final Class forClaim = RequestUserClaim.class; - final Class forPayload = RequestUserPayload.class; // when TargetAnnotations targetAnnotations = new TargetAnnotations( Set.of(forSubject), - Set.of(forRoles), - Set.of(forClaims), - Set.of(forClaim), - Set.of(forPayload) + Set.of(forRoles) ); // then assertThat(targetAnnotations).isInstanceOf(TargetAnnotations.class); assertThat(targetAnnotations.forSubject()).contains(forSubject); assertThat(targetAnnotations.forRoles()).contains(forRoles); - assertThat(targetAnnotations.forClaims()).contains(forClaims); - assertThat(targetAnnotations.forClaim()).contains(forClaim); - assertThat(targetAnnotations.forPayload()).contains(forPayload); } } \ No newline at end of file diff --git a/src/test/java/io/wwan13/wintersecurity/resolve/support/TargetAnnotationsRegistryTest.java b/src/test/java/io/wwan13/wintersecurity/resolve/support/TargetAnnotationsRegistryTest.java index 5968611..c0af7ed 100644 --- a/src/test/java/io/wwan13/wintersecurity/resolve/support/TargetAnnotationsRegistryTest.java +++ b/src/test/java/io/wwan13/wintersecurity/resolve/support/TargetAnnotationsRegistryTest.java @@ -17,10 +17,7 @@ package io.wwan13.wintersecurity.resolve.support; import io.wwan13.wintersecurity.UnitTest; -import io.wwan13.wintersecurity.resolve.RequestUserClaim; -import io.wwan13.wintersecurity.resolve.RequestUserClaims; import io.wwan13.wintersecurity.resolve.RequestUserId; -import io.wwan13.wintersecurity.resolve.RequestUserPayload; import io.wwan13.wintersecurity.resolve.RequestUserRoles; import io.wwan13.wintersecurity.resolve.RequestUserSubject; import io.wwan13.wintersecurity.resolve.TargetAnnotations; @@ -37,17 +34,11 @@ void should_CreateTargetAnnotationsObject_when_Apply() { // given final Class forSubject = RequestUserSubject.class; final Class forRoles = RequestUserRoles.class; - final Class forClaims = RequestUserClaims.class; - final Class forClaim = RequestUserClaim.class; - final Class forPayload = RequestUserPayload.class; TargetAnnotationsRegistry registry = new TargetAnnotationsRegistry(); registry .addSubjectResolveAnnotation(forSubject) - .addRolesResolveAnnotation(forRoles) - .addClaimsResolveAnnotation(forClaims) - .addClaimResolveAnnotation(forClaim) - .addPayloadResolveAnnotation(forPayload); + .addRolesResolveAnnotation(forRoles); // when TargetAnnotations targetAnnotations = registry.apply(); @@ -56,9 +47,6 @@ void should_CreateTargetAnnotationsObject_when_Apply() { assertThat(targetAnnotations).isInstanceOf(TargetAnnotations.class); assertThat(targetAnnotations.forSubject()).contains(forSubject); assertThat(targetAnnotations.forRoles()).contains(forRoles); - assertThat(targetAnnotations.forClaims()).contains(forClaims); - assertThat(targetAnnotations.forClaim()).contains(forClaim); - assertThat(targetAnnotations.forPayload()).contains(forPayload); } @Test @@ -73,8 +61,5 @@ void should_ContainsDefaultTargetAnnotations_when_NotingEntered() { assertThat(targetAnnotations.forSubject()) .contains(RequestUserSubject.class, RequestUserId.class); assertThat(targetAnnotations.forRoles()).contains(RequestUserRoles.class); - assertThat(targetAnnotations.forClaims()).contains(RequestUserClaims.class); - assertThat(targetAnnotations.forClaim()).contains(RequestUserClaim.class); - assertThat(targetAnnotations.forPayload()).contains(RequestUserPayload.class); } } \ No newline at end of file