Il est tout simplement difficile d'obtenir une visibilité en temps réel sur une authentification en cours d'exécution flux. Des parties du processus peuvent nous être complètement cachées ; si le processus d'autorisation complet nécessite une redirection depuis un serveur de production OAuth distant, alors chaque effort de débogage doit passer par le serveur de production.Il est pratiquement impossible de déboguer cela localement. Il n'y a aucun moyen de reproduire l'état exact et aucun moyen d'inspecter ce qui se passe réellement sous le capot. Pas idéal.Connaissant ces types de défis, nous avons conçu Lightrun - un outil de débogage de production en temps réel - pour vous permettre de comprendre les flux complexes avec des informations au niveau du code. Ajoutez des journaux, prenez des instantanés (points d'arrêt virtuels) et instrumentez des métriques sans débogueur distant, sans arrêter le service en cours d'exécution et, surtout - en temps réel et sans effets secondaires .Apprenez-en plus avec ce tutoriel de 5 minutes concentré sur le débogage de ces types de scénarios à l'aide de Lightrun :>> Débogage de l'authentification et de l'autorisation à l'aide de Lightrun 1. Présentation
Spring Security propose différents systèmes d'authentification, par exemple via une base de données et UserDetailService .
Au lieu d'utiliser une couche de persistance JPA, nous pouvons également utiliser, par exemple, un référentiel MongoDB. Dans ce didacticiel, nous verrons comment authentifier un utilisateur à l'aide de Spring Security et de MongoDB.
2. Authentification Spring Security avec MongoDB
Semblable à l'utilisation d'un référentiel JPA, nous pouvons utiliser un référentiel MongoDB . Cependant, nous devons définir une configuration différente pour pouvoir l'utiliser.
2.1. Dépendances Maven
Pour ce tutoriel, nous allons utiliser Embedded MongoDB . Cependant, une instance MongoDB et Testcontainer pourraient être des options valides pour un environnement de production. Tout d'abord, ajoutons le spring-boot-starter-data-mongodb et de.flapdoodle.embed.mongo dépendances :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>3.3.1</version>
</dependency> 2.2. Configuration
Une fois les dépendances définies, nous pouvons créer notre configuration :
@Configuration
public class MongoConfig {
private static final String CONNECTION_STRING = "mongodb://%s:%d";
private static final String HOST = "localhost";
@Bean
public MongoTemplate mongoTemplate() throws Exception {
int randomPort = SocketUtils.findAvailableTcpPort();
ImmutableMongodConfig mongoDbConfig = MongodConfig.builder()
.version(Version.Main.PRODUCTION)
.net(new Net(HOST, randomPort, Network.localhostIsIPv6()))
.build();
MongodStarter starter = MongodStarter.getDefaultInstance();
MongodExecutable mongodExecutable = starter.prepare(mongoDbConfig);
mongodExecutable.start();
return new MongoTemplate(MongoClients.create(String.format(CONNECTION_STRING, HOST, randomPort)), "mongo_auth");
}
} Nous devons également configurer notre AuthenticationManager avec par exemple une authentification HTTP basique :
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ...
public SecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Bean
public AuthenticationManager customAuthenticationManager() throws Exception {
return authenticationManager();
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(@Autowired AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.and()
.httpBasic()
.and()
.authorizeRequests()
.anyRequest()
.permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
} 2.3. Domaine utilisateur et référentiel
Définissons d'abord un utilisateur simple avec des rôles pour notre authentification. Nous lui ferons implémenter les UserDetails interface pour réutiliser les méthodes communes d'un Principal objet :
@Document
public class User implements UserDetails {
private @MongoId ObjectId id;
private String username;
private String password;
private Set<UserRole> userRoles;
// getters and setters
}
Maintenant que nous avons notre utilisateur, définissons un référentiel simple :
public interface UserRepository extends MongoRepository<User, String> {
@Query("{username:'?0'}")
User findUserByUsername(String username);
} 2.4. Service d'authentification
Enfin, implémentons notre UserDetailService afin de récupérer un utilisateur et vérifier s'il est authentifié :
@Service
public class MongoAuthUserDetailService implements UserDetailsService {
// ...
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
com.baeldung.mongoauth.domain.User user = userRepository.findUserByUsername(userName);
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
user.getAuthorities()
.forEach(role -> {
grantedAuthorities.add(new SimpleGrantedAuthority(role.getRole()
.getName()));
});
return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
}
} 2.5. Tester l'authentification
Pour tester notre application, définissons un contrôleur simple. À titre d'exemple, nous avons défini deux rôles différents pour tester l'authentification et l'autorisation pour des points de terminaison spécifiques :
@RestController
public class ResourceController {
@RolesAllowed("ROLE_ADMIN")
@GetMapping("/admin")
public String admin() {
return "Hello Admin!";
}
@RolesAllowed({ "ROLE_ADMIN", "ROLE_USER" })
@GetMapping("/user")
public String user() {
return "Hello User!";
}
} Résumons tout cela dans un Spring Boot Test pour vérifier si notre authentification fonctionne. Comme nous pouvons le voir, nous attendons un code 401 pour quelqu'un qui fournit des informations d'identification invalides ou qui n'existe pas dans notre système :
class MongoAuthApplicationTest {
// set up
@Test
void givenUserCredentials_whenInvokeUserAuthorizedEndPoint_thenReturn200() throws Exception {
mvc.perform(get("/user").with(httpBasic(USER_NAME, PASSWORD)))
.andExpect(status().isOk());
}
@Test
void givenUserNotExists_whenInvokeEndPoint_thenReturn401() throws Exception {
mvc.perform(get("/user").with(httpBasic("not_existing_user", "password")))
.andExpect(status().isUnauthorized());
}
@Test
void givenUserExistsAndWrongPassword_whenInvokeEndPoint_thenReturn401() throws Exception {
mvc.perform(get("/user").with(httpBasic(USER_NAME, "wrong_password")))
.andExpect(status().isUnauthorized());
}
@Test
void givenUserCredentials_whenInvokeAdminAuthorizedEndPoint_thenReturn403() throws Exception {
mvc.perform(get("/admin").with(httpBasic(USER_NAME, PASSWORD)))
.andExpect(status().isForbidden());
}
@Test
void givenAdminCredentials_whenInvokeAdminAuthorizedEndPoint_thenReturn200() throws Exception {
mvc.perform(get("/admin").with(httpBasic(ADMIN_NAME, PASSWORD)))
.andExpect(status().isOk());
mvc.perform(get("/user").with(httpBasic(ADMIN_NAME, PASSWORD)))
.andExpect(status().isOk());
}
}