web-dev-qa-db-fra.com

authentification dans Spring Boot à l'aide de graphql

Je travaille sur un projet Spring Boot avec GraphQL. J'utilise graphql-Java-tools et graphql-spring-boot-starter. J'ai réussi à configurer la sécurité et la gestion des sessions avec la sécurité Spring comme vous pouvez le voir dans les fichiers de configuration Java ci-dessous).

Le chemin "/ graphql" est désormais sécurisé (il n'est accessible qu'en envoyant "l'authentification http de base" ou un jeton de session (x-auth-token) Dans un en-tête http de la requête). L'authentification avec "l'authentification http de base" sur toute opération GraphQL démarrera une nouvelle session et renverra le nouveau jeton de session dans un en-tête, et ce jeton peut être utilisé plus loin pour continuer cette session.

Comment donner accès à des utilisateurs anonymes à certaines requêtes/mutations GraphQL en conservant le comportement ci-dessus?

Si je change antMatchers("/graphql").authenticated() en antMatchers("/graphql").permitAll() afin de permettre l'accès anonyme, alors mon AuthenticationProvider personnalisé n'est plus appelé même lorsque j'essaye de m'authentifier avec "l'authentification http de base" .

Merci!

Voici mes configs:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthenticationProvider authenticationProvider;

    @Override
    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) {
        authenticationManagerBuilder.authenticationProvider(authenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/graphql").authenticated()
            .and()
            .requestCache()
            .requestCache(new NullRequestCache())
            .and()
            .httpBasic()
            .and()
            .headers()
            .frameOptions().sameOrigin() // needed for H2 web console
            .and()
            .sessionManagement()
            .maximumSessions(1)
            .maxSessionsPreventsLogin(true)
            .sessionRegistry(sessionRegistry());
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }
}
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 180)
public class HttpSessionConfig {

    @Bean
    public HttpSessionStrategy httpSessionStrategy() {
        return new HeaderHttpSessionStrategy();
    }

}
11
Roland

Au lieu de .antMatchers("/graphql").authenticated(), nous avons utilisé .antMatchers("/graphql").permitAll(), puis nous avons supprimé .httpBasic() et également supprimé le AuthenticationProvider personnalisé. Maintenant, les configurations de sécurité ressemblent à ceci:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/graphql").permitAll()
            .and()
            .requestCache()
            .requestCache(new NullRequestCache())
            .and()
            .headers()
            .frameOptions().sameOrigin() // needed for H2 web console
            .and()
            .sessionManagement()
            .maximumSessions(1)
            .maxSessionsPreventsLogin(true)
            .sessionRegistry(sessionRegistry());
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }
}

Ensuite, nous avons créé une mutation pour la connexion qui accepte les informations d'identification de l'utilisateur et renvoie le jeton de session. Voici le schéma graphql:

login(credentials: CredentialsInputDto!): String

input CredentialsInputDto {
    username: String!
    password: String!
}

Fondamentalement, le code que nous avions dans notre AuthenticationProvider personnalisé est entré dans le service appelé par l'opération de connexion:

public String login(CredentialsInputDto credentials) {
    String username = credentials.getUsername();
    String password = credentials.getPassword();

    UserDetails userDetails = userDetailsService.loadUserByUsername(username);

    ... credential checks and third party authentication ...

    Authentication authentication = new UsernamePasswordAuthenticationToken(username, password, userDetails.getAuthorities());
    SecurityContextHolder.getContext().setAuthentication(authentication);
    httpSession.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
    return httpSession.getId();
}

La clé est que nous avons préparé le contexte de la session avec l'authentification de l'utilisateur authentifié puis nous l'enregistrons (en redis) en tant qu'attribut de session appelé "SPRING_SECURITY_CONTEXT". C'est tout ce dont Spring a besoin pour pouvoir restaurer automatiquement le contexte lorsque vous faites une demande ayant l'en-tête "x-auth-token" défini avec la valeur du jeton de session obtenue à partir de l'opération de connexion.

Désormais, les appels anonymes sont également autorisés en raison de .antMatchers("/graphql").permitAll() et dans la couche service, sur les méthodes publiques, nous pouvons utiliser des annotations comme ceci: @Preauthorize("isAnonymous() OR hasRole("USER")").

5
Roland

Même si vous devez utiliser permitAll(), vous pouvez toujours créer une valeur par défaut raisonnable pour vos méthodes de résolution à l'aide d'AOP.

Vous pouvez créer votre aspect de sécurité personnalisé qui nécessitera une authentification par défaut.

Les méthodes non sécurisées peuvent être marquées par exemple à l'aide d'annotations.

Voir mon article de blog pour plus de détails: https://mi3o.com/spring-graphql-security

6
Michal Gebauer