SEBSERV-4 #Unauthorized request redirect to login or respond with error
This commit is contained in:
parent
5a8ad16325
commit
b0b5876981
3 changed files with 89 additions and 36 deletions
|
@ -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/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() + "\" }");
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue