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;
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.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.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.oauth2.config.annotation.web.configuration.ResourceServerConfiguration;
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.TokenStore;
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.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebClientDetailsService;
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
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Order(4)
public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(ClientSessionWebSecurityConfig.class);
/** Spring bean name of single AuthenticationManager bean */
public static final String AUTHENTICATION_MANAGER = "AUTHENTICATION_MANAGER";
@ -103,6 +134,28 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter
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
// 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 {
@ -117,6 +170,7 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter
tokenStore,
webServiceClientDetails,
authenticationManager,
new LoginRedirectOnUnauthorized(),
ADMIN_API_RESOURCE_ID,
apiEndpoint,
true,
@ -138,6 +192,13 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter
tokenStore,
webServiceClientDetails,
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,
apiEndpoint,
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.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.AuthenticationEntryPoint;
/** Abstract Spring ResourceServerConfiguration to configure different resource services
* for different API's. */
@ -34,6 +35,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
final TokenStore tokenStore,
final WebClientDetailsService webServiceClientDetails,
final AuthenticationManager authenticationManager,
final AuthenticationEntryPoint authenticationEntryPoint,
final String resourceId,
final String apiEndpoint,
final boolean supportRefreshToken,
@ -44,6 +46,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
tokenStore,
webServiceClientDetails,
authenticationManager,
authenticationEntryPoint,
resourceId,
apiEndpoint,
supportRefreshToken);
@ -62,6 +65,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
private final TokenStore tokenStore;
private final WebClientDetailsService webServiceClientDetails;
private final AuthenticationManager authenticationManager;
private final AuthenticationEntryPoint authenticationEntryPoint;
private final String resourceId;
private final String apiEndpoint;
private final boolean supportRefreshToken;
@ -70,6 +74,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
final TokenStore tokenStore,
final WebClientDetailsService webServiceClientDetails,
final AuthenticationManager authenticationManager,
final AuthenticationEntryPoint authenticationEntryPoint,
final String resourceId,
final String apiEndpoint,
final boolean supportRefreshToken) {
@ -78,6 +83,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
this.tokenStore = tokenStore;
this.webServiceClientDetails = webServiceClientDetails;
this.authenticationManager = authenticationManager;
this.authenticationEntryPoint = authenticationEntryPoint;
this.resourceId = resourceId;
this.apiEndpoint = apiEndpoint;
this.supportRefreshToken = supportRefreshToken;
@ -105,6 +111,9 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(this.authenticationEntryPoint)
.and()
.formLogin().disable()
.httpBasic().disable()
.logout().disable()