sql >> Database >  >> NoSQL >> MongoDB

Authenticatie met Spring Security en MongoDB

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

  1. Meerdere $elemMatch-expressies voor het matchen van arraywaarden met $all in MongoDB?

  2. De meest efficiënte manier om meerdere hashes in Redis te krijgen?

  3. Rails en caching, is het makkelijk om te wisselen tussen memcache en redis?

  4. Verbinding geweigerd met MongoDB errno 111