Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring OAUTH - different login for web e REST

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?

like image 921
MarioC Avatar asked May 16 '17 12:05

MarioC


1 Answers

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.

  • SecurityConfiguration protects /** (Default URL)
  • grantCredentialsConfiguration /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

like image 88
11thdimension Avatar answered Oct 04 '22 03:10

11thdimension