Merge pull request #1610 from raehalme/KEYCLOAK-1828
KEYCLOAK-1828 attemptAuthentication throws KeycloakAuthenticationException if authentication fails
This commit is contained in:
commit
4eaf893492
3 changed files with 51 additions and 10 deletions
|
@ -0,0 +1,13 @@
|
|||
package org.keycloak.adapters.springsecurity;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
public class KeycloakAuthenticationException extends AuthenticationException {
|
||||
public KeycloakAuthenticationException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
|
||||
public KeycloakAuthenticationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import org.keycloak.adapters.AuthOutcome;
|
|||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.RequestAuthenticator;
|
||||
import org.keycloak.adapters.springsecurity.AdapterDeploymentContextBean;
|
||||
import org.keycloak.adapters.springsecurity.KeycloakAuthenticationException;
|
||||
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint;
|
||||
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator;
|
||||
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
|
||||
|
@ -22,6 +23,7 @@ import org.springframework.security.core.Authentication;
|
|||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
|
||||
|
@ -41,7 +43,7 @@ import java.io.IOException;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter implements ApplicationContextAware {
|
||||
|
||||
public static final String DEFAULT_LOGIN_URL = "/sso/login";
|
||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
/**
|
||||
|
@ -49,7 +51,7 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
|||
* and any request with a <code>Authorization</code> header.
|
||||
*/
|
||||
public static final RequestMatcher DEFAULT_REQUEST_MATCHER =
|
||||
new OrRequestMatcher(new AntPathRequestMatcher("/sso/login"), new RequestHeaderRequestMatcher("Authorization"));
|
||||
new OrRequestMatcher(new AntPathRequestMatcher(DEFAULT_LOGIN_URL), new RequestHeaderRequestMatcher(AUTHORIZATION_HEADER));
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(KeycloakAuthenticationProcessingFilter.class);
|
||||
|
||||
|
@ -67,6 +69,7 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
|||
*/
|
||||
public KeycloakAuthenticationProcessingFilter(AuthenticationManager authenticationManager) {
|
||||
this(authenticationManager, DEFAULT_REQUEST_MATCHER);
|
||||
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(DEFAULT_LOGIN_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,22 +117,24 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
|||
= new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1);
|
||||
|
||||
AuthOutcome result = authenticator.authenticate();
|
||||
AuthChallenge challenge = authenticator.getChallenge();
|
||||
|
||||
log.debug("Auth outcome: {}", result);
|
||||
|
||||
if (challenge != null) {
|
||||
challenge.challenge(facade);
|
||||
if (AuthOutcome.FAILED.equals(result)) {
|
||||
throw new KeycloakAuthenticationException("Auth outcome: " + result);
|
||||
}
|
||||
|
||||
if (AuthOutcome.AUTHENTICATED.equals(result)) {
|
||||
else if (AuthOutcome.AUTHENTICATED.equals(result)) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
Assert.notNull(authentication, "Authentication SecurityContextHolder was null");
|
||||
return authenticationManager.authenticate(authentication);
|
||||
}
|
||||
|
||||
else {
|
||||
AuthChallenge challenge = authenticator.getChallenge();
|
||||
if (challenge != null) {
|
||||
challenge.challenge(facade);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request was made with a bearer token authorization header.
|
||||
|
|
|
@ -7,8 +7,12 @@ import org.keycloak.KeycloakSecurityContext;
|
|||
import org.keycloak.adapters.KeycloakAccount;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.springsecurity.AdapterDeploymentContextBean;
|
||||
import org.keycloak.adapters.springsecurity.KeycloakAuthenticationException;
|
||||
import org.keycloak.adapters.springsecurity.account.KeycloakRole;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.keycloak.enums.SslRequired;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.util.KeycloakUriBuilder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -18,6 +22,7 @@ import org.springframework.security.authentication.BadCredentialsException;
|
|||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
|
||||
|
@ -105,6 +110,24 @@ public class KeycloakAuthenticationProcessingFilterTest {
|
|||
assertTrue(filter.isBasicAuthRequest(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttemptAuthenticationExpectRedirect() throws Exception {
|
||||
when(keycloakDeployment.getAuthUrl()).thenReturn(KeycloakUriBuilder.fromUri("http://localhost:8080/auth"));
|
||||
when(keycloakDeployment.getResourceName()).thenReturn("resource-name");
|
||||
when(keycloakDeployment.getStateCookieName()).thenReturn("kc-cookie");
|
||||
when(keycloakDeployment.getSslRequired()).thenReturn(SslRequired.NONE);
|
||||
filter.attemptAuthentication(request, response);
|
||||
|
||||
verify(response).setStatus(302);
|
||||
verify(response).setHeader(eq("Location"), startsWith("http://localhost:8080/auth"));
|
||||
}
|
||||
|
||||
@Test(expected = KeycloakAuthenticationException.class)
|
||||
public void testAttemptAuthenticationWithInvalidToken() throws Exception {
|
||||
request.addHeader("Authorization", "Bearer xxx");
|
||||
filter.attemptAuthentication(request, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulAuthenticationInteractive() throws Exception {
|
||||
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
|
||||
|
|
Loading…
Reference in a new issue