Heim >Java >javaLernprogramm >Implementierung der einmaligen Token-Authentifizierung mit Spring Security

Implementierung der einmaligen Token-Authentifizierung mit Spring Security

DDD
DDDOriginal
2024-12-04 17:11:11636Durchsuche

Implementing One-Time Token Authentication with Spring Security

In der heutigen digitalen Landschaft ist die Bereitstellung sicherer und benutzerfreundlicher Authentifizierungsmethoden von entscheidender Bedeutung. Eine dieser immer beliebter werdenden Methoden ist die One-Time-Token-Authentifizierung (OTT), die oft als „magische Links“ implementiert wird, die per E-Mail verschickt werden. Spring Security 6.4.0 bietet robuste integrierte Unterstützung für die OTT-Authentifizierung, einschließlich gebrauchsfertiger Implementierungen. In diesem umfassenden Leitfaden erfahren Sie, wie Sie eine sichere OTT-Authentifizierung mithilfe integrierter Lösungen und benutzerdefinierter Implementierungen implementieren.

Einmal-Token vs. Einmal-Passwörter verstehen

Bevor wir uns mit der Implementierung befassen, ist es wichtig zu verstehen, dass sich Einmal-Tokens (OTT) von Einmal-Passwörtern (OTP) unterscheiden. Während OTP-Systeme in der Regel eine Ersteinrichtung erfordern und für die Passwortgenerierung auf externe Tools angewiesen sind, sind OTT-Systeme aus Benutzersicht einfacher: Sie erhalten ein eindeutiges Token (normalerweise per E-Mail), mit dem sie sich authentifizieren können.

Zu den wichtigsten Unterschieden gehören:

  • OTT erfordert keine anfängliche Benutzereinrichtung
  • Tokens werden von Ihrer Anwendung generiert und bereitgestellt
  • Jeder Token ist normalerweise für eine einmalige Verwendung gültig und läuft nach einer festgelegten Zeit ab

Verfügbare integrierte Implementierungen

Spring Security bietet zwei Implementierungen von OneTimeTokenService:

  1. InMemoryOneTimeTokenService:

    • Speichert Token im Speicher
    • Ideal für Entwicklung und Tests
    • Nicht für Produktions- oder Clusterumgebungen geeignet
    • Token gehen beim Neustart der Anwendung verloren
  2. JdbcOneTimeTokenService:

    • Speichert Token in einer Datenbank
    • Geeignet für den Produktionseinsatz
    • Funktioniert in Cluster-Umgebungen
    • Persistenter Speicher mit automatischer Bereinigung

Verwenden von InMemoryOneTimeTokenService

So implementieren Sie die einfachere In-Memory-Lösung:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login/**", "/ott/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults())
            .oneTimeTokenLogin(Customizer.withDefaults());  // Uses InMemoryOneTimeTokenService by default

        return http.build();
    }
}

Verwenden von JdbcOneTimeTokenService

Für Produktionsumgebungen verwenden Sie die JDBC-Implementierung:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    JdbcTemplate jdbcTemplate;

    @Bean
    public OneTimeTokenService oneTimeTokenService() {
        return new JdbcOneTimeTokenService(jdbcTemplate);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login/**", "/ott/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults())
            .oneTimeTokenLogin(Customizer.withDefaults());

        return http.build();
    }
}

Erforderliche Tabellenstruktur für JdbcOneTimeTokenService:

CREATE TABLE one_time_tokens (
    token_value VARCHAR(255) PRIMARY KEY,
    username VARCHAR(255) NOT NULL,
    issued_at TIMESTAMP NOT NULL,
    expires_at TIMESTAMP NOT NULL,
    used BOOLEAN NOT NULL
);

Benutzerdefinierte Implementierung

Für mehr Kontrolle über den Token-Generierungs- und Validierungsprozess können Sie eine benutzerdefinierte Implementierung erstellen:

1. Token-Entität und Repository

@Entity
@Table(name = "one_time_tokens")
public class OneTimeToken {
    @Id
    @GeneratedValue
    private Long id;

    private String tokenValue;
    private String username;
    private LocalDateTime createdAt;
    private LocalDateTime expiresAt;
    private boolean used;

    // Getters and setters omitted for brevity
}

@Repository
public interface OneTimeTokenRepository extends JpaRepository<OneTimeToken, Long> {
    Optional<OneTimeToken> findByTokenValueAndUsedFalse(String tokenValue);
    void deleteByExpiresAtBefore(LocalDateTime dateTime);
}

2. Benutzerdefinierter Token-Dienst

@Service
@Transactional
public class PersistentOneTimeTokenService implements OneTimeTokenService {
    private static final int TOKEN_VALIDITY_MINUTES = 15;

    @Autowired
    private OneTimeTokenRepository tokenRepository;

    @Override
    public OneTimeToken generate(GenerateOneTimeTokenRequest request) {
        String tokenValue = UUID.randomUUID().toString();
        LocalDateTime now = LocalDateTime.now();

        OneTimeToken token = new OneTimeToken();
        token.setTokenValue(tokenValue);
        token.setUsername(request.getUsername());
        token.setCreatedAt(now);
        token.setExpiresAt(now.plusMinutes(TOKEN_VALIDITY_MINUTES));
        token.setUsed(false);

        return return new DefaultOneTimeToken(token.getTokenValue(),token.getUsername(), Instant.now());
    }

    @Override
    public Authentication consume(ConsumeOneTimeTokenRequest request) {
        OneTimeToken token = tokenRepository.findByTokenValueAndUsedFalse(request.getTokenValue())
            .orElseThrow(() -> new BadCredentialsException("Invalid or expired token"));

        if (token.getExpiresAt().isBefore(LocalDateTime.now())) {
            throw new BadCredentialsException("Token has expired");
        }

        token.setUsed(true);
        tokenRepository.save(token);

        UserDetails userDetails = loadUserByUsername(token.getUsername());
        return new UsernamePasswordAuthenticationToken(
            userDetails, null, userDetails.getAuthorities());
    }
}

Implementierung der Token-Lieferung

Spring Security kümmert sich nicht um die Token-Zustellung, daher müssen Sie sie implementieren:

@Component
public class EmailMagicLinkHandler implements OneTimeTokenGenerationSuccessHandler {
    @Autowired
    private JavaMailSender mailSender;

    private final OneTimeTokenGenerationSuccessHandler redirectHandler = 
        new RedirectOneTimeTokenGenerationSuccessHandler("/ott/check-email");

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, 
                      OneTimeToken token) throws IOException, ServletException {
        String magicLink = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
            .replacePath(request.getContextPath())
            .replaceQuery(null)
            .fragment(null)
            .path("/login/ott")
            .queryParam("token", token.getTokenValue())
            .toUriString();

        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(getUserEmail(token.getUsername()));
        message.setSubject("Your Sign-in Link");
        message.setText("Click here to sign in: " + magicLink);

        mailSender.send(message);
        redirectHandler.handle(request, response, token);
    }
}

Anpassen von URLs und Seiten

Spring Security bietet mehrere Anpassungsoptionen:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login/**", "/ott/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults())
            .oneTimeTokenLogin(Customizer.withDefaults());  // Uses InMemoryOneTimeTokenService by default

        return http.build();
    }
}

Produktionsüberlegungen

Beim Einsatz der OTT-Authentifizierung in der Produktion:

  1. Wählen Sie die richtige Implementierung

    • Verwenden Sie JdbcOneTimeTokenService oder eine benutzerdefinierte Implementierung für die Produktion
    • InMemoryOneTimeTokenService sollte nur für Entwicklung/Tests verwendet werden
  2. E-Mail-Zustellung konfigurieren

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    JdbcTemplate jdbcTemplate;

    @Bean
    public OneTimeTokenService oneTimeTokenService() {
        return new JdbcOneTimeTokenService(jdbcTemplate);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login/**", "/ott/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults())
            .oneTimeTokenLogin(Customizer.withDefaults());

        return http.build();
    }
}
  1. Best Practices für die Sicherheit
    • Legen Sie geeignete Token-Ablaufzeiten fest (15 Minuten empfohlen)
    • Ratenbegrenzung für die Token-Generierung implementieren
    • HTTPS für alle Endpunkte verwenden
    • Überwachen Sie fehlgeschlagene Authentifizierungsversuche
    • Stellen Sie sicher, dass Token nur für den einmaligen Gebrauch bestimmt sind und unmittelbar nach der Verwendung ungültig werden
    • Automatische Bereinigung abgelaufener Token implementieren
    • Verwenden Sie eine sichere zufällige Token-Generierung, um Raten zu verhindern

Wie es funktioniert

  1. Der Benutzer fordert ein Token an, indem er seine E-Mail-Adresse übermittelt
  2. Das System generiert ein sicheres Token und sendet einen magischen Link per E-Mail
  3. Der Benutzer klickt auf den Link und wird zur Seite zur Token-Übermittlung weitergeleitet
  4. Das System validiert das Token und authentifiziert den Benutzer, wenn es gültig ist

Abschluss

Die OTT-Unterstützung von Spring Security bietet eine solide Grundlage für die Implementierung einer sicheren, benutzerfreundlichen Authentifizierung. Unabhängig davon, ob Sie sich für die integrierten Implementierungen entscheiden oder eine benutzerdefinierte Lösung erstellen, können Sie Ihren Benutzern eine passwortlose Anmeldeoption anbieten und gleichzeitig hohe Sicherheitsstandards einhalten.

Denken Sie bei der Implementierung der OTT-Authentifizierung an Folgendes:

  • Wählen Sie die passende Implementierung für Ihre Umgebung
  • Implementieren Sie eine sichere Token-Zustellung
  • Konfigurieren Sie den ordnungsgemäßen Ablauf des Tokens
  • Befolgen Sie die Best Practices für die Sicherheit
  • Erstellen Sie eine benutzerfreundliche Fehlerbehandlung und Weiterleitungen
  • Implementieren Sie geeignete E-Mail-Vorlagen für ein professionelles Erscheinungsbild

Indem Sie diesem Leitfaden folgen, können Sie ein sicheres und benutzerfreundliches OTT-Authentifizierungssystem implementieren, das die Anforderungen Ihrer Anwendung erfüllt und gleichzeitig die robusten Sicherheitsfunktionen von Spring Security nutzt.

Referenz: https://docs.spring.io/spring-security/reference/servlet/authentication/onetimetoken.html

Das obige ist der detaillierte Inhalt vonImplementierung der einmaligen Token-Authentifizierung mit Spring Security. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn