SEBSERV-4 #Unauthorized request redirect to login or respond with error

This commit is contained in:
anhefti 2018-11-26 13:47:08 +01:00
parent 5a8ad16325
commit b0b5876981
3 changed files with 89 additions and 36 deletions

View file

@ -8,16 +8,29 @@
package ch.ethz.seb.sebserver.webservice.weblayer; package ch.ethz.seb.sebserver.webservice.weblayer;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter; import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
@ -25,19 +38,37 @@ import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConv
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter; import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter; import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import org.springframework.security.web.AuthenticationEntryPoint;
import ch.ethz.seb.sebserver.WebSecurityConfig; import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebClientDetailsService; import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebClientDetailsService;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebResourceServerConfiguration; import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebResourceServerConfiguration;
/** This is the main web-security Spring configuration for SEB-Server webservice API */ /** This is the main web-security Spring configuration for SEB-Server webservice API
*
* Currently two separated Rest API's are implemented, one for administration and maintenance
* of the SEB-Server (AdminAPI) and one for SEB-Client connection on running exams and eventually
* also for LMS communication), if needed (ExamAPI). The AdministrationAPI uses OAuth 2 password
* grant with refresh-token, same as in the prototype and the ExamAPI uses the client_credential grant.
*
* There is a Spring Authorization-Server defining this two clients (AdminAPIClient and ExamAPIClient) as well as
* two Spring Resource-Server for the separation of the different API's
*
* The endpoint of the AdministrationAPI can be configured within the key; sebserver.webservice.api.admin.endpoint
* and is by default set to "/admin-api/**"
*
* The endpoint of the ExamAPI can be configured within the key; sebserver.webservice.api.exam.endpoint
* and is by default set to "/exam-api/**" */
@WebServiceProfile @WebServiceProfile
@Configuration @Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity @EnableWebSecurity
@Order(4)
public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter { public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(ClientSessionWebSecurityConfig.class);
/** Spring bean name of single AuthenticationManager bean */ /** Spring bean name of single AuthenticationManager bean */
public static final String AUTHENTICATION_MANAGER = "AUTHENTICATION_MANAGER"; public static final String AUTHENTICATION_MANAGER = "AUTHENTICATION_MANAGER";
@ -103,6 +134,28 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter
this.examAPIEndpoint); this.examAPIEndpoint);
} }
@Override
public void configure(final HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginRedirectOnUnauthorized())
.and()
.formLogin().disable()
.httpBasic().disable()
.logout().disable()
.headers().frameOptions().disable()
.and()
.csrf().disable();
}
// NOTE: We need two different class types here to support Spring configuration for different // NOTE: We need two different class types here to support Spring configuration for different
// ResourceServerConfiguration. There is a class type now for the Admin API as well as for the Exam API // ResourceServerConfiguration. There is a class type now for the Admin API as well as for the Exam API
private static final class AdminAPIResourceServerConfiguration extends WebResourceServerConfiguration { private static final class AdminAPIResourceServerConfiguration extends WebResourceServerConfiguration {
@ -117,6 +170,7 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter
tokenStore, tokenStore,
webServiceClientDetails, webServiceClientDetails,
authenticationManager, authenticationManager,
new LoginRedirectOnUnauthorized(),
ADMIN_API_RESOURCE_ID, ADMIN_API_RESOURCE_ID,
apiEndpoint, apiEndpoint,
true, true,
@ -138,6 +192,13 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter
tokenStore, tokenStore,
webServiceClientDetails, webServiceClientDetails,
authenticationManager, authenticationManager,
(request, response, exception) -> {
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
log.warn("Unauthorized Request: {}", request, exception);
log.info("Redirect to login after unauthorized request");
response.getOutputStream().println("{ \"error\": \"" + exception.getMessage() + "\" }");
},
EXAM_API_RESOURCE_ID, EXAM_API_RESOURCE_ID,
apiEndpoint, apiEndpoint,
true, true,
@ -145,4 +206,22 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter
} }
} }
private static class LoginRedirectOnUnauthorized implements AuthenticationEntryPoint {
@Override
public void commence(
final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authenticationException) throws IOException, ServletException {
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
log.warn("Unauthorized Request: {}", request, authenticationException);
log.info("Redirect to login after unauthorized request");
// TODO define login redirect
response.sendRedirect("/gui/");
}
}
} }

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
/** Implements a custom unauthorized authentication error end-point */
@Component
public class CustomAuthenticationError implements AuthenticationEntryPoint {
@Override
public void commence(final HttpServletRequest request, final HttpServletResponse response,
final AuthenticationException authenticationException) throws IOException, ServletException {
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getOutputStream().println("{ \"error\": \"" + authenticationException.getMessage() + "\" }");
}
}

View file

@ -20,6 +20,7 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.AuthenticationEntryPoint;
/** Abstract Spring ResourceServerConfiguration to configure different resource services /** Abstract Spring ResourceServerConfiguration to configure different resource services
* for different API's. */ * for different API's. */
@ -34,6 +35,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
final TokenStore tokenStore, final TokenStore tokenStore,
final WebClientDetailsService webServiceClientDetails, final WebClientDetailsService webServiceClientDetails,
final AuthenticationManager authenticationManager, final AuthenticationManager authenticationManager,
final AuthenticationEntryPoint authenticationEntryPoint,
final String resourceId, final String resourceId,
final String apiEndpoint, final String apiEndpoint,
final boolean supportRefreshToken, final boolean supportRefreshToken,
@ -44,6 +46,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
tokenStore, tokenStore,
webServiceClientDetails, webServiceClientDetails,
authenticationManager, authenticationManager,
authenticationEntryPoint,
resourceId, resourceId,
apiEndpoint, apiEndpoint,
supportRefreshToken); supportRefreshToken);
@ -62,6 +65,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
private final TokenStore tokenStore; private final TokenStore tokenStore;
private final WebClientDetailsService webServiceClientDetails; private final WebClientDetailsService webServiceClientDetails;
private final AuthenticationManager authenticationManager; private final AuthenticationManager authenticationManager;
private final AuthenticationEntryPoint authenticationEntryPoint;
private final String resourceId; private final String resourceId;
private final String apiEndpoint; private final String apiEndpoint;
private final boolean supportRefreshToken; private final boolean supportRefreshToken;
@ -70,6 +74,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
final TokenStore tokenStore, final TokenStore tokenStore,
final WebClientDetailsService webServiceClientDetails, final WebClientDetailsService webServiceClientDetails,
final AuthenticationManager authenticationManager, final AuthenticationManager authenticationManager,
final AuthenticationEntryPoint authenticationEntryPoint,
final String resourceId, final String resourceId,
final String apiEndpoint, final String apiEndpoint,
final boolean supportRefreshToken) { final boolean supportRefreshToken) {
@ -78,6 +83,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
this.tokenStore = tokenStore; this.tokenStore = tokenStore;
this.webServiceClientDetails = webServiceClientDetails; this.webServiceClientDetails = webServiceClientDetails;
this.authenticationManager = authenticationManager; this.authenticationManager = authenticationManager;
this.authenticationEntryPoint = authenticationEntryPoint;
this.resourceId = resourceId; this.resourceId = resourceId;
this.apiEndpoint = apiEndpoint; this.apiEndpoint = apiEndpoint;
this.supportRefreshToken = supportRefreshToken; this.supportRefreshToken = supportRefreshToken;
@ -105,6 +111,9 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
.anyRequest() .anyRequest()
.authenticated() .authenticated()
.and() .and()
.exceptionHandling()
.authenticationEntryPoint(this.authenticationEntryPoint)
.and()
.formLogin().disable() .formLogin().disable()
.httpBasic().disable() .httpBasic().disable()
.logout().disable() .logout().disable()