web-dev-qa-db-fra.com

Sécurité de printemps oauth2 et configuration de la connexion au formulaire

Mon projet consiste à exposer deux parties différentes, un panneau d'administration JSF et un service RESTfull. J'essaie de configurer la sécurité du printemps pour utiliser différentes méthodes d'authentification en fonction de l'URL que l'utilisateur navigue.

Les exigences sont

  • Les utilisateurs accédant à la page JSF obtiennent un écran de connexion où ils s'authentifient à l'aide de l'authentification par formulaire.
  • Les utilisateurs naviguant vers le service REST utilisent l'authentification implicite OAuth2 avec l'authentification de base pour l'octroi de jeton.

Les configurations séparées fonctionnent par elles-mêmes, le problème est quand j'essaye de les combiner toutes les deux dans une configuration, dans ce cas il semble que le fournisseur REST gêne et authentifie chaque demande même si les requêtes sont envoyées à l'url d'administration (cela est documenté lors de la commande de sécurité du printemps).

Mes exemples de configurations sont les suivants:

  • Pour la connexion au formulaire (JSF)

    @Override
    @Order(1)
    protected void configure(HttpSecurity http) throws Exception {
    http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/resources/**").permitAll()
            .antMatchers("/templates/**").permitAll()
            .antMatchers("/401.html").permitAll()
            .antMatchers("/404.html").permitAll()
            .antMatchers("/500.html").permitAll()
            .antMatchers("/api/**").permitAll()
            .antMatchers("/ui/admin.xhtml").hasAnyAuthority("admin", "ADMIN")
            .antMatchers("/thymeleaf").hasAnyAuthority("admin", "ADMIN")
            //.anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/login")
            .defaultSuccessUrl("/ui/index.xhtml")
            .failureUrl("/login?error=1")
            .permitAll()
            .and()
            .logout()
            .permitAll()
            .and()
            .rememberMe()
            .and().exceptionHandling().accessDeniedPage("/error/403");
    
  • Configuration de sécurité OAuth2 (REST)

    @EnableResourceServer
    @Order(2)
    public class RestSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Inject
        private UserRepository userRepository;
    
        @Inject
        private PasswordEncoder passwordEncoder;
    
        @Bean
        ApplicationListener<AbstractAuthorizationEvent> loggerBean() {
            return new AuthenticationLoggerListener();
        }
    
        @Bean
        AccessDeniedHandler accessDeniedHandler() {
            return new AccessDeniedExceptionHandler();
        }
    
        @Bean
        AuthenticationEntryPoint entryPointBean() {
            return new UnauthorizedEntryPoint();
        }
    
        /*Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring()
                    .antMatchers(
                            "/resources/**"
                            , "/templates/**"
                            , "/login"
                            , "/logout"
                            , "/ui/**"
                            , "/401.html"
                            , "/404.html"
                            , "/500.html"
                    );
        }*/
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
            if (contentNegotiationStrategy == null) {
                contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
            }
            MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
                    MediaType.APPLICATION_FORM_URLENCODED,
                    MediaType.APPLICATION_JSON,
                    MediaType.MULTIPART_FORM_DATA);
    
            http.authorizeRequests()
                    .antMatchers("/ui/**").permitAll()
                    .and()
                    .anonymous().disable()
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and().httpBasic()
                    .and()
                    .exceptionHandling()
                    .accessDeniedHandler(accessDeniedHandler()) // handle access denied in general (for example comming from @PreAuthorization
                    .authenticationEntryPoint(entryPointBean()) // handle authentication exceptions for unauthorized calls.
                    .defaultAuthenticationEntryPointFor(entryPointBean(), preferredMatcher)
                    .and()
                    .authorizeRequests()
                    .antMatchers("/api/**").fullyAuthenticated();
    
        }
    
        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(new UserDetailsService() {
                @Override
                public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
                    User user = userRepository.findOneByUsername(s);
    
                    if (null == user) {
                        // leave that to be handled by log listener
                        throw new UsernameNotFoundException("The user with email " + s + " was not found");
                    }
    
                    return (UserDetails) user;
                }
            }).passwordEncoder(passwordEncoder);
        }
    
    
        @Configuration
        @EnableAuthorizationServer
        protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
    
    
    
            @Autowired
            private AuthenticationManager authenticationManager;
    
    
            @Bean
            public JwtAccessTokenConverter accessTokenConverter() {
                return new JwtAccessTokenConverter();
            }
    
            @Override
            public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
                oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
            }
    
            @Override
            public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
                endpoints.authenticationManager(authenticationManager).accessTokenConverter(accessTokenConverter());
            }
    
    
            @Override
            public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                clients.inMemory()
                        .withClient("xxx")
                        .resourceIds(xxx)
                        .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
                        .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
                        .scopes("read", "write", "trust", "update")
                        .accessTokenValiditySeconds(xxx)
                        .refreshTokenValiditySeconds(xxx)
                        .secret("xxx")
    
            }
        }
    }
    

Ces configurations existent sur différentes classes et la commande est définie manuellement.

Quelqu'un at-il des solutions à ce problème?

Meilleur,

18
maxsap

J'ai essayé d'adapter votre configuration de sécurité. Malheureusement, je ne peux pas valider cette configuration en raison de l'application de référence manquante.

Cela peut peut-être vous aider:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
                User user = userRepository.findOneByUsername(s);

                if (null == user) {
                    throw new UsernameNotFoundException("The user with email " + s + " was not found");
                }

                return (UserDetails) user;
            }
        }).passwordEncoder(passwordEncoder);
    }

    @Override
    public void configure(WebSecurity webSecurity) throws Exception {
        webSecurity
                .ignoring()
                .antMatchers("/resources/**"
                        , "/templates/**"
                        , "/login"
                        , "/logout"
                        , "/ui/**"
                        , "/401.html"
                        , "/404.html"
                        , "/500.html");
    }

    @Configuration
    @EnableAuthorizationServer
    public static class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;

        @Bean
        public JwtAccessTokenConverter accessTokenConverter() {
            return new JwtAccessTokenConverter();
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
        }

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager).accessTokenConverter(accessTokenConverter());
        }


        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient("xxx")
                    .resourceIds("xxx")
                    .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
                    .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
                    .scopes("read", "write", "trust", "update")
                    .accessTokenValiditySeconds(xxx)
                    .refreshTokenValiditySeconds(xxx)
                    .secret("xxx");

        }
    }

    @Configuration
    @Order(1)
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                    .csrf().disable()
                    .authorizeRequests()
                    .antMatchers("/ui/admin.xhtml").hasAnyAuthority("admin", "ADMIN")
                    .antMatchers("/thymeleaf").hasAnyAuthority("admin", "ADMIN")
                    .and()
                    .formLogin()
                    .loginPage("/login")
                    .defaultSuccessUrl("/ui/index.xhtml")
                    .failureUrl("/login?error=1")
                    .permitAll()
                    .and()
                    .logout()
                    .permitAll()
                    .and()
                    .rememberMe()
                    .and().exceptionHandling().accessDeniedPage("/error/403");
        }
    }

    @Order(2)
    @Configuration
    @EnableResourceServer
    public static class CustomResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {

        @Bean
        ApplicationListener<AbstractAuthorizationEvent> loggerBean() {
            return new AuthenticationLoggerListener();
        }

        @Bean
        AccessDeniedHandler accessDeniedHandler() {
            return new AccessDeniedExceptionHandler();
        }

        @Bean
        AuthenticationEntryPoint entryPointBean() {
            return new UnauthorizedEntryPoint();
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
            if (contentNegotiationStrategy == null) {
                contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
            }
            MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
                    MediaType.APPLICATION_FORM_URLENCODED,
                    MediaType.APPLICATION_JSON,
                    MediaType.MULTIPART_FORM_DATA);

            http.authorizeRequests()
                    .and()
                    .anonymous().disable()
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and().httpBasic()
                    .and()
                    .exceptionHandling()
                    .accessDeniedHandler(accessDeniedHandler()) // handle access denied in general (for example comming from @PreAuthorization
                    .authenticationEntryPoint(entryPointBean()) // handle authentication exceptions for unauthorized calls.
                    .defaultAuthenticationEntryPointFor(entryPointBean(), preferredMatcher)
                    .and()
                    .authorizeRequests()
                    .antMatchers("/api/**").fullyAuthenticated();
        }
    }
}
19
Kamill Sokol