Het is gewoon moeilijk om waar te krijgen, realtime inzicht in een lopende auth stromen. Delen van het proces kunnen volledig voor ons verborgen blijven; als het volledige autorisatieproces een omleiding vereist van een externe OAuth-productieserver, dan moet elke foutopsporing via de productieserver gaan.Het is praktisch onhaalbaar om dit lokaal te debuggen. Er is geen manier om de exacte staat te reproduceren en geen manier om te inspecteren wat er werkelijk onder de motorkap gebeurt. Niet ideaal.We kennen dit soort uitdagingen en hebben Lightrun gebouwd, een realtime debuggingtool voor productie, zodat je ingewikkelde stromen kunt begrijpen met informatie op codeniveau. Voeg logboeken toe, maak snapshots (virtuele onderbrekingspunten) en meet meetgegevens zonder een externe debugger, zonder de actieve service te stoppen, en vooral - in realtime en zonder bijwerkingen .Lees meer met deze tutorial van 5 minuten gericht op het debuggen van dit soort scenario's met Lightrun:>> Debuggen van authenticatie en autorisatie met Lightrun 1. Overzicht
Spring Security biedt verschillende authenticatiesystemen aan, zoals via een database en UserDetailService .
In plaats van een JPA-persistentielaag te gebruiken, willen we misschien ook een MongoDB-repository gebruiken. In deze zelfstudie laten we zien hoe u een gebruiker authenticeert met Spring Security en MongoDB.
2. Spring Security-verificatie met MongoDB
Net als bij het gebruik van een JPA-repository, kunnen we een MongoDB-repository gebruiken . We moeten echter een andere configuratie instellen om deze te kunnen gebruiken.
2.1. Maven-afhankelijkheden
Voor deze tutorial gaan we Embedded MongoDB gebruiken . Een MongoDB-instantie en Testcontainer kunnen geldige opties zijn voor een productieomgeving. Laten we eerst de spring-boot-starter-data-mongodb . toevoegen en de.flapdoodle.embed.mongo afhankelijkheden:
<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. Configuratie
Zodra we afhankelijkheden hebben ingesteld, kunnen we onze configuratie maken:
@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");
}
} We moeten ook onze AuthenticationManager . configureren met bijvoorbeeld een HTTP-basisauthenticatie:
@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. Gebruikersdomein en repository
Laten we eerst een eenvoudige gebruiker definiëren met rollen voor onze authenticatie. We laten het de UserDetails . implementeren interface om gemeenschappelijke methoden van een Opdrachtgever te hergebruiken voorwerp:
@Document
public class User implements UserDetails {
private @MongoId ObjectId id;
private String username;
private String password;
private Set<UserRole> userRoles;
// getters and setters
}
Nu we onze gebruiker hebben, laten we een eenvoudige repository definiëren:
public interface UserRepository extends MongoRepository<User, String> {
@Query("{username:'?0'}")
User findUserByUsername(String username);
} 2.4. Authenticatieservice
Laten we ten slotte onze UserDetailService implementeren om een gebruiker op te halen en te controleren of deze is geverifieerd :
@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. Verificatie testen
Laten we, om onze applicatie te testen, een eenvoudige controller definiëren. We hebben bijvoorbeeld twee verschillende rollen gedefinieerd om authenticatie en autorisatie voor specifieke eindpunten te 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!";
}
} Laten we het allemaal afronden in een Spring Boot Test om te controleren of onze authenticatie werkt. Zoals we kunnen zien, verwachten we een 401-code voor iemand die ongeldige inloggegevens verstrekt of die niet in ons systeem bestaat :
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());
}
}