0
0

More than 1 year has passed since last update.

SpringSecurity DBアクセス処理

Last updated at Posted at 2022-06-26

はじめに

SpringSecurity その3です。今回はDB認証の機能を実装していきます。
完全に理解することができていない為、不備等あるかもしれませんがご了承ください。

SpringSecurityシリーズ 項目表

NO タイトル
その1 SpringSecurity 導入から基本的な認証フロー
その2 SpringSecurity ログインページをカスタマイズする
その3 SpringSecurity DBアクセス処理
その4 SpringSecurity 403エラーページをカスタマイズする

前回の続きからとなる為、その1とその2を実装後にご覧ください。

実装

データベースの作成

PostgreSQLを使って、ログイン機能を実装していきます。
DB名をsecurityとし、以下のテーブル構成とします。

CREATE TABLE users (
  id SERIAL NOT NULL,
  name TEXT NOT NULL,
  password TEXT NOT NULL,
  role TEXT NOT NULL,
  created_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY(id)
);

テストデータの登録

ログインに使用するテスト用アカウントを作成します。
パスワードはBCrypt(ビー・クリプト)で暗号化して登録します。
2a$08$MtiHnF8YzT80cyhgE9ZC.uCkGz.bHPH5Csq0yXtQrXvgZbPAgdwfS = user 
$2a$08$LLlfr54ItEpsjG5o7hySeelDms2.LZOHiEvA6QOI9Irt2t9LjbkH2 = admin

INSERT INTO users
  (name,password,role)
VALUES 
  ('user', '$2a$08$MtiHnF8YzT80cyhgE9ZC.uCkGz.bHPH5Csq0yXtQrXvgZbPAgdwfS', 'USER'),
  ('admin', '$2a$08$LLlfr54ItEpsjG5o7hySeelDms2.LZOHiEvA6QOI9Irt2t9LjbkH2', 'ADMIN');

依存関係の追加

DB認証を行うために、依存関係を追加します。

pom.xml
<!-- 中略 -->

<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.18.12</version>
	<scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- 中略 -->

接続設定の追加

DBにアクセスするために、接続設定を行います。

application.properties
spring.jpa.database=POSTGRESQL
spring.datasource.url=jdbc:postgresql://localhost:5432/{データベース名}
spring.datasource.username={POSTGRESQLのユーザー名}
spring.datasource.password={POSTGRESQLのパスワード}
spring.mvc.hiddenmethod.filter.enabled=true

DB認証処理に使用するEntityとRepositoryを用意

DB認証を行うために、必要最小限のEntityとRepositoryを用意します。

Entity

User.java
@Data
@Table(name = "users")
@Entity
public class User {
  @Id
  @Column
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private int id;

  @Column
  private String name;

  @Column
  private String password;

  @Column
  private String role;
}

Repository

UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
  @Query("select t from User t WHERE t.name = ?1")
  User findUser(String name);
}

SpringSecurityの設定

DB認証を行うためにSpringSecurityの設定をカスタマイズして行きます。

WebSecurityConfigurerAdapter

SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private LoginUserDetailsService loginUserDetailsService;

  <!-- 中略 -->

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // 認証するユーザー情報をデータベースからロードする
    auth.userDetailsService(loginUserDetailsService).passwordEncoder(passwordEncoder());
  }
}

AuthenticationProvider

認証時に呼び出される。
権限の付与等はUserDetailsで行う。

authenticate()メソッド : 認証処理を実装するメソッド
supports()メソッド : この認証プロバイダがサポートする

AuthenticationProviderImpl.java
@Component
public class AuthenticationProviderImpl implements AuthenticationProvider {

  @Autowired
  LoginUserDetailsService loginUserDetailsService;

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationCredentialsNotFoundException {
    authentication.isAuthenticated(); // この時点では false

    // 入力されたユーザー名とパスワードを取得
    String username = authentication.getName();
    String password = authentication.getCredentials().toString();

    // userの検索
    UserDetails user = loginUserDetailsService.loadUserByUsername(username);

    if (!user.getPassword().equals(password)) {
      throw new AuthenticationCredentialsNotFoundException("ユーザーIDまたはパスワードが誤っています");
    }

    // 入力されたユーザー名とパスワードを保持するAuthenticationを作成
    UsernamePasswordAuthenticationToken authenticationResult = new UsernamePasswordAuthenticationToken(user,
        authentication.getCredentials(), user.getAuthorities());

    // 認証情報にUserDetailsをセットする
    authenticationResult.setDetails(authentication.getDetails());
    authenticationResult.isAuthenticated(); // この時点では true
    return authenticationResult;
  }

  @Override
  public boolean supports(Class<?> authentication) {
     // POST で送信されたユーザー名とパスワードで認証を行う
    return UsernamePasswordAuthenticationToken.class
        .isAssignableFrom(authentication);
  }
}

UserDetailsService

ログインページで入力されたユーザー名をキーにDBからユーザー情報を取得する。
ユーザーが存在しない場合、エラーメッセージが表示され認証に失敗する。

LoginUserDetailsService.java
@Service
public class LoginUserDetailsService implements UserDetailsService {

  @Autowired
  UserRepository userRepository;

  @Override
  public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
    // 入力された名前をキーに「usersテーブル」のレコードを1件取得
    User user = userRepository.findUser(name);

    // 取得できなかった場合はExceptionを投げる
    if (user == null) {
      throw new AuthenticationCredentialsNotFoundException("ユーザーIDまたはパスワードが誤っています");
    }

    return new LoginUserDetails(user);
  }
}

User

SpringSecurityのUserクラスを継承する。 ※EntityのUserクラスではない
権限などを付与し、認証処理を行う。

LoginUserDetails.java
@Data
@EqualsAndHashCode(callSuper = false)
public class LoginUserDetails extends User {
  private final com.example.demo.entity.User user;

  // 認証処理
  public LoginUserDetails(com.example.demo.entity.User user) {
    super(user.getName(), user.getPassword(), AuthorityUtils.createAuthorityList("ROLE_" + user.getRole()));
    this.user = user;
  }

  public com.example.demo.entity.User getUserName() {
    return this.user;
  }
}

おわりに

これでDB認証機能を実装することができました。
間違っている箇所ありましたら、ご指摘いただければ幸いです。
まだまだ課題や追加機能があるため、時間のある際に更新していきたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0