ホームページ > Java > &#&チュートリアル > Spring Security を使用したワンタイムトークン認証の実装

Spring Security を使用したワンタイムトークン認証の実装

DDD
リリース: 2024-12-04 17:11:11
オリジナル
657 人が閲覧しました

Implementing One-Time Token Authentication with Spring Security

今日のデジタル環境では、安全でユーザーフレンドリーな認証方法を提供することが非常に重要です。このような方法の 1 つとして人気が高まっているのが、ワンタイム トークン (OTT) 認証であり、多くの場合、電子メールで送信される「マジック リンク」として実装されます。 Spring Security 6.4.0 は、すぐに使用できる実装を含む、OTT 認証の強力な組み込みサポートを提供します。この包括的なガイドでは、組み込みソリューションとカスタム実装の両方を使用して安全な OTT 認証を実装する方法を説明します。

ワンタイムトークンとワンタイムパスワードについて理解する

実装に入る前に、ワンタイム トークン (OTT) がワンタイム パスワード (OTP) とは異なることを理解することが重要です。 OTP システムは通常、初期設定が必要で、パスワード生成には外部ツールに依存しますが、OTT システムはユーザーの観点から見るとよりシンプルです。つまり、認証に使用できる一意のトークン (通常は電子メール経由) を受け取ります。

主な違いは次のとおりです:

  • OTT はユーザーによる初期設定を必要としません
  • トークンはアプリケーションによって生成され、配信されます
  • 各トークンは通常 1 回のみ有効で、設定された時間が経過すると期限切れになります

利用可能な組み込み実装

Spring Security は、OneTimeTokenService の 2 つの実装を提供します。

  1. InMemoryOneTimeTokenService:

    • トークンをメモリに保存します
    • 開発とテストに最適
    • 実稼働環境やクラスター環境には適していません
    • アプリケーションの再起動時にトークンが失われます
  2. JdbcOneTimeTokenService:

    • トークンをデータベースに保存します
    • 本番環境での使用に適しています
    • クラスター環境で動作します
    • 自動クリーンアップを備えた永続ストレージ

InMemoryOneTimeTokenService の使用

よりシンプルなインメモリ ソリューションを実装する方法は次のとおりです。

@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();
    }
}
ログイン後にコピー
ログイン後にコピー

JdbcOneTimeTokenService の使用

実稼働環境の場合は、JDBC 実装を使用します。

@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();
    }
}
ログイン後にコピー
ログイン後にコピー

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
);
ログイン後にコピー

カスタム実装

トークンの生成と検証プロセスをさらに制御するには、カスタム実装を作成できます。

1. トークンエンティティとリポジトリ

@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. カスタムトークンサービス

@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());
    }
}
ログイン後にコピー

トークン配信の実装

Spring Security はトークンの配信を処理しないため、トークンの配信を実装する必要があります。

@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);
    }
}
ログイン後にコピー

URL とページのカスタマイズ

Spring Security はいくつかのカスタマイズ オプションを提供します。

@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();
    }
}
ログイン後にコピー
ログイン後にコピー

生産上の考慮事項

本番環境に OTT 認証を導入する場合:

  1. 適切な実装を選択してください

    • 本番環境には JdbcOneTimeTokenService またはカスタム実装を使用します
    • InMemoryOneTimeTokenService は開発/テストにのみ使用してください
  2. メール配信の設定

@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. セキュリティのベストプラクティス
    • 適切なトークンの有効期限を設定します (15 分を推奨)
    • トークン生成のレート制限を実装します
    • すべてのエンドポイントに HTTPS を使用します
    • 失敗した認証試行を監視する
    • トークンが使い捨てであり、使用後すぐに無効になることを確認します
    • 期限切れのトークンの自動クリーンアップを実装します
    • 安全なランダムトークン生成を使用して推測を防ぎます

仕組み

  1. ユーザーは電子メール アドレスを送信してトークンをリクエストします
  2. システムは安全なトークンを生成し、電子メール経由でマジック リンクを送信します
  3. ユーザーがリンクをクリックすると、トークン送信ページにリダイレクトされます
  4. システムはトークンを検証し、有効であればユーザーを認証します

結論

Spring Security の OTT サポートは、安全でユーザーフレンドリーな認証を実装するための堅牢な基盤を提供します。組み込みの実装を選択するか、カスタム ソリューションを作成するかに関係なく、高いセキュリティ基準を維持しながら、ユーザーにパスワードなしのログイン オプションを提供できます。

OTT 認証を実装する場合は、次の点に注意してください。

  • 環境に適した実装を選択してください
  • 安全なトークン配信を実装する
  • 適切なトークンの有効期限を構成する
  • セキュリティのベストプラクティスに従います
  • ユーザーフレンドリーなエラー処理とリダイレクトを作成する
  • プロフェッショナルな外観を実現する適切な電子メール テンプレートを実装します

このガイドに従うことで、Spring Security の堅牢なセキュリティ機能を活用しながら、アプリケーションのニーズを満たす安全でユーザーフレンドリーな OTT 認証システムを実装できます。

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

以上がSpring Security を使用したワンタイムトークン認証の実装の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート