Es ist einfach schwer zu verstehen, Echtzeiteinsicht in eine laufende Authentifizierung fließen. Teile des Prozesses können uns vollständig verborgen bleiben; Wenn der vollständige Autorisierungsprozess eine Umleitung von einem Remote-OAuth-Produktionsserver erfordert, müssen alle Debugging-Bemühungen über den Produktionsserver gehen.Es ist praktisch nicht möglich, dies lokal zu debuggen. Es gibt keine Möglichkeit, den genauen Zustand zu reproduzieren und zu überprüfen, was tatsächlich unter der Haube passiert. Nicht ideal.Da wir diese Art von Herausforderungen kennen, haben wir Lightrun entwickelt – ein Echtzeit-Debugging-Tool für die Produktion – damit Sie komplizierte Abläufe mit Informationen auf Codeebene verstehen können. Fügen Sie Protokolle hinzu, erstellen Sie Snapshots (virtuelle Haltepunkte) und instrumentieren Sie Metriken ohne einen Remote-Debugger, ohne den laufenden Dienst zu stoppen, und vor allem - in Echtzeit und ohne Nebenwirkungen .In diesem 5-minütigen Tutorial erfahren Sie mehr konzentrierte sich auf das Debuggen dieser Art von Szenarien mit Lightrun:>> Debuggen von Authentifizierung und Autorisierung mit Lightrun 1. Übersicht
Spring Security bietet verschiedene Authentifizierungssysteme, beispielsweise über eine Datenbank und UserDetailService .
Anstatt eine JPA-Persistenzschicht zu verwenden, möchten wir möglicherweise auch beispielsweise ein MongoDB-Repository verwenden. In diesem Tutorial erfahren Sie, wie Sie einen Benutzer mit Spring Security und MongoDB authentifizieren.
2. Spring Security-Authentifizierung mit MongoDB
Ähnlich wie bei der Verwendung eines JPA-Repositorys können wir ein MongoDB-Repository verwenden . Wir müssen jedoch eine andere Konfiguration festlegen, um sie verwenden zu können.
2.1. Maven-Abhängigkeiten
Für dieses Tutorial verwenden wir Embedded MongoDB . Allerdings eine MongoDB-Instanz und Testcontainer könnten gültige Optionen für eine Produktionsumgebung sein. Zuerst fügen wir die spring-boot-starter-data-mongodb hinzu und de.flapdoodle.embed.mongo Abhängigkeiten:
<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. Konfiguration
Sobald wir Abhängigkeiten festgelegt haben, können wir unsere Konfiguration erstellen:
@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");
}
} Außerdem müssen wir unseren AuthenticationManager konfigurieren B. mit einer HTTP-Basisauthentifizierung:
@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. Benutzerdomäne und Repository
Lassen Sie uns zunächst einen einfachen Benutzer mit Rollen für unsere Authentifizierung definieren. Wir lassen es die UserDetails implementieren -Schnittstelle, um allgemeine Methoden eines Prinzipals wiederzuverwenden Objekt:
@Document
public class User implements UserDetails {
private @MongoId ObjectId id;
private String username;
private String password;
private Set<UserRole> userRoles;
// getters and setters
}
Nachdem wir nun unseren Benutzer haben, definieren wir ein einfaches Repository:
public interface UserRepository extends MongoRepository<User, String> {
@Query("{username:'?0'}")
User findUserByUsername(String username);
} 2.4. Authentifizierungsdienst
Lassen Sie uns zum Schluss unseren UserDetailService implementieren um einen Benutzer abzurufen und zu prüfen, ob er authentifiziert ist :
@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. Authentifizierung testen
Um unsere Anwendung zu testen, definieren wir einen einfachen Controller. Als Beispiel haben wir zwei verschiedene Rollen definiert, um die Authentifizierung und Autorisierung für bestimmte Endpunkte zu testen:
@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!";
}
} Lassen Sie uns alles in einem Spring Boot Test zusammenfassen, um zu überprüfen, ob unsere Authentifizierung funktioniert. Wie wir sehen, erwarten wir einen 401-Code für jemanden, der ungültige Anmeldeinformationen bereitstellt oder der nicht in unserem System existiert :
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());
}
}