Skip to content

Commit f542bcb

Browse files
feature : Create more selective pointcuts (refer to README)
1 parent d8503dc commit f542bcb

File tree

53 files changed

+430
-215
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+430
-215
lines changed

README.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
<dependency>
77
<groupId>io.github.patternknife.securityhelper.oauth2.api</groupId>
88
<artifactId>spring-security-oauth2-password-jpa-implementation</artifactId>
9-
<version>2.5.0</version>
9+
<version>2.6.0</version>
1010
</dependency>
1111
```
12-
* Set up the same access & refresh token APIs on both ``/oauth2/token`` and on our controller layer such as ``/api/v1/traditional-oauth/token``, both of which function same and have `the same request & response payloads for success and errors`.
12+
* Set up the same access & refresh token APIs on both ``/oauth2/token`` and on our controller layer such as ``/api/v1/traditional-oauth/token``, both of which function same and have `the same request & response payloads for success and errors`. (However, ``/oauth2/token`` is the standard that "spring-authorization-server" provides.)
1313
* As you are aware, the API ``/oauth2/token`` is what "spring-authorization-server" provides.
14-
* ``/api/v1/traditional-oauth/token`` is what this library implemented manually.
14+
* ``/api/v1/traditional-oauth/token`` is what this library implemented directly.
1515
* Success Payload
1616
```json
1717
{
@@ -129,10 +129,18 @@ public class CommonDataSourceConfiguration {
129129
- **Register error user messages as desired**
130130
- ``ISecurityUserExceptionMessageService``
131131
- See the source code in ``client.config.securityimpl.message``
132-
- **Customize the whole error payload as desired**
133-
- Customize only two points in
134-
- ``client.config.securityimpl.errorhandler.CustomAuthenticationFailureHandlerImpl``
135-
- ``client.config.response.error.GlobalExceptionHandler``
132+
- **Customize the whole error payload as desired for all cases**
133+
- What is "all cases"?
134+
- Authorization Server ("/oauth2/token", "/api/v1/traditional-oauth/token") and Resource Server (Bearer token inspection : 401, Permission : 403)
135+
- Customize two points such as
136+
- ``client.config.securityimpl.response.CustomAuthenticationFailureHandlerImpl`` ("/oauth2/token")
137+
- ``client.config.response.error.GlobalExceptionHandler`` ("/api/v1/traditional-oauth/token", Resource Server (Bearer token inspection))
138+
- ``client.config.securityimpl.response.CustomAuthenticationEntryPointImpl`` (Resource Server (Bearer token inspection : 401))
139+
- ``client.config.securityimpl.response.CustomAccessDeniedHandlerImpl`` (Resource Server (Permission inspection : 403))
140+
- **Customize the whole success payload as desired for the only "/oauth2/token"**
141+
- ``client.config.securityimpl.response.CustomAuthenticationSuccessHandlerImpl``
142+
- The success response payload of "/api/v1/traditional-oauth/token" is in ``api.domain.traditionaloauth.dto``, which doesn't yet to be customizable.
143+
136144
## Running this App with Docker
137145
* Use the following module for Blue-Green deployment:
138146
* https://github.com/patternknife/docker-blue-green-runner

client/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
77
<modelVersion>4.0.0</modelVersion>
88
<groupId>com.patternknife.securityhelper.oauth2.client</groupId>
99
<artifactId>spring-security-oauth2-password-jpa-implementation-client</artifactId>
10-
<version>2.5.0</version>
10+
<version>2.6.0</version>
1111
<packaging>jar</packaging>
1212

1313
<properties>
@@ -41,7 +41,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
4141
<dependency>
4242
<groupId>io.github.patternknife.securityhelper.oauth2.api</groupId>
4343
<artifactId>spring-security-oauth2-password-jpa-implementation</artifactId>
44-
<version>2.5.0</version>
44+
<version>2.6.0</version>
4545
</dependency>
4646

4747
<!-- DB -->

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/response/error/GlobalExceptionHandler.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
import com.patternknife.securityhelper.oauth2.client.config.response.error.dto.CustomErrorResponsePayload;
55

66
import com.patternknife.securityhelper.oauth2.client.config.response.error.message.GeneralErrorMessage;
7-
import io.github.patternknife.securityhelper.oauth2.api.config.response.error.ExceptionKnifeUtils;
8-
import io.github.patternknife.securityhelper.oauth2.api.config.response.error.dto.ErrorResponsePayload;
9-
import io.github.patternknife.securityhelper.oauth2.api.config.response.error.exception.auth.KnifeOauth2AuthenticationException;
7+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.handler.SecurityKnifeExceptionHandler;
8+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.util.ExceptionKnifeUtils;
9+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.ErrorResponsePayload;
10+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
1011
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
1112
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
13+
import io.github.patternknife.securityhelper.oauth2.api.config.security.util.OrderConstants;
1214
import lombok.RequiredArgsConstructor;;
15+
import org.springframework.core.Ordered;
16+
import org.springframework.core.annotation.Order;
1317
import org.springframework.http.HttpStatus;
1418
import org.springframework.http.ResponseEntity;
1519
import org.springframework.security.access.AccessDeniedException;
@@ -23,11 +27,12 @@
2327
/*
2428
*
2529
* Customize the exception payload by implementing this, which replaces
26-
* 'io.github.patternknife.securityhelper.oauth2.api.config.response.error.SecurityKnifeExceptionHandler'
30+
* 'io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.handler.SecurityKnifeExceptionHandler'
2731
*
28-
* Once you create 'GlobalExceptionHandler', you should insert the following two as default. Otherwise, 'unhandledExceptionHandler' is prior to 'io.github.patternknife.securityhelper.oauth2.api.config.response.error.SecurityKnifeExceptionHandler'.
32+
* Once you create 'GlobalExceptionHandler', you should insert the following two as default. Otherwise, 'unhandledExceptionHandler' is prior to 'io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.handler.SecurityKnifeExceptionHandler'.
2933
*
3034
* */
35+
@Order(OrderConstants.SECURITY_KNIFE_EXCEPTION_HANDLER_ORDER - 1)
3136
@ControllerAdvice
3237
@RequiredArgsConstructor
3338
public class GlobalExceptionHandler {

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/response/error/dto/CustomErrorResponsePayload.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33

44
import com.fasterxml.jackson.annotation.JsonIgnore;
5-
import io.github.patternknife.securityhelper.oauth2.api.config.response.TimestampUtil;
6-
import io.github.patternknife.securityhelper.oauth2.api.config.response.error.dto.ErrorMessages;
5+
import io.github.patternknife.securityhelper.oauth2.api.config.util.TimestampUtil;
6+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.ErrorMessages;
77

88
import lombok.ToString;
99
import org.apache.commons.lang3.StringUtils;

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/response/error/exception/ErrorMessagesContainedException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.patternknife.securityhelper.oauth2.client.config.response.error.exception;
22

3-
import io.github.patternknife.securityhelper.oauth2.api.config.response.error.dto.ErrorMessages;
3+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.ErrorMessages;
44

55
public abstract class ErrorMessagesContainedException extends RuntimeException {
66

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/response/error/exception/data/ResourceNotFoundException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.patternknife.securityhelper.oauth2.client.config.response.error.exception.data;
22

3-
import io.github.patternknife.securityhelper.oauth2.api.config.response.error.dto.ErrorMessages;
3+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.ErrorMessages;
44
import com.patternknife.securityhelper.oauth2.client.config.response.error.exception.ErrorMessagesContainedException;
55
import org.springframework.http.HttpStatus;
66
import org.springframework.web.bind.annotation.ResponseStatus;

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/securityimpl/aop/SecurityPointCutImpl.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33

44
import io.github.patternknife.securityhelper.oauth2.api.config.security.aop.SecurityPointCut;
5-
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.CustomOauthAccessToken;
6-
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.CustomOauthRefreshToken;
7-
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.OauthClientDetail;
5+
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.KnifeOauthAccessToken;
6+
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.KnifeOauthRefreshToken;
7+
import io.github.patternknife.securityhelper.oauth2.api.config.security.entity.KnifeOauthClientDetail;
88

99
import jakarta.annotation.Nullable;
1010
import lombok.RequiredArgsConstructor;
@@ -16,7 +16,7 @@
1616
public class SecurityPointCutImpl implements SecurityPointCut {
1717

1818
@Override
19-
public <T> @Nullable T afterTokensSaved(@Nullable CustomOauthAccessToken customOauthAccessToken, @Nullable CustomOauthRefreshToken customOauthRefreshToken, @Nullable OauthClientDetail oauthClientDetail) {
19+
public <T> @Nullable T afterTokensSaved(@Nullable KnifeOauthAccessToken knifeOauthAccessToken, @Nullable KnifeOauthRefreshToken knifeOauthRefreshToken, @Nullable KnifeOauthClientDetail knifeOauthClientDetail) {
2020

2121
// Implement what you need right after tokens are persisted.
2222
return null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.response;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import jakarta.servlet.http.HttpServletResponse;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.beans.factory.annotation.Qualifier;
7+
import org.springframework.context.annotation.Configuration;
8+
import org.springframework.security.access.AccessDeniedException;
9+
import org.springframework.security.web.access.AccessDeniedHandler;
10+
import org.springframework.web.servlet.HandlerExceptionResolver;
11+
12+
import java.io.IOException;
13+
14+
@Configuration
15+
@RequiredArgsConstructor
16+
public class CustomAccessDeniedHandlerImpl implements AccessDeniedHandler {
17+
18+
@Qualifier("handlerExceptionResolver")
19+
private final HandlerExceptionResolver resolver;
20+
21+
@Override
22+
public void handle(HttpServletRequest request, HttpServletResponse response,
23+
AccessDeniedException accessDeniedException) throws IOException {
24+
25+
resolver.resolveException(request, response, null, accessDeniedException);
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.response;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import jakarta.servlet.http.HttpServletResponse;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.beans.factory.annotation.Qualifier;
7+
import org.springframework.context.annotation.Configuration;
8+
import org.springframework.security.core.AuthenticationException;
9+
import org.springframework.security.web.AuthenticationEntryPoint;
10+
import org.springframework.web.servlet.HandlerExceptionResolver;
11+
12+
import java.io.IOException;
13+
14+
@Configuration
15+
@RequiredArgsConstructor
16+
public class CustomAuthenticationEntryPointImpl implements AuthenticationEntryPoint {
17+
18+
@Qualifier("handlerExceptionResolver")
19+
private final HandlerExceptionResolver resolver;
20+
21+
@Override
22+
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException {
23+
resolver.resolveException(request, response, null, ex);
24+
}
25+
}
Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.errorhandler;
1+
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.response;
22

33

44
import com.fasterxml.jackson.databind.ObjectMapper;
5-
import io.github.patternknife.securityhelper.oauth2.api.config.logger.module.CustomSecurityLogConfig;
6-
import io.github.patternknife.securityhelper.oauth2.api.config.response.error.ExceptionKnifeUtils;
7-
import io.github.patternknife.securityhelper.oauth2.api.config.response.error.dto.ErrorResponsePayload;
8-
import io.github.patternknife.securityhelper.oauth2.api.config.response.error.exception.auth.KnifeOauth2AuthenticationException;
5+
import io.github.patternknife.securityhelper.oauth2.api.config.logger.KnifeSecurityLogConfig;
96
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
107
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
8+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.ErrorResponsePayload;
9+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
10+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.util.ExceptionKnifeUtils;
1111
import jakarta.servlet.http.HttpServletRequest;
1212
import jakarta.servlet.http.HttpServletResponse;
1313
import lombok.RequiredArgsConstructor;
@@ -19,21 +19,20 @@
1919
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
2020
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
2121

22-
2322
import java.io.IOException;
2423

2524

2625
/*
27-
*
28-
* Customize the exception payload by implementing this, which replaces
29-
* 'io.github.patternknife.securityhelper.oauth2.api.security.errorhandler.auth.authentication.DefaultAuthenticationFailureHandlerImpl'
30-
*
31-
* */
26+
*
27+
* Customize the exception payload by implementing this, which replaces
28+
* 'io.github.patternknife.securityhelper.oauth2.api.security.response.auth.authentication.CustomAuthenticationFailureHandlerImpl'
29+
*
30+
* */
3231
@Configuration
3332
@RequiredArgsConstructor
3433
public class CustomAuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
3534

36-
private static final Logger logger = LoggerFactory.getLogger(CustomSecurityLogConfig.class);
35+
private static final Logger logger = LoggerFactory.getLogger(KnifeSecurityLogConfig.class);
3736

3837
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
3938

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.response;
2+
3+
4+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
5+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
6+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.ErrorMessages;
7+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
8+
import jakarta.servlet.http.HttpServletRequest;
9+
import jakarta.servlet.http.HttpServletResponse;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.http.converter.HttpMessageConverter;
13+
import org.springframework.http.server.ServletServerHttpResponse;
14+
import org.springframework.security.core.Authentication;
15+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
16+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
17+
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
18+
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
19+
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
20+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
21+
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
22+
23+
import java.io.IOException;
24+
import java.time.Instant;
25+
import java.time.temporal.ChronoUnit;
26+
import java.util.Map;
27+
28+
@Configuration
29+
@RequiredArgsConstructor
30+
public class CustomAuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
31+
32+
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
33+
new OAuth2AccessTokenResponseHttpMessageConverter();
34+
35+
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
36+
37+
@Override
38+
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
39+
40+
41+
final OAuth2AccessTokenAuthenticationToken accessTokenAuthentication=(OAuth2AccessTokenAuthenticationToken)authentication;
42+
43+
OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
44+
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
45+
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
46+
/* Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
47+
48+
// Lookup the authorization using the access token
49+
OAuth2Authorization authorization = this.authorizationService.findByToken(
50+
accessToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN);
51+
52+
Map<String, Object> opaqueTokenClaims = authorization.getAccessToken().getClaims();
53+
Authentication userPrincipal = authorization.getAttribute(Principal.class.getName());*/
54+
55+
OAuth2AccessTokenResponse.Builder builder =
56+
OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
57+
.tokenType(accessToken.getTokenType())
58+
.scopes(accessToken.getScopes());
59+
if(((String)additionalParameters.get("grant_type")).equals(AuthorizationGrantType.PASSWORD.getValue())){
60+
if(accessToken.getExpiresAt() != null) {
61+
builder.expiresIn(ChronoUnit.SECONDS.between(Instant.now(), accessToken.getExpiresAt()));
62+
}
63+
}else if(((String)additionalParameters.get("grant_type")).equals(AuthorizationGrantType.REFRESH_TOKEN.getValue())){
64+
assert refreshToken != null;
65+
if(refreshToken.getExpiresAt() != null) {
66+
builder.expiresIn(ChronoUnit.SECONDS.between(Instant.now(), refreshToken.getExpiresAt()));
67+
}
68+
}else{
69+
throw new KnifeOauth2AuthenticationException(ErrorMessages.builder().message("Wrong grant type from Req : " + (String)additionalParameters.get("grant_type")).userMessage(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_WRONG_GRANT_TYPE)).build());
70+
}
71+
72+
73+
if (refreshToken != null) {
74+
builder.refreshToken(refreshToken.getTokenValue());
75+
}
76+
/* if (!CollectionUtils.isEmpty(additionalParameters)) {
77+
builder.additionalParameters(additionalParameters);
78+
}*/
79+
80+
// TODO Add custom response parameters using `opaqueTokenClaims` and/or `userPrincipal`
81+
82+
83+
OAuth2AccessTokenResponse accessTokenResponse = builder.build();
84+
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
85+
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
86+
}
87+
}

0 commit comments

Comments
 (0)