JavaRush /Java Blog /Random-IT /Introduciamo l'accesso regolare tramite e-mail e OAuth2 a...

Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note

Pubblicato nel gruppo Random-IT
Durante la scrittura della mia domanda, ho riscontrato la mancanza di articoli chiari su come convincere l'utente a registrarsi sia tramite e-mail che tramite social network. C'erano buoni tutorial sulla configurazione del modulo di accesso classico. C'erano buoni tutorial su OAuth2 . C'erano pochissime informazioni su come combinare i due metodi. Durante il processo di ricerca, siamo riusciti a trovare una soluzione praticabile. Non pretende di essere la verità ultima, ma adempie alla sua funzione. In questo articolo mostrerò come implementare da zero un servizio di archiviazione delle note con una configurazione Spring Security simile. Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 1Nota: è bene che il lettore abbia seguito almeno un paio di tutorial su Spring, perché l'attenzione si concentrerà solo su Spring Security, senza spiegazioni dettagliate su repository, controller, ecc. Altrimenti, un articolo già piuttosto ampio risulterebbe essere gigantesco. Contenuto
  1. Creazione di un progetto
  2. Creazione di entità e logica applicativa
    1. Entità
    2. Repository
    3. Controllori
    4. Pagine
  3. Configurazione di Spring Security per l'accesso classico
    1. Configurazione di base SecurityConfig
    2. Accesso utente personalizzato
    3. Miglioriamo il controller
    4. Lancio
  4. Configurazione di OAuth2 utilizzando Google come esempio in Spring Security
    1. Filtra configurazione e application.properties
    2. Punti salienti della registrazione di un'applicazione con Google Cloud Platform
    3. CustomUserInfoTokenServices
  5. Lancio definitivo del progetto

Creazione di un progetto

Andiamo su start.spring.io e formiamo la base del progetto:
  • Web: avvio di un'applicazione sul Tomcat integrato, mappature URL e simili;
  • JPA - connessione al database;
  • Moustache è un motore di template utilizzato per generare pagine web;
  • Sicurezza: protezione dell'applicazione. Questo è lo scopo per cui è stato creato questo articolo.
Scarica l'archivio risultante e decomprimilo nella cartella che ti serve. Lo lanciamo nell'IDE. Puoi scegliere il database a tua discrezione. Utilizzo MySQL come database per il progetto, quindi aggiungo la seguente dipendenza al file pom.xml nel blocco <dependencies>:
<dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.34</version>
</dependency>
La configurazione application.properties è attualmente la seguente:
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

Creazione di entità e logica applicativa

Entità

Creiamo un pacchetto entitiesin cui inseriremo le entità del database. L'utente verrà descritto da una classe Userche implementa l'interfaccia UserDetails, che sarà necessaria per la configurazione di Spring Security. L'utente avrà un ID, nome utente (questa è l'e-mail), password, nome, ruolo, flag di attività, nome dell'account Google ed e-mail ( googleNamee 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
}
I ruoli utente vengono utilizzati per regolare l'accesso in Spring Security. La nostra applicazione utilizzerà solo un ruolo:
public enum Role implements GrantedAuthority
{
  USER;

  @Override
  public String getAuthority()
  {
     return name();
  }
}
Creiamo una classe nota con id, titolo della nota, corpo della nota e id dell'utente a cui appartiene:
@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()
}

Repository

Per salvare le entità nel database, abbiamo bisogno di repository che faranno tutto il lavoro sporco per noi. Creiamo un pacchetto repos, in esso creeremo le interfacce UserRepoereditate NoteRepodall'interfaccia 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);
}

Controllori

Il nostro servizio note avrà le seguenti pagine:
  • Casa;
  • Registrazione;
  • Entrata;
  • Elenco delle note dell'utente.
Solo un utente autorizzato dovrebbe avere accesso all'elenco delle note. Le restanti pagine sono pubbliche. Creiamo un pacchetto controllerscontenente una classe IndexControllercontenente la solita get-mapping della pagina principale. La classe RegistrationControllerè responsabile della registrazione dell'utente. La post-mappatura prende i dati dal modulo, salva l'utente nel database e reindirizza alla pagina di accesso. PasswordEncoderverrà descritto più avanti. Viene utilizzato per crittografare le password.
@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";
  }
Il controller responsabile della pagina di elenco delle note contiene attualmente funzionalità semplificate, che diventeranno più complesse dopo l'implementazione di 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";
  }
}
Non scriveremo un controller per la pagina di accesso perché viene utilizzato da Spring Security. Avremo invece bisogno di una configurazione speciale. Come al solito, creiamo un altro pacchetto, chiamiamolo confige posizioniamo lì la classe MvcConfig. Quando scriviamo la configurazione di Spring Security, saprà a quale pagina ci riferiamo quando utilizziamo "/login".
@Configuration
public class MvcConfig implements WebMvcConfigurer
{
  public void addViewControllers(ViewControllerRegistry registry)
  {
     registry.addViewController("/login").setViewName("login");
     registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
  }
}

Pagine

Utilizzo il motore di template Moustache per creare pagine . Puoi implementarne un altro, non importa. È stato creato un file meta.mustache per le metainformazioni utilizzate su tutte le pagine. Include anche Bootstrap per rendere più belle le pagine del nostro progetto. Le pagine vengono create nella directory "src/main/resources/templates". Le lime hanno l'estensione baffi. Inserire il codice html direttamente nell'articolo lo renderà troppo grande, quindi ecco un collegamento alla cartella dei modelli nel repository GitHub del progetto .

Configurazione di Spring Security per l'accesso classico

Spring Security ci aiuta a proteggere l'applicazione e le sue risorse da accessi non autorizzati. Creeremo una configurazione di lavoro concisa in una classe SecurityConfigereditata da WebSecurityConfigurerAdapter, che inseriremo nel pacchetto config. Contrassegniamolo con l'annotazione @EnableWebSecurity, che abiliterà il supporto di Spring Security, e l'annotazione @Configuration, che indica che questa classe contiene alcune configurazioni. Nota: il pom.xml configurato automaticamente conteneva la versione del componente principale Spring Boot 2.1.4.RELEASE, che impediva l'implementazione della sicurezza nel modo stabilito. Per evitare conflitti nel progetto, si consiglia di modificare la versione in 2.0.1.RELEASE.

Configurazione di base SecurityConfig

La nostra configurazione sarà in grado di:
  1. Crittografare le password utilizzando BCryptPasswordEncoder:

    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Bean
    PasswordEncoder passwordEncoder()
    {
      PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
      return passwordEncoder;
    }
  2. Accedi utilizzando un provider di autenticazione appositamente scritto:

    @Autowired
    private AuthProvider authProvider;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth)
    {
      auth.authenticationProvider(authProvider);
    }
  3. Consenti agli utenti anonimi l'accesso alla home page, alle pagine di registrazione e di accesso. Tutte le altre richieste devono essere eseguite dagli utenti registrati. Assegnamo il "/login" descritto in precedenza come pagina di accesso. Se il login va a buon fine l'utente verrà indirizzato ad una pagina con un elenco di note; se si verifica un errore l'utente rimarrà nella pagina di login. Una volta uscita con successo, l'utente verrà indirizzato alla pagina 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();
    }

Accesso utente personalizzato

Uno scritto da sé AuthProviderconsentirà all'utente di accedere non solo tramite e-mail, ma anche tramite nome utente.
@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;
  }
}
Come avrai notato, la classe UserServicesituata nel pacchetto è responsabile del caricamento dell'utente services. Nel nostro caso, cerca un utente non solo in base al campo username, come nell'implementazione integrata, ma anche in base al nome utente, al nome dell'account Google e all'e-mail dell'account Google. Gli ultimi due metodi ci saranno utili quando implementeremo l'accesso tramite OAuth2. Qui la lezione è riportata in una versione abbreviata.
@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;
  }
}
Nota: non dimenticare di scrivere i metodi necessari in UserRepo!

Miglioriamo il controller

Abbiamo configurato Spring Security. Ora è il momento di approfittarne nel controller delle note. Ora ogni mappatura accetterà un parametro Principal aggiuntivo, tramite il quale tenterà di trovare l'utente. Perché non posso inserire direttamente la classe User? Quindi si verificherà un conflitto dovuto alla mancata corrispondenza dei tipi di utente quando scriviamo un accesso tramite i social network. Forniamo in anticipo la flessibilità necessaria. Il nostro codice del controller delle note ora assomiglia a questo:
@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";
}
Nota: il progetto ha la protezione CSRF abilitata per impostazione predefinita , quindi disabilitala tu stesso (http.csrf().disable()), oppure non dimenticare, come autore dell'articolo, di aggiungere un campo nascosto con un token csrf a tutte le richieste di posta.

Lancio

Stiamo cercando di lanciare il progetto.
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 1
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 2
Vediamo che un nuovo utente è apparso nel database. La password è crittografata.
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 3
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 4
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 5
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 6
Le note vengono salvate nel database.
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 7
Vediamo che il progetto è stato avviato e funzionante con successo. Per la completa felicità, abbiamo solo bisogno della possibilità di accedere tramite i social network. Bene, cominciamo!

Configurazione di OAuth2 utilizzando Google come esempio in Spring Security

Durante l'implementazione di OAuth2, mi sono affidato a questo tutorial ufficiale di Spring . Per supportare OAuth2, aggiungi la seguente libreria a pom.xml:
<dependency>
  <groupId>org.springframework.security.oauth.boot</groupId>
  <artifactId>spring-security-oauth2-autoconfigure</artifactId>
  <version>2.0.0.RELEASE</version>
</dependency>
Modifichiamo la nostra configurazione Spring Security nel file SecurityConfig. Per prima cosa aggiungiamo l'annotazione @EnableOAuth2Client. Troverà automaticamente ciò di cui hai bisogno per accedere tramite i social network.

Filtra configurazione e application.properties

Inseriamo OAuth2ClientContext da utilizzare nella nostra configurazione di sicurezza.
@Autowired
private OAuth2ClientContext oAuth2ClientContext;
OAuth2ClientContext viene utilizzato durante la creazione di un filtro che convalida la richiesta di accesso social dell'utente. Il filtro è disponibile grazie all'annotazione @EnableOAuth2Client. Tutto quello che dobbiamo fare è chiamarlo nell'ordine corretto, prima del filtro principale di Spring Security. Solo allora saremo in grado di rilevare i reindirizzamenti durante il processo di accesso con OAuth2. Per fare ciò usiamo FilterRegistrationBean, in cui impostiamo la priorità del nostro filtro su -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;
}
È inoltre necessario aggiungere un nuovo filtro alla funzione configure (HttpSecurity http):
http.addFilterBefore(ssoFilter(), UsernamePasswordAuthenticationFilter.class);
Il filtro deve anche sapere che il cliente si è registrato tramite Google. L'annotazione @ConfigurationProperties specifica quali proprietà di configurazione cercare in application.properties.
@Bean
@ConfigurationProperties("google.client")
public AuthorizationCodeResourceDetails google()
{
  return new AuthorizationCodeResourceDetails();
}
Per completare l'autenticazione, devi specificare l'endpoint delle informazioni utente di Google:
@Bean
@ConfigurationProperties("google.resource")
public ResourceServerProperties googleResource()
{
  return new ResourceServerProperties();
}
Dopo aver registrato la nostra applicazione in Google Cloud Platform , aggiungeremo le proprietà con i prefissi appropriati ad 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

Punti salienti della registrazione di un'applicazione con Google Cloud Platform

Percorso: API e servizi -> Finestra di richiesta di accesso OAuth credenziali:
  • Nome dell'applicazione: modulo di accesso Spring ed esercitazione su OAuth2
  • Indirizzo e-mail di supporto: la tua e-mail
  • Ambito per API Google: email, profilo, openid
  • Domini autorizzati: me.org
  • Collegamento alla pagina principale dell'applicazione: http://me.org:8080
  • Collegamento all'informativa sulla privacy dell'app: http://me.org:8080
  • Collegamento ai termini di utilizzo dell'applicazione: http://me.org:8080
Credenziali:
  • Tipo: applicazione Web
  • Titolo: Modulo di accesso Spring ed esercitazione su OAuth2
  • Sorgenti JavaScript consentite: http://me.org, http://me.org:8080
  • URI di reindirizzamento consentiti: http://me.org:8080/login, http://me.org:8080/login/google
Nota: poiché Google non vuole lavorare con l'indirizzo localhost:8080, aggiungi alla fine la riga “127.0.0.1 me.org” o qualcosa di simile al file C:\Windows\System32\drivers\etc\hosts. La cosa principale è che il dominio sia nella forma classica.

CustomUserInfoTokenServices

Hai notato la parola Personalizzato nella descrizione della funzione filtro? Classe CustomUserInfoTokenServices. Sì, creeremo la nostra classe con il blackjack e la possibilità di salvare l'utente nel database! Utilizzando la scorciatoia da tastiera Ctrl-N in IntelliJ IDEA, puoi trovare e vedere come UserInfoTokenServicesviene implementata l'impostazione predefinita. Copiamo il suo codice nella classe appena creata CustomUserInfoTokenServices. La maggior parte può essere lasciata invariata. Prima di cambiare la logica delle funzioni aggiungiamo UserRepoe come campi privati ​​della classe PasswordEncoder. Creiamo setter per loro. Aggiungiamo @Autowired UserRepo userRepo alla classe SecurityConfig. Osserviamo come scompare il puntatore all'errore nel metodo di creazione del filtro e ci rallegriamo. Perché @Autowired non può essere applicato direttamente a CustomUserInfoTokenServices? Perché questa classe non rileverà la dipendenza, poiché non è contrassegnata con alcuna annotazione Spring e il suo costruttore viene creato esplicitamente quando viene dichiarato il filtro. Di conseguenza, il meccanismo DI di Spring non ne è a conoscenza. Se annotiamo @Autowired su qualsiasi cosa in questa classe, otterremo una NullPointerException quando utilizzata. Ma attraverso i setter espliciti tutto funziona molto bene. Dopo aver implementato i componenti necessari, l'oggetto principale di interesse diventa la funzione loadAuthentication, in cui viene recuperato il Map<String, Object> con le informazioni sull'utente. È stato in questo progetto che ho implementato il salvataggio nel database di un utente registrato tramite un social network. Poiché utilizziamo un account Google come provider OAuth2, controlliamo se la mappa contiene il campo "sub" tipico di Google. Se è presente significa che le informazioni sull'utente sono state ricevute correttamente. Creiamo un nuovo utente e lo salviamo nel database.
@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);
}
Quando si utilizzano più provider, è possibile specificare diverse opzioni in un CustomUserInfoTokenServices e registrare diverse classi di servizi simili nel metodo di dichiarazione del filtro. Ora sia l'Utente che OAuth2Authentication possono agire come Principal. Poiché in UserService abbiamo preso in considerazione in anticipo il caricamento dell'utente tramite i dati di Google, l'applicazione funzionerà per entrambi i tipi di utenti. Modifichiamo il controller della pagina principale del progetto in modo che reindirizzi gli utenti che hanno effettuato l'accesso utilizzando OAuth2 alla pagina delle note.
@GetMapping("/")
public String index(Principal principal)
{
  if(principal != null)
  {
     return "redirect:/notes";
  }
  return "index";
}

Lancio definitivo del progetto

Dopo piccole modifiche estetiche e l'aggiunta di un pulsante di uscita, eseguiamo il lancio finale del progetto.
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 8
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 9
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 10
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 11
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 12
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 13
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 14
Introduciamo l'accesso regolare tramite e-mail e OAuth2 a Spring Security utilizzando l'esempio del servizio note - 15
L'utente accede con successo sia tramite il modulo normale che tramite un account Google. Questo è quello che volevamo! Spero che questo articolo abbia chiarito alcuni punti sulla creazione di un'applicazione web, sulla sua protezione con Spring Security e sulla combinazione di diversi metodi di accesso. Con il codice completo del progetto puoi
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION