È semplicemente difficile ottenere il vero, visibilità in tempo reale su un'autenticazione in esecuzione flusso. Parti del processo possono essere completamente nascoste a noi; se il processo di autorizzazione completo richiede un reindirizzamento da un server di produzione OAuth remoto, ogni sforzo di debug deve passare attraverso il server di produzione.È praticamente impossibile eseguire il debug di questo in locale. Non c'è modo di riprodurre lo stato esatto e non c'è modo di ispezionare ciò che sta effettivamente accadendo sotto il cofano. Non è l'ideale.Conoscendo questo tipo di sfide, abbiamo creato Lightrun, uno strumento di debug della produzione in tempo reale, per consentirti di comprendere flussi complicati con informazioni a livello di codice. Aggiungi log, scatta istantanee (punti di interruzione virtuali) e strumenta metriche senza un debugger remoto, senza interrompere il servizio in esecuzione e, soprattutto - in tempo reale e senza effetti collaterali .Ulteriori informazioni con questo tutorial di 5 minuti incentrato sul debug di questo tipo di scenari utilizzando Lightrun:>> Debug di autenticazione e autorizzazione utilizzando Lightrun 1. Panoramica
Spring Security offre diversi sistemi di autenticazione, ad esempio tramite un database e UserDetailService .
Invece di utilizzare un livello di persistenza JPA, potremmo anche voler utilizzare, ad esempio, un repository MongoDB. In questo tutorial vedremo come autenticare un utente utilizzando Spring Security e MongoDB.
2. Autenticazione di sicurezza di primavera con MongoDB
Simile all'utilizzo di un repository JPA, possiamo utilizzare un repository MongoDB . Tuttavia, dobbiamo impostare una configurazione diversa per poterla utilizzare.
2.1. Dipendenze Maven
Per questo tutorial utilizzeremo MongoDB incorporato . Tuttavia, un'istanza MongoDB e Testcontainer potrebbero essere valide opzioni per un ambiente di produzione. Innanzitutto, aggiungiamo spring-boot-starter-data-mongodb e de.flapdoodle.embed.mongo dipendenze:
<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. Configurazione
Una volta impostate le dipendenze, possiamo creare la nostra configurazione:
@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");
}
} Dobbiamo anche configurare il nostro AuthenticationManager con, ad esempio, un'autenticazione di base HTTP:
@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. Dominio utente e repository
Innanzitutto, definiamo un utente semplice con ruoli per la nostra autenticazione. Faremo implementare gli UserDetails interfaccia per riutilizzare i metodi comuni di un Principale oggetto:
@Document
public class User implements UserDetails {
private @MongoId ObjectId id;
private String username;
private String password;
private Set<UserRole> userRoles;
// getters and setters
}
Ora che abbiamo il nostro utente, definiamo un semplice repository:
public interface UserRepository extends MongoRepository<User, String> {
@Query("{username:'?0'}")
User findUserByUsername(String username);
} 2.4. Servizio di autenticazione
Infine, implementiamo il nostro UserDetailService per recuperare un utente e verificare se è autenticato :
@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. Verifica dell'autenticazione
Per testare la nostra applicazione, definiamo un semplice controller. Ad esempio, abbiamo definito due ruoli diversi per testare l'autenticazione e l'autorizzazione per endpoint specifici:
@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!";
}
} Concludiamo il tutto in uno Spring Boot Test per verificare se la nostra autenticazione funziona. Come possiamo vedere, ci aspettiamo un codice 401 per qualcuno che fornisce credenziali non valide o che non esiste nel nostro sistema :
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());
}
}