7 changed files with 175 additions and 64 deletions
@ -0,0 +1,72 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2023 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 |
||||||
|
* |
||||||
|
* https://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 org.springframework.security.oauth2.server.authorization.web.authentication; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import jakarta.servlet.ServletException; |
||||||
|
import jakarta.servlet.http.HttpServletRequest; |
||||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||||
|
import org.apache.commons.logging.Log; |
||||||
|
import org.apache.commons.logging.LogFactory; |
||||||
|
import org.springframework.core.log.LogMessage; |
||||||
|
import org.springframework.http.HttpStatus; |
||||||
|
import org.springframework.http.converter.HttpMessageConverter; |
||||||
|
import org.springframework.http.server.ServletServerHttpResponse; |
||||||
|
import org.springframework.security.core.AuthenticationException; |
||||||
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; |
||||||
|
import org.springframework.security.oauth2.core.OAuth2Error; |
||||||
|
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter; |
||||||
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler; |
||||||
|
|
||||||
|
/** |
||||||
|
* A default implementation of an {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} |
||||||
|
* and returning the {@link OAuth2Error Error Response}. |
||||||
|
* |
||||||
|
* @author Dmitriy Dubson |
||||||
|
* @see AuthenticationFailureHandler |
||||||
|
* @since 1.2.0 |
||||||
|
*/ |
||||||
|
public final class OAuth2ErrorAuthenticationFailureHandler implements AuthenticationFailureHandler { |
||||||
|
|
||||||
|
private final Log logger = LogFactory.getLog(getClass()); |
||||||
|
|
||||||
|
private HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException { |
||||||
|
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); |
||||||
|
httpResponse.setStatusCode(HttpStatus.BAD_REQUEST); |
||||||
|
|
||||||
|
if (authenticationException instanceof OAuth2AuthenticationException) { |
||||||
|
OAuth2Error error = ((OAuth2AuthenticationException) authenticationException).getError(); |
||||||
|
this.errorHttpResponseConverter.write(error, null, httpResponse); |
||||||
|
} else { |
||||||
|
if (this.logger.isWarnEnabled()) { |
||||||
|
this.logger.warn(LogMessage.format("Authentication exception must be of type 'org.springframework.security.oauth2.core.OAuth2AuthenticationException'. Provided exception was '%s'", authenticationException.getClass().getName())); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets OAuth error HTTP message converter to write to upon authentication failure |
||||||
|
* |
||||||
|
* @param errorHttpResponseConverter the error HTTP message converter to set |
||||||
|
*/ |
||||||
|
public void setErrorHttpResponseConverter(HttpMessageConverter<OAuth2Error> errorHttpResponseConverter) { |
||||||
|
this.errorHttpResponseConverter = errorHttpResponseConverter; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,89 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2023 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 |
||||||
|
* |
||||||
|
* https://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 org.springframework.security.oauth2.server.authorization.web.authentication; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import jakarta.servlet.ServletException; |
||||||
|
import jakarta.servlet.http.HttpServletRequest; |
||||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.springframework.http.HttpStatus; |
||||||
|
import org.springframework.http.converter.HttpMessageConverter; |
||||||
|
import org.springframework.http.server.ServletServerHttpResponse; |
||||||
|
import org.springframework.mock.web.MockHttpServletRequest; |
||||||
|
import org.springframework.mock.web.MockHttpServletResponse; |
||||||
|
import org.springframework.security.authentication.BadCredentialsException; |
||||||
|
import org.springframework.security.core.AuthenticationException; |
||||||
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; |
||||||
|
import org.springframework.security.oauth2.core.OAuth2Error; |
||||||
|
import org.springframework.security.oauth2.core.OAuth2ErrorCodes; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.mockito.ArgumentMatchers.any; |
||||||
|
import static org.mockito.ArgumentMatchers.eq; |
||||||
|
import static org.mockito.ArgumentMatchers.isNull; |
||||||
|
import static org.mockito.Mockito.mock; |
||||||
|
import static org.mockito.Mockito.verify; |
||||||
|
import static org.mockito.Mockito.verifyNoInteractions; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link OAuth2ErrorAuthenticationFailureHandler} |
||||||
|
* |
||||||
|
* @author Dmitriy Dubson |
||||||
|
*/ |
||||||
|
public class OAuth2ErrorAuthenticationFailureHandlerTests { |
||||||
|
|
||||||
|
private HttpMessageConverter<OAuth2Error> errorHttpMessageConverter; |
||||||
|
|
||||||
|
private HttpServletRequest request; |
||||||
|
|
||||||
|
private HttpServletResponse response; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public void setUp() { |
||||||
|
errorHttpMessageConverter = (HttpMessageConverter<OAuth2Error>) mock(HttpMessageConverter.class); |
||||||
|
request = new MockHttpServletRequest(); |
||||||
|
response = new MockHttpServletResponse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void onAuthenticationFailure() throws IOException, ServletException { |
||||||
|
OAuth2Error invalidRequestError = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST); |
||||||
|
AuthenticationException authenticationException = new OAuth2AuthenticationException(invalidRequestError); |
||||||
|
OAuth2ErrorAuthenticationFailureHandler handler = new OAuth2ErrorAuthenticationFailureHandler(); |
||||||
|
handler.setErrorHttpResponseConverter(errorHttpMessageConverter); |
||||||
|
|
||||||
|
handler.onAuthenticationFailure(request, response, authenticationException); |
||||||
|
|
||||||
|
verify(errorHttpMessageConverter).write(eq(invalidRequestError), isNull(), any(ServletServerHttpResponse.class)); |
||||||
|
assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void onAuthenticationFailure_ifExceptionProvidedIsNotOAuth2AuthenticationException() throws ServletException, IOException { |
||||||
|
OAuth2ErrorAuthenticationFailureHandler handler = new OAuth2ErrorAuthenticationFailureHandler(); |
||||||
|
handler.setErrorHttpResponseConverter(errorHttpMessageConverter); |
||||||
|
|
||||||
|
handler.onAuthenticationFailure(request, response, new BadCredentialsException("Not a valid exception.")); |
||||||
|
|
||||||
|
verifyNoInteractions(errorHttpMessageConverter); |
||||||
|
assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue