Skip to content

Commit 6bfe9be

Browse files
feature : distinguish api/web for success/failure conditional beans
1 parent 0fec0d1 commit 6bfe9be

10 files changed

+337
-146
lines changed
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
import lombok.RequiredArgsConstructor;
1313
import org.slf4j.Logger;
1414
import org.slf4j.LoggerFactory;
15+
import org.springframework.beans.factory.annotation.Qualifier;
1516
import org.springframework.context.annotation.Configuration;
17+
import org.springframework.context.annotation.Primary;
1618
import org.springframework.http.HttpStatus;
1719
import org.springframework.security.core.AuthenticationException;
1820
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
@@ -28,9 +30,11 @@
2830
*
2931
* Create this class only if you need a custom implementation that differs from the default.
3032
*/
33+
@Primary
34+
@Qualifier("apiAuthenticationFailureHandler")
3135
@Configuration
3236
@RequiredArgsConstructor
33-
public class CustomAuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
37+
public class CustomApiAuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
3438

3539
private static final Logger logger = LoggerFactory.getLogger(KnifeSecurityLogConfig.class);
3640

Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import jakarta.servlet.http.HttpServletRequest;
99
import jakarta.servlet.http.HttpServletResponse;
1010
import lombok.RequiredArgsConstructor;
11+
import org.springframework.beans.factory.annotation.Qualifier;
1112
import org.springframework.context.annotation.Configuration;
13+
import org.springframework.context.annotation.Primary;
1214
import org.springframework.http.converter.HttpMessageConverter;
1315
import org.springframework.http.server.ServletServerHttpResponse;
1416
import org.springframework.security.core.Authentication;
@@ -33,9 +35,11 @@
3335
*
3436
* Create this class only if you need a custom implementation that differs from the default.
3537
*/
38+
@Primary
39+
@Qualifier("apiAuthenticationSuccessHandler")
3640
@Configuration
3741
@RequiredArgsConstructor
38-
public class CustomAuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
42+
public class CustomApiAuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
3943

4044
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
4145
new OAuth2AccessTokenResponseHttpMessageConverter();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.response;
2+
3+
4+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
5+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
6+
import jakarta.servlet.ServletException;
7+
import jakarta.servlet.http.HttpServletRequest;
8+
import jakarta.servlet.http.HttpServletResponse;
9+
import lombok.RequiredArgsConstructor;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
import org.springframework.beans.factory.annotation.Qualifier;
13+
import org.springframework.context.annotation.Configuration;
14+
import org.springframework.context.annotation.Primary;
15+
import org.springframework.security.core.AuthenticationException;
16+
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
17+
18+
import java.io.IOException;
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
@Primary
23+
@Qualifier("webAuthenticationFailureHandler")
24+
@Configuration
25+
@RequiredArgsConstructor
26+
public class CustomWebAuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
27+
28+
private static final Logger logger = LoggerFactory.getLogger(CustomWebAuthenticationFailureHandlerImpl.class);
29+
30+
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
31+
32+
@Override
33+
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
34+
// SecurityKnifeExceptionHandler does NOT handle this error.
35+
logger.error("Authentication failed: ", exception);
36+
37+
String errorMessage = "An unexpected error occurred.";
38+
List<String> errorDetails = new ArrayList<>();
39+
// Extract error messages if the exception is of type KnifeOauth2AuthenticationException
40+
if (exception instanceof KnifeOauth2AuthenticationException) {
41+
KnifeOauth2AuthenticationException oauth2Exception = (KnifeOauth2AuthenticationException) exception;
42+
errorMessage = oauth2Exception.getErrorMessages().getUserMessage();
43+
}
44+
45+
if(errorMessage.equals("Authorization code missing in GET request")){
46+
request.getRequestDispatcher("/login").forward(request, response);
47+
}else {
48+
49+
// Redirect to /error with query parameters
50+
request.setAttribute("errorMessage", errorMessage);
51+
request.setAttribute("errorDetails", errorDetails);
52+
53+
request.getRequestDispatcher("/error").forward(request, response);
54+
}
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.response;
2+
3+
4+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
5+
import jakarta.servlet.ServletException;
6+
import jakarta.servlet.http.HttpServletRequest;
7+
import jakarta.servlet.http.HttpServletResponse;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.beans.factory.annotation.Qualifier;
10+
import org.springframework.context.annotation.Configuration;
11+
import org.springframework.context.annotation.Primary;
12+
import org.springframework.security.core.Authentication;
13+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
14+
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
15+
16+
import java.io.IOException;
17+
18+
@Primary
19+
@Qualifier("webAuthenticationSuccessHandler")
20+
@Configuration
21+
@RequiredArgsConstructor
22+
public class CustomWebAuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
23+
24+
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
25+
26+
@Override
27+
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
28+
OAuth2AuthorizationCodeAuthenticationToken oAuth2AuthorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
29+
30+
String redirectUri = oAuth2AuthorizationCodeAuthenticationToken.getRedirectUri();
31+
String authorizationCode = oAuth2AuthorizationCodeAuthenticationToken.getCode();
32+
String state = oAuth2AuthorizationCodeAuthenticationToken.getAdditionalParameters().get("state").toString();
33+
34+
response.sendRedirect(redirectUri+"?code="+authorizationCode+"&state="+state);
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323

2424
@RequiredArgsConstructor
25-
public class DefaultAuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
25+
public class DefaultApiAuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
2626

2727
private static final Logger logger = LoggerFactory.getLogger(KnifeSecurityLogConfig.class);
2828

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package io.github.patternknife.securityhelper.oauth2.api.config.security.response.auth.authentication;
2+
3+
4+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.KnifeErrorMessages;
5+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
6+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
7+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
8+
import jakarta.servlet.http.HttpServletRequest;
9+
import jakarta.servlet.http.HttpServletResponse;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.http.converter.HttpMessageConverter;
12+
import org.springframework.http.server.ServletServerHttpResponse;
13+
import org.springframework.security.core.Authentication;
14+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
15+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
16+
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
17+
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
18+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
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+
@RequiredArgsConstructor
29+
public class DefaultApiAuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
30+
31+
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
32+
new OAuth2AccessTokenResponseHttpMessageConverter();
33+
34+
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
35+
36+
@Override
37+
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
38+
39+
final OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication;
40+
41+
OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
42+
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
43+
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
44+
45+
if (((String) additionalParameters.get("grant_type")).equals(AuthorizationGrantType.PASSWORD.getValue())
46+
|| ((String) additionalParameters.get("grant_type")).equals(OAuth2ParameterNames.CODE)) {
47+
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
48+
.tokenType(accessToken.getTokenType())
49+
.scopes(accessToken.getScopes());
50+
if (accessToken.getExpiresAt() != null) {
51+
builder.expiresIn(ChronoUnit.SECONDS.between(Instant.now(), accessToken.getExpiresAt()));
52+
}
53+
if (refreshToken != null) {
54+
builder.refreshToken(refreshToken.getTokenValue());
55+
}
56+
OAuth2AccessTokenResponse accessTokenResponse = builder.build();
57+
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
58+
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
59+
60+
} else if (((String) additionalParameters.get("grant_type")).equals(AuthorizationGrantType.REFRESH_TOKEN.getValue())) {
61+
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
62+
.tokenType(accessToken.getTokenType())
63+
.scopes(accessToken.getScopes());
64+
if (refreshToken.getExpiresAt() != null) {
65+
builder.expiresIn(ChronoUnit.SECONDS.between(Instant.now(), refreshToken.getExpiresAt()));
66+
}
67+
if (refreshToken != null) {
68+
builder.refreshToken(refreshToken.getTokenValue());
69+
}
70+
OAuth2AccessTokenResponse accessTokenResponse = builder.build();
71+
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
72+
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
73+
74+
} else if (((String) additionalParameters.get("grant_type")).equals(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())) {
75+
// Authorization Code만 JSON으로 응답
76+
String code = (String) additionalParameters.get("authorization_code");
77+
78+
// JSON 응답 생성 (authorization_code만 포함)
79+
String jsonResponse = String.format("{\"code\":\"%s\"}", code);
80+
81+
// JSON 응답 전송
82+
response.setContentType("application/json");
83+
response.setCharacterEncoding("UTF-8");
84+
response.getWriter().write(jsonResponse);
85+
86+
} else {
87+
throw new KnifeOauth2AuthenticationException(KnifeErrorMessages.builder()
88+
.message("Wrong grant type from Req : " + (String) additionalParameters.get("grant_type"))
89+
.userMessage(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_WRONG_GRANT_TYPE))
90+
.build());
91+
}
92+
}
93+
}

lib/src/main/java/io/github/patternknife/securityhelper/oauth2/api/config/security/response/auth/authentication/DefaultAuthenticationSuccessHandlerImpl.java

-85
This file was deleted.

0 commit comments

Comments
 (0)