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();
}
}
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")")
.
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