JavaRush /Blog Java /Random-FR /Introduisons la connexion régulière par e-mail et OAuth2 ...

Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service de notes

Publié dans le groupe Random-FR
Lors de la rédaction de ma candidature, j'ai rencontré un manque d'articles clairs sur la manière d'amener l'utilisateur à s'inscrire à la fois par email et sur les réseaux sociaux. Il y avait de bons tutoriels sur la configuration du formulaire de connexion classique. Il y avait de bons tutoriels sur OAuth2 . Il y avait peu d'informations sur la façon de combiner les deux méthodes. Au cours du processus de recherche, nous avons pu trouver une solution viable. Il ne prétend pas être la vérité ultime, mais il remplit sa fonction. Dans cet article, je vais montrer comment implémenter un service de stockage de notes avec une configuration Spring Security similaire à partir de zéro. Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 1Remarque : c'est bien si le lecteur a parcouru au moins quelques tutoriels sur Spring, car l'attention se concentrera uniquement sur Spring Security, sans explications détaillées sur les référentiels, les contrôleurs, etc. Sinon, un article déjà assez volumineux se révélerait être être gigantesque. Contenu
  1. Créer un projet
  2. Création d'entités et de logique d'application
    1. Entités
    2. Dépôts
    3. Contrôleurs
    4. Pages
  3. Configuration de Spring Security pour la connexion classique
    1. Configuration de base SecurityConfig
    2. Connexion utilisateur personnalisée
    3. Améliorons le contrôleur
    4. Lancement
  4. Configuration d'OAuth2 en utilisant Google comme exemple dans Spring Security
    1. Configuration du filtre et application.properties
    2. Points forts de l'enregistrement d'une application auprès de Google Cloud Platform
    3. Services personnalisésUserInfoTokenServices
  5. Lancement définitif du projet

Créer un projet

Nous allons sur start.spring.io et formons la base du projet :
  • Web - lancement d'une application sur le Tomcat intégré, mappages d'URL, etc. ;
  • JPA - connexion à la base de données ;
  • Moustache est un moteur de modèles utilisé pour générer des pages Web ;
  • Sécurité - protection des applications. C'est pour cela que cet article a été créé.
Téléchargez l'archive résultante et décompressez-la dans le dossier dont vous avez besoin. Nous le lançons dans l'EDI. Vous pouvez choisir la base de données à votre discrétion. J'utilise MySQL comme base de données pour le projet, j'ajoute donc la dépendance suivante au fichier pom.xml dans le bloc <dependencies> :
<dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.34</version>
</dependency>
La configuration application.properties est actuellement la suivante :
spring.datasource.url=jdbc:mysql://localhost:3306/springsectut?createDatabaseIfNotExist=true&useSSL=false&autoReconnect=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=yes&characterEncoding=UTF-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=yourUsername
spring.datasource.password=yourPassword

spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update

spring.jpa.properties.connection.characterEncoding=utf-8
spring.jpa.properties.connection.CharSet=utf-8
spring.jpa.properties.connection.useUnicode=true

spring.mustache.expose-request-attributes=true

Création d'entités et de logique d'application

Entités

Créons un package entitiesdans lequel nous placerons les entités de la base de données. L'utilisateur sera décrit par une classe Userqui implémente l'interface UserDetails, qui sera nécessaire à la configuration de Spring Security. L'utilisateur aura un identifiant, un nom d'utilisateur (il s'agit d'un e-mail), un mot de passe, un nom, un rôle, un indicateur d'activité, un nom de compte Google et un e-mail ( googleNameet googleUsername).
@Entity
@Table(name = "user")
public class User implements UserDetails
{
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

  private String username;
  private String password;
  private String name;
  private boolean active;
  private String googleName;
  private String googleUsername;

  @ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER)
  @CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"))
  @Enumerated(EnumType.STRING)
  private Set<Role> roles;

    //Геттеры, сеттеры, toString(), equals(), hashcode(), имплементация UserDetails
}
Les rôles d'utilisateur sont utilisés pour réguler l'accès dans Spring Security. Notre application n'utilisera qu'un seul rôle :
public enum Role implements GrantedAuthority
{
  USER;

  @Override
  public String getAuthority()
  {
     return name();
  }
}
Créons une classe de note avec l'identifiant, le titre de la note, le corps de la note et l'identifiant de l'utilisateur auquel elle appartient :
@Entity
@Table(name = "note")
public class Note
{
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

  private String title;
  private String note;
  private Long userId;

    //Геттеры, сеттеры, toString(), equals(), hashcode()
}

Dépôts

Pour enregistrer des entités dans la base de données, nous avons besoin de référentiels qui feront tout le sale boulot à notre place. Créons un package repos, dans lequel nous créerons des interfaces UserRepohéritées NoteRepode l'interface JpaRepository<Entity, Id>.
@Service
@Repository
public interface UserRepo extends JpaRepository<User, Long>
{}

@Service
@Repository
public interface NoteRepo extends JpaRepository<Note, Long>
{
  List<Note> findByUserId(Long userId);
}

Contrôleurs

Notre service de notes comportera les pages suivantes :
  • Maison;
  • Inscription;
  • Entrée;
  • Liste des notes utilisateur.
Seul un utilisateur autorisé doit avoir accès à la liste des notes. Les pages restantes sont publiques. Créons un package controllerscontenant une classe IndexControllercontenant le get-mapping habituel de la page principale. La classe RegistrationControllerest responsable de l'inscription de l'utilisateur. Le post-mapping récupère les données du formulaire, enregistre l'utilisateur dans la base de données et le redirige vers la page de connexion. PasswordEncodersera décrit plus tard. Il est utilisé pour crypter les mots de passe.
@Controller
public class RegistrationController
{
  @Autowired
  private UserRepo userRepo;

  @Autowired
  private PasswordEncoder passwordEncoder;

  @GetMapping("/registration")
  public String registration()
  {
     return "registration";
  }

  @PostMapping("/registration")
  public String addUser(String name, String username, String password)
  {
     User user = new User();
     user.setName(name);
     user.setUsername(username);
     user.setPassword(passwordEncoder.encode(password));
     user.setActive(true);
     user.setRoles(Collections.singleton(Role.USER));

     userRepo.save(user);

     return "redirect:/login";
  }
Le contrôleur responsable de la page de liste de notes contient actuellement des fonctionnalités simplifiées, qui deviendront plus complexes après la mise en œuvre de Spring Security.
@Controller
public class NoteController
{
  @Autowired
  private NoteRepo noteRepo;

  @GetMapping("/notes")
  public String notes(Model model)
  {
     List<Note> notes = noteRepo.findAll();
     model.addAttribute("notes", notes);

     return "notes";
  }

  @PostMapping("/addnote")
  public String addNote(String title, String note)
  {
     Note newNote = new Note();
     newNote.setTitle(title);
     newNote.setNote(note);

     noteRepo.save(newNote);

     return "redirect:/notes";
  }
}
Nous n'écrirons pas de contrôleur pour la page de connexion car il est utilisé par Spring Security. Au lieu de cela, nous aurons besoin d’une configuration spéciale. Comme d'habitude, créons un autre package, appelons-le configet plaçons-y la classe MvcConfig. Lorsque nous écrivons la configuration de Spring Security, il saura à quelle page nous faisons référence lorsque nous utilisons "/login".
@Configuration
public class MvcConfig implements WebMvcConfigurer
{
  public void addViewControllers(ViewControllerRegistry registry)
  {
     registry.addViewController("/login").setViewName("login");
     registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
  }
}

Pages

J'utilise le moteur de modèles Moustache pour créer des pages . Vous pouvez en implémenter un autre, ce n’est pas grave. Un fichier méta.mustache a été créé pour les méta-informations utilisées sur toutes les pages. Il inclut également Bootstrap pour rendre les pages de notre projet plus jolies. Les pages sont créées dans le répertoire "src/main/resources/templates". Les fichiers portent l'extension moustache. Placer le code html directement dans l'article le rendra trop volumineux, voici donc un lien vers le dossier des modèles dans le référentiel GitHub du projet .

Configuration de Spring Security pour la connexion classique

Spring Security nous aide à protéger l'application et ses ressources contre tout accès non autorisé. Nous allons créer une configuration de travail concise dans une classe SecurityConfighéritée de WebSecurityConfigurerAdapter, que nous placerons dans le package config. Marquons-le avec l'annotation @EnableWebSecurity, qui activera la prise en charge de Spring Security, et l'annotation @Configuration, qui indique que cette classe contient une configuration. Remarque : le pom.xml configuré automatiquement contenait la version 2.1.4.RELEASE du composant parent Spring Boot, ce qui empêchait la mise en œuvre de la sécurité de la manière établie. Pour éviter les conflits dans le projet, il est recommandé de changer la version en 2.0.1.RELEASE.

Configuration de base SecurityConfig

Notre configuration pourra :
  1. Cryptez les mots de passe en utilisantBCryptPasswordEncoder :

    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Bean
    PasswordEncoder passwordEncoder()
    {
      PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
      return passwordEncoder;
    }
  2. Connectez-vous à l'aide d'un fournisseur d'authentification spécialement écrit :

    @Autowired
    private AuthProvider authProvider;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth)
    {
      auth.authenticationProvider(authProvider);
    }
  3. Autorisez les utilisateurs anonymes à accéder à la page d’accueil, aux pages d’inscription et de connexion. Toutes les autres demandes doivent être effectuées par des utilisateurs connectés. Attribuons le «/login» décrit précédemment comme page de connexion. Si la connexion réussit, l'utilisateur sera redirigé vers une page avec une liste de notes ; s'il y a une erreur, l'utilisateur restera sur la page de connexion. Une fois la sortie réussie, l'utilisateur sera redirigé vers la page principale.

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
      http
            .authorizeRequests()
            .antMatchers("/resources/**", "/", "/login**", "/registration").permitAll()
            .anyRequest().authenticated()
            .and().formLogin().loginPage("/login")
            .defaultSuccessUrl("/notes").failureUrl("/login?error").permitAll()
            .and().logout().logoutSuccessUrl("/").permitAll();
    }

Connexion utilisateur personnalisée

Un formulaire auto-écrit AuthProviderpermettra à l'utilisateur de se connecter non seulement par e-mail, mais également par nom d'utilisateur.
@Component
public class AuthProvider implements AuthenticationProvider
{
  @Autowired
  private UserService userService;

  @Autowired
  private PasswordEncoder passwordEncoder;

  public Authentication authenticate(Authentication authentication) throws AuthenticationException
  {
     String username = authentication.getName();
     String password = (String) authentication.getCredentials();

     User user = (User) userService.loadUserByUsername(username);

     if(user != null && (user.getUsername().equals(username) || user.getName().equals(username)))
     {
        if(!passwordEncoder.matches(password, user.getPassword()))
        {
           throw new BadCredentialsException("Wrong password");
        }

        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();

        return new UsernamePasswordAuthenticationToken(user, password, authorities);
     }
     else
        throw new BadCredentialsException("Username not found");
  }

  public boolean supports(Class<?> arg)
  {
     return true;
  }
}
Comme vous l'avez peut-être remarqué, la classe UserServicesituée dans le package est responsable du chargement de l'utilisateur services. Dans notre cas, il recherche un utilisateur non seulement par le champ username, comme l'implémentation intégrée, mais également par nom d'utilisateur, nom de compte Google et adresse e-mail du compte Google. Les deux dernières méthodes nous seront utiles lors de la mise en œuvre de la connexion via OAuth2. Ici, le cours est donné dans une version abrégée.
@Service
public class UserService implements UserDetailsService
{
  @Autowired
  private UserRepo userRepo;

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
  {
     User userFindByUsername = userRepo.findByUsername(username);
     //Остальные поиски

     if(userFindByUsername != null)
     {
        return userFindByUsername;
     }
     //Остальные проверки
     return null;
  }
}
Remarque : n'oubliez pas d'écrire les méthodes nécessaires dans UserRepo!

Améliorons le contrôleur

Nous avons configuré Spring Security. Il est maintenant temps d’en profiter dans votre contrôleur de notes. Désormais, chaque mappage acceptera un paramètre Principal supplémentaire, grâce auquel il tentera de trouver l'utilisateur. Pourquoi ne puis-je pas injecter directement la classe User? Il y aura alors un conflit dû à une inadéquation des types d'utilisateurs lorsque nous écrivons une connexion via les réseaux sociaux. Nous fournissons à l’avance la flexibilité nécessaire. Le code de notre contrôleur de notes ressemble désormais à ceci :
@GetMapping("/notes")
public String notes(Principal principal, Model model)
{
  User user = (User) userService.loadUserByUsername(principal.getName());
  List<Note> notes = noteRepo.findByUserId(user.getId());
  model.addAttribute("notes", notes);
  model.addAttribute("user", user);

  return "notes";
}

@PostMapping("/addnote")
public String addNote(Principal principal, String title, String note)
{
  User user = (User) userService.loadUserByUsername(principal.getName());

  Note newNote = new Note();
  newNote.setTitle(title);
  newNote.setNote(note);
  newNote.setUserId(user.getId());

  noteRepo.save(newNote);

  return "redirect:/notes";
}
Remarque : le projet a la protection CSRF activée par défaut , donc soit désactivez-la vous-même (http.csrf().disable()), soit n'oubliez pas, en tant qu'auteur de l'article, d'ajouter un champ caché avec un jeton csrf à toutes les demandes de publication.

Lancement

Nous essayons de lancer le projet.
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 1
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 2
On voit qu'un nouvel utilisateur est apparu dans la base de données. Le mot de passe est crypté.
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 3
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 4
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 5
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 6
Les notes sont enregistrées dans la base de données.
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 7
Nous constatons que le projet est lancé et fonctionne avec succès. Pour un bonheur complet, nous avons seulement besoin de pouvoir nous connecter via les réseaux sociaux. Eh bien, commençons !

Configuration d'OAuth2 en utilisant Google comme exemple dans Spring Security

Lors de l'implémentation d'OAuth2, je me suis appuyé sur ce tutoriel officiel de Spring . Pour prendre en charge OAuth2, ajoutez la bibliothèque suivante à pom.xml :
<dependency>
  <groupId>org.springframework.security.oauth.boot</groupId>
  <artifactId>spring-security-oauth2-autoconfigure</artifactId>
  <version>2.0.0.RELEASE</version>
</dependency>
Modifions notre configuration Spring Security dans le fichier SecurityConfig. Tout d’abord, ajoutons l’annotation @EnableOAuth2Client. Il affichera automatiquement ce dont vous avez besoin pour vous connecter via les réseaux sociaux.

Configuration du filtre et application.properties

Injectons OAuth2ClientContext à utiliser dans notre configuration de sécurité.
@Autowired
private OAuth2ClientContext oAuth2ClientContext;
OAuth2ClientContext est utilisé lors de la création d'un filtre qui valide la demande de connexion sociale de l'utilisateur. Le filtre est disponible grâce à l'annotation @EnableOAuth2Client. Tout ce que nous avons à faire est de l'appeler dans le bon ordre, avant le filtre principal Spring Security. Ce n'est qu'alors que nous pourrons intercepter les redirections pendant le processus de connexion avec OAuth2. Pour ce faire, nous utilisons FilterRegistrationBean, dans lequel nous fixons la priorité de notre filtre à -100.
@Bean
public FilterRegistrationBean oAuth2ClientFilterRegistration(OAuth2ClientContextFilter oAuth2ClientContextFilter)
{
  FilterRegistrationBean registration = new FilterRegistrationBean();
  registration.setFilter(oAuth2ClientContextFilter);
  registration.setOrder(-100);
  return registration;
}

private Filter ssoFilter()
{
  OAuth2ClientAuthenticationProcessingFilter googleFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/google");
  OAuth2RestTemplate googleTemplate = new OAuth2RestTemplate(google(), oAuth2ClientContext);
  googleFilter.setRestTemplate(googleTemplate);
  CustomUserInfoTokenServices tokenServices = new CustomUserInfoTokenServices(googleResource().getUserInfoUri(), google().getClientId());
  tokenServices.setRestTemplate(googleTemplate);
  googleFilter.setTokenServices(tokenServices);
  tokenServices.setUserRepo(userRepo);
  tokenServices.setPasswordEncoder(passwordEncoder);
  return googleFilter;
}
Vous devez également ajouter un nouveau filtre à la fonction configure(HttpSecurity http) :
http.addFilterBefore(ssoFilter(), UsernamePasswordAuthenticationFilter.class);
Le filtre doit également savoir que le client s'est inscrit via Google. L'annotation @ConfigurationProperties spécifie les propriétés de configuration à rechercher dans application.properties.
@Bean
@ConfigurationProperties("google.client")
public AuthorizationCodeResourceDetails google()
{
  return new AuthorizationCodeResourceDetails();
}
Pour terminer l'authentification, vous devez spécifier le point de terminaison des informations utilisateur Google :
@Bean
@ConfigurationProperties("google.resource")
public ResourceServerProperties googleResource()
{
  return new ResourceServerProperties();
}
Après avoir enregistré notre application dans Google Cloud Platform , nous ajouterons des propriétés avec les préfixes appropriés à application.properties :
google.client.clientId=yourClientId
google.client.clientSecret=yourClientSecret
google.client.accessTokenUri=https://www.googleapis.com/oauth2/v4/token
google.client.userAuthorizationUri=https://accounts.google.com/o/oauth2/v2/auth
google.client.clientAuthenticationScheme=form
google.client.scope=openid,email,profile
google.resource.userInfoUri=https://www.googleapis.com/oauth2/v3/userinfo
google.resource.preferTokenInfo=true

Points forts de l'enregistrement d'une application auprès de Google Cloud Platform

Chemin : API et services -> Fenêtre de demande d'accès OAuth d'informations d'identification :
  • Nom de l'application : formulaire de connexion Spring et didacticiel OAuth2
  • Adresse e-mail du support : votre e-mail
  • Portée de l'API Google : e-mail, profil, openid
  • Domaines autorisés : me.org
  • Lien vers la page principale de l'application : http://me.org:8080
  • Lien vers la politique de confidentialité de l'application : http://me.org:8080
  • Lien vers les conditions d'utilisation de l'application : http://me.org:8080
Informations d'identification:
  • Type : Application Web
  • Titre : Formulaire de connexion Spring et tutoriel OAuth2
  • Sources JavaScript autorisées : http://me.org, http://me.org:8080
  • URI de redirection autorisés : http://me.org:8080/login, http://me.org:8080/login/google
Remarque : puisque Google ne souhaite pas travailler avec l'adresse localhost:8080, ajoutez la ligne « 127.0.0.1 me.org » ou quelque chose de similaire au fichier C:\Windows\System32\drivers\etc\hosts à la fin. L'essentiel est que le domaine soit sous une forme classique.

Services personnalisésUserInfoTokenServices

Avez-vous remarqué le mot Custom dans la description de la fonction de filtre ? Classe CustomUserInfoTokenServices. Oui, nous allons créer notre propre classe avec le blackjack et la possibilité de sauvegarder l'utilisateur dans la base de données ! À l'aide du raccourci clavier Ctrl-N dans IntelliJ IDEA, vous pouvez rechercher et voir comment la UserInfoTokenServicesvaleur par défaut est implémentée. Copions son code dans la classe nouvellement créée CustomUserInfoTokenServices. La majeure partie peut rester inchangée. Avant de changer la logique des fonctions, ajoutons UserRepoet comme champs privés de la classe PasswordEncoder. Créons des setters pour eux. Ajoutons @Autowired UserRepo userRepo à la classe SecurityConfig. Nous regardons comment le pointeur vers l'erreur dans la méthode de création de filtre disparaît et nous nous réjouissons. Pourquoi @Autowired n'a-t-il pas pu être appliqué directement à CustomUserInfoTokenServices ? Parce que cette classe ne récupérera pas la dépendance, car elle-même n'est marquée d'aucune annotation Spring et son constructeur est créé explicitement lorsque le filtre est déclaré. En conséquence, le mécanisme DI de Spring n'en est pas informé. Si nous annotons @Autowired sur quoi que ce soit dans cette classe, nous obtiendrons une NullPointerException lors de son utilisation. Mais grâce aux setters explicites, tout fonctionne très bien. Après avoir implémenté les composants nécessaires, le principal objet d'intérêt devient la fonction loadAuthentication, dans laquelle le Map<String, Object> avec des informations sur l'utilisateur est récupéré. C'est dans ce projet que j'ai mis en œuvre la sauvegarde d'un utilisateur connecté via un réseau social dans la base de données. Puisque nous utilisons un compte Google comme fournisseur OAuth2, nous vérifions si la carte contient le champ « sous » typique de Google. S'il est présent, cela signifie que les informations sur l'utilisateur ont été reçues correctement. Nous créons un nouvel utilisateur et l'enregistrons dans la base de données.
@Override
public OAuth2Authentication loadAuthentication(String accessToken)
     throws AuthenticationException, InvalidTokenException
{
  Map<String, Object> map = getMap(this.userInfoEndpointUrl, accessToken);

  if(map.containsKey("sub"))
  {
     String googleName = (String) map.get("name");
     String googleUsername = (String) map.get("email");

     User user = userRepo.findByGoogleUsername(googleUsername);

     if(user == null)
     {
        user = new User();
        user.setActive(true);
        user.setRoles(Collections.singleton(Role.USER));
     }

     user.setName(googleName);
     user.setUsername(googleUsername);
     user.setGoogleName(googleName);
     user.setGoogleUsername(googleUsername);
     user.setPassword(passwordEncoder.encode("oauth2user"));

     userRepo.save(user);
  }

  if (map.containsKey("error"))
  {
     this.logger.debug("userinfo returned error: " + map.get("error"));
     throw new InvalidTokenException(accessToken);
  }
  return extractAuthentication(map);
}
Lorsque vous utilisez plusieurs fournisseurs, vous pouvez spécifier différentes options dans un CustomUserInfoTokenServices et enregistrer différentes classes de services similaires dans la méthode de déclaration de filtre. Désormais, l'utilisateur et OAuth2Authentication peuvent agir en tant que principal. Puisque nous avons pris en compte au préalable dans UserService le chargement de l'utilisateur via les données Google, l'application fonctionnera pour les deux types d'utilisateurs. Nous modifions le contrôleur de page principale du projet afin qu'il redirige les utilisateurs connectés via OAuth2 vers la page de notes.
@GetMapping("/")
public String index(Principal principal)
{
  if(principal != null)
  {
     return "redirect:/notes";
  }
  return "index";
}

Lancement définitif du projet

Après des modifications esthétiques mineures et l'ajout d'un bouton de sortie, nous procédons au lancement final du projet.
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 8
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 9
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 10
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 11
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 12
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 13
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 14
Introduisons la connexion régulière par e-mail et OAuth2 à Spring Security en utilisant l'exemple du service Notes - 15
L'utilisateur se connecte avec succès à la fois via le formulaire habituel et via un compte Google. C'est ce que nous voulions ! J'espère que cet article a clarifié certains points concernant la création d'une application Web, sa sécurisation avec Spring Security et la combinaison de différentes méthodes de connexion. Avec le code complet du projet, vous pouvez
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION