Hello I'have a web application secured with Spring security, with a login page. This is my Security Configuration
@Configuration
@ComponentScan("it.besmart")
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
@Autowired
@Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
@Autowired
CustomSuccessHandler customSuccessHandler;
@Autowired
CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
@Autowired
DataSource dataSource;
@Autowired
private ConnectionFactoryLocator connectionFactoryLocator;
@Autowired
private UsersConnectionRepository usersConnectionRepository;
@Autowired
private FacebookConnectionSignup facebookConnectionSignup;
private final static Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
@Autowired
public void configureGlobalService(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Webapp security configured");
http.
authorizeRequests()
.antMatchers("/", "/register", "/registrationConfirm", "/resendRegistrationToken", "/park/**")
.permitAll()
.antMatchers("/edit/**", "/payment/**", "/plate/**", "/book/**", "/home", "/stop/**",
"/notification/**", "/include/**")
.access("hasRole('USER') or hasRole('ADMIN') or hasRole('PARK')").antMatchers("/admin/**")
.access("hasRole('ADMIN') or hasRole('PARK')").antMatchers("/updatePassword")
.hasAuthority("CHANGE_PASSWORD_PRIVILEGE")
.and().formLogin().loginPage("/")
.successHandler(customSuccessHandler).failureHandler(customAuthenticationFailureHandler)
.usernameParameter("email").passwordParameter("password").and().rememberMe()
.rememberMeParameter("remember-me").tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(86400).and().exceptionHandling().accessDeniedPage("/Access_Denied").and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/?logout=true").permitAll();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
db.setDataSource(dataSource);
return db;
}
}
This works good by securing all my web application.
In the same application I have also a Resource/Authorization Server to protect some REST api.
Some resources are protected with an authorization code grant, so the untrusted Mobile App should take the access token from my application with a login form. I would like that the application use a different login page when trying to login from the Mobile App.
This is my resourceServer configuration
@EnableResourceServer
@ComponentScan("it.besmart.easyparking")
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig {
private final Logger logger = LoggerFactory.getLogger(ResourceServerConfig.class);
@Autowired
DataSource dataSource;
private static final String RESOURCE_ID = "easyparking_api";
@Configuration
// @Order(2)
public class grantCredentialsConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
logger.debug("Api security configured");
http
.requestMatchers().antMatchers("/api/oauth/**").and().authorizeRequests()
.antMatchers("/api/oauth/**").access("hasRole('USER')").and().formLogin().loginPage("/apilogin")
.permitAll();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore()).resourceId(RESOURCE_ID);
}
}
@Configuration
// @Order(4)
public class clientCredentialsConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
logger.debug("Client security configured");
http
.requestMatchers().antMatchers("/oauth2/**", "/api/registration", "/api/park/**").and()
.authorizeRequests().antMatchers("/oauth2/**", "/api/registration", "/api/park/**").authenticated();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore()).resourceId(RESOURCE_ID);
}
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
}
so, grantCredentialsConfiguration
should redirect the requests to /apilogin form, but it does not, i am redirected to the main web app login page... How it can be accomplished?
EDIT
Looking closer into the logs, it looks like that when i try to hit /oauth/authorize/ the normal security chain takes place and i get
2017-05-25 12:23:15 DEBUG o.s.security.web.FilterChainProxy[310] - /oauth/authorize?response_type=token&client_id=test&redirect_uri=https://www.getpostman.com/oauth2/callback reached end of additional filter chain; proceeding with original chain
2017-05-25 12:23:15 DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping[310] - Looking up handler method for path /oauth/authorize
2017-05-25 12:23:15 DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping[317] - Returning handler method [public org.springframework.web.servlet.ModelAndView org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.authorize(java.util.Map<java.lang.String, java.lang.Object>,java.util.Map<java.lang.String, java.lang.String>,org.springframework.web.bind.support.SessionStatus,java.security.Principal)]
2017-05-25 12:23:15 DEBUG o.s.s.w.a.ExceptionTranslationFilter[163] - Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.authentication.InsufficientAuthenticationException: User must be authenticated with Spring Security before authorization can be completed.
So it looks like searching for a handler to manage the request, instead of redirecting to /api/apilogin as desired, he finds an Authentication exception
and so i go to the standard login page... But why i get this exception?
Its happening because you haven't specified the order of the security configuration classes.
In Spring security resources protection should be mentioned from specific to generic.
Class SecurityConfiguration
is more generic than grantCredentialsConfiguration
. As both protect following resources.
/**
(Default URL)/api/oauth/**
Since the order is not defined, SecurityConfiguration
's generic configuration hides the specific configuration by grantCredentialsConfiguration
To get these to work as expected you'll have to define the order as below.
@Configuration
@Order(2)//Generic config should have larger value (lower priority)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
}
@Configuration
@Order(1)//Specific with lower value (higher priority)
public class grantCredentialsConfiguration extends ResourceServerConfigurerAdapter {
}
Note: Since these login pages are not from different applications, they share the SecurityContextHolder
or the security context. So if you login from one login page and then try to go the protected resource of the other, you won't be redirected to the next login page. Instead you'll get the 403 (depending on the roles assigned by the different login pages). At a time only one login session can be maintained.
Here's a sample on Github
https://github.com/ConsciousObserver/TestMultipleLoginPages.git
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With