【Java】Spring Security(5.6.2)で独自の認証を実装する

2022年6月28日火曜日

Java 開発

t f B! P L C


みなさまこんにちは。ムチャ@うつ病SE(@mutoj_rdm821)です。

Spring Securityを使って独自認証を実装したので、方法をまとめておきたいと思います。
Spring Securityのバージョンは5.6.2、SrpingBootは2.6.6です。


ユーザー情報クラスを作成する

ユーザー情報を格納するクラスを作成します。ここではメールアドレスとパスワードを保管するものとして作成しています。必要に応じて属性は追加ください。
package test;

import lombok.Data;

@Data
public class User {
  private String mail;
  private String password;
}


UserDetailsクラスを作成する

org.springframework.security.core.userdetails.Userクラスを継承したクラスを作成します。
package test.security;

import java.util.Collections;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import test.User;

/**
 * ユーザー情報の実装.
 */
public class UserDetail extends org.springframework.security.core.userdetails.User {
  private User user;
  /** ロールはUSERのみ */
  private static final List DEFAULT_AUTHORITIES = Collections
      .singletonList(new SimpleGrantedAuthority("ROLE_USER"));

  public UserDetail(User user) {
    super(user.getMail(), user.getPassword(), true, true, true, true, DEFAULT_AUTHORITIES);
    this.user = user;
  }

  public User getUser() {
    return user;
  }

}

認証プロバイダーを実装する

org.springframework.security.authentication.AuthenticationProviderを実装したクラスを作成します。
package test.security;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Service;

import test.User;
import test.repository.UserRepository;

/**
 * 認証プロバイダー.
 */
@Service
public class TestAuthenticationProvider implements AuthenticationProvider {

  @Autowired
  private UserRepository repository;

  /**
   * 認証を行う.
   */
  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String mail = (String) authentication.getPrincipal();
    String pass = (String) authentication.getCredentials();
    User user;
    try {
      user = repository.findByMail(mail);
    } catch (Exception e) {
       throw new AuthenticationServiceException("システムエラーが発生しました。",e);
    }
    String hash = createHash(pass);
    
    if (user != null && hash.equals(user.getPassword())) {
      UserDetail detail = new UserDetail(user);
      return new UsernamePasswordAuthenticationToken(detail, pass, detail.getAuthorities());
    }
    System.out.println("ログイン失敗:" + mail);
    throw new AuthenticationCredentialsNotFoundException("Authentication failure");
  }

  @Override
  public boolean supports(Class authentication) {
    return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
  }

  /**
   * パスワードのハッシュを計算する.
   * @param pass
   * @return
   */
  private String createHash(String pass) {
    try {
      MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
      byte[] data = sha512.digest(pass.getBytes());
      Base64.Encoder enc = Base64.getEncoder();
      return new String(enc.encode(data));
    } catch (NoSuchAlgorithmException e) {
      throw new RuntimeException("アルゴリズム名の不正");
    }
  }
}
authenticateメソッドを実装します。ここでは引数のAuthenticationからメールアドレスとパスワードを取得し、DB等からユーザー情報を取得します。

パスワードはハッシュ化後Base64エンコードされている想定で、復号しています。

認証が成功したら、UsernamePasswordAuthenticationTokenを生成して返します。失敗の場合はAuthenticationCredentialsNotFoundExceptionをスローします。


認証プロバイダーの設定を行う

package test.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import test.security.TestAuthenticationProvider;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  @Autowired
  private TestAuthenticationProvider provider;

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(provider);
  }
  :
WebSecurityConfigerAdapterconfigureメソッドで、引数がAuthenticationManagerBuilderものについて、プロバイダーを設定します。これでログイン認証に 作成したプロバイダーが利用されます。

※Spring Security5.7からWebSecurityConfigerAdapterは推奨されなくなったようで、その場合の方法については調べられていません。


まとめ

Spring Securityで独自認証を実装する方法について解説しました。
DB以外にも別のサービスなどを利用して認証する場合でもつ変えるかと思います。
参考になれば幸いです。

それではみなさまよきJavaライフを(´∀`)ノ


▼ブログを気に入っていただけたらRSS登録をお願いします!
▼ブログランキング参加中!応援よろしくお願いします。

ブログ内検索

自己紹介

猫とガジェットが好きなJava屋さんです。うつ病で休職後退職し、1年半の休養後に社会復帰。・・・が、いろいろあって再び退職。さらに1年休職の後に復帰して、なんとかSE続けてます。茶トラのすずと一緒に生活していましたが、2014年9月4日に亡くなって1人に。

より詳細なプロフィールはこちら↓

↓更新情報を受け取るにはフォローをお願いします!

Instagramでフォロー

※ヘッダー及びアイコンで使用しているドロイド君は、googleが作成、提供しているコンテンツをベースに複製したものです。

▼ココナラでメンターサービスを販売しています。招待コード「C3VG3」で1000ポイントもらえます。
▼欲しい物リスト

ブログ アーカイブ

QooQ