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.KeycloakDeployment;
|
||||||
import org.keycloak.adapters.RequestAuthenticator;
|
import org.keycloak.adapters.RequestAuthenticator;
|
||||||
import org.keycloak.adapters.springsecurity.AdapterDeploymentContextBean;
|
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.KeycloakAuthenticationEntryPoint;
|
||||||
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator;
|
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator;
|
||||||
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
|
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.AuthenticationException;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
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.AntPathRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
|
||||||
|
@ -41,7 +43,7 @@ import java.io.IOException;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter implements ApplicationContextAware {
|
public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter implements ApplicationContextAware {
|
||||||
|
public static final String DEFAULT_LOGIN_URL = "/sso/login";
|
||||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
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.
|
* and any request with a <code>Authorization</code> header.
|
||||||
*/
|
*/
|
||||||
public static final RequestMatcher DEFAULT_REQUEST_MATCHER =
|
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);
|
private static final Logger log = LoggerFactory.getLogger(KeycloakAuthenticationProcessingFilter.class);
|
||||||
|
|
||||||
|
@ -67,6 +69,7 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
||||||
*/
|
*/
|
||||||
public KeycloakAuthenticationProcessingFilter(AuthenticationManager authenticationManager) {
|
public KeycloakAuthenticationProcessingFilter(AuthenticationManager authenticationManager) {
|
||||||
this(authenticationManager, DEFAULT_REQUEST_MATCHER);
|
this(authenticationManager, DEFAULT_REQUEST_MATCHER);
|
||||||
|
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(DEFAULT_LOGIN_URL));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -114,21 +117,23 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
||||||
= new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1);
|
= new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1);
|
||||||
|
|
||||||
AuthOutcome result = authenticator.authenticate();
|
AuthOutcome result = authenticator.authenticate();
|
||||||
AuthChallenge challenge = authenticator.getChallenge();
|
|
||||||
|
|
||||||
log.debug("Auth outcome: {}", result);
|
log.debug("Auth outcome: {}", result);
|
||||||
|
|
||||||
if (challenge != null) {
|
if (AuthOutcome.FAILED.equals(result)) {
|
||||||
challenge.challenge(facade);
|
throw new KeycloakAuthenticationException("Auth outcome: " + result);
|
||||||
}
|
}
|
||||||
|
else if (AuthOutcome.AUTHENTICATED.equals(result)) {
|
||||||
if (AuthOutcome.AUTHENTICATED.equals(result)) {
|
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
Assert.notNull(authentication, "Authentication SecurityContextHolder was null");
|
Assert.notNull(authentication, "Authentication SecurityContextHolder was null");
|
||||||
return authenticationManager.authenticate(authentication);
|
return authenticationManager.authenticate(authentication);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
return null;
|
AuthChallenge challenge = authenticator.getChallenge();
|
||||||
|
if (challenge != null) {
|
||||||
|
challenge.challenge(facade);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,8 +7,12 @@ import org.keycloak.KeycloakSecurityContext;
|
||||||
import org.keycloak.adapters.KeycloakAccount;
|
import org.keycloak.adapters.KeycloakAccount;
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
import org.keycloak.adapters.springsecurity.AdapterDeploymentContextBean;
|
import org.keycloak.adapters.springsecurity.AdapterDeploymentContextBean;
|
||||||
|
import org.keycloak.adapters.springsecurity.KeycloakAuthenticationException;
|
||||||
import org.keycloak.adapters.springsecurity.account.KeycloakRole;
|
import org.keycloak.adapters.springsecurity.account.KeycloakRole;
|
||||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
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.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.springframework.context.ApplicationContext;
|
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.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
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.AuthenticationFailureHandler;
|
||||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
|
|
||||||
|
@ -105,6 +110,24 @@ public class KeycloakAuthenticationProcessingFilterTest {
|
||||||
assertTrue(filter.isBasicAuthRequest(request));
|
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
|
@Test
|
||||||
public void testSuccessfulAuthenticationInteractive() throws Exception {
|
public void testSuccessfulAuthenticationInteractive() throws Exception {
|
||||||
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
|
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
|
||||||
|
|
Loading…
Reference in a new issue