はじめてのSpringBoot4 ~ログイン機能:DB値利用~

公開:2020/12/18
更新:2020/12/19
8 min読了の目安(約7500字TECH技術記事

はじめに

はじめまして
私はJavaを研修で少しかじった程度でSpringBootを使用した開発に参加し、てんやわんやしております森田和華です。
全く分からない中から少しずつ調べて実装をしていますが、自分の学習の為にも記事にして残しておこうと思い作成しております。
参考にしていただける際はそのあたりを念頭に置いたうえで参考にしていただければと思います。
もし私の記事を読んでおかしなところなどありましたらご指摘いただけると嬉しいです。
前回までの記事はこちらです。
はじめてのSpringBoot1 ~プロジェクトの作成~
はじめてのSpringBoot2 ~DB接続~
はじめてのSpringBoot3 ~ログイン機能その1~

環境

SpringBootバージョン:2.4.0
Javaバージョン:8
DB:MySQL 8.0
IDE:eclipse

ログイン処理にDBのデータを利用する

前回はデフォルトのログイン機能を実装しましたが、ユーザー名とパスワードがデフォルト指定のままで変更出来ていなかったのでDBを利用した実装に変更していきます。

UserDetailsの実装

まず認証処理で必要となる資格情報(ユーザー名とパスワード)とユーザーの情報を提供するためのインターフェースのUserDetailsの実装クラスを作成する必要があります。
今回の私の実装ではuser_sampleテーブルのエンティティで実装を兼ねることにします。

User.java
package com.elpmas.test.domain.entity;

import java.util.Collection;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import lombok.Getter;
import lombok.Setter;

//ユーザー情報Entity

@Entity //エンティティクラスのアノテーション
@Getter //Getterの自動生成のアノテーション
@Setter //Setterの自動生成のアノテーション
@Table(name="user_sample") //テーブル名の指定
public class User implements UserDetails{

	@Id //idであることをアノテーションで明示する
	@Column(name="id") //DB上のカラム名を指定する
	private int id;

	@Column(name="username")
	private String username;

	@Column(name="password")
	private String password;

	@Column(name="delflg")
	private int delflg;

	//この下はインターフェースのメソッドを実装する
	
	//ユーザーに与えられる権限リストを返却するメソッド
	@Override 
	public Collection<? extends GrantedAuthority> getAuthorities(){
		return null;
	}
	
	//ユーザー名を返却するメソッド
	@Override
	public String getUsername() {
		return this.username;
	}
	
	//パスワードを返却するメソッド
	@Override
	public String getPassword() {
		return this.password;
	}

	//アカウントの有効期限の状態を判定するメソッド
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	//アカウントのロック状態を判定するメソッド
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}
	
	//資格情報の有効期限の状態を判定するメソッド
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	//有効なユーザーか判定するメソッド
	@Override
	public boolean isEnabled() {
		return true;
	}

}

UserDetailsServiceの実装

UserDetailsServiceは資格情報とユーザーの状態をデータストアから取得するためのインターフェースです。
今回はこちらの実装も必要です。

UserDetailsServiceImpl.java
package com.elpmas.test.service;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.elpmas.test.domain.entity.User;
import com.elpmas.test.domain.repository.UserRepository;

@Service
public class UserDetailsServiceImpl implements UserDetailsService{
  //ユーザーテーブルの取得用repositoryのインスタンス生成
   @Autowired //オートワイヤリング設定(DIコンテナから型が一致するものを取り出しインジェクションする)
    private UserRepository userRepository;

  //与えられたユーザー名を用いてUserDetailsを取得し返却するメソッド
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
	//データベースからアカウント情報を検索する
    	User user = userRepository.findByUsernameEquals(username);
    	return user;
    }

}

認証処理の適用

また作成したUserDetailsServiceを使用して認証処理を行うにはDaoAuthenticationProviderを有効化して作成したUserDetailsServiceを適用する必要があります。
以前のWebSecurityConfigに追記していきます。

UserDetailsServiceImpl.java
package com.elpmas.test.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.elpmas.test.domain.service.UserDetailsServiceImpl;

/**
 * SpringSecurityを利用するための設定クラス
 * ログイン処理でのパラメータ、画面遷移や認証処理でのデータアクセス先を設定する
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
  //UserDetailsServiceを利用出来るように@Autowiredしておく
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

  //認証用パスワードはハッシュ化して扱うためPasswordをハッシュ化する際に必要なBCryptPasswordEncoder()を返すメソッドを作成しておく。
    @Bean
    public PasswordEncoder passwordEncoder() {

    	BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    	
	//これはハッシュ化済みの値をDBに登録する確認用に出力させるコード//
	String password = "1234";
        String digest = bCryptPasswordEncoder.encode(password);
        System.out.println("ハッシュ値 = " + digest);
	///////////////////////////////////////////////////////////////

        return new BCryptPasswordEncoder();
    }


    /**
     * 認可設定を無視するリクエストを設定
     * 静的リソース(image,javascript,css)を認可処理の対象から除外する
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
        		.antMatchers("/resources/**");
    }

    /**
     * 認証・認可の情報を設定する
     * SpringSecurityのconfigureメソッドをオーバーライドしています。
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            	.antMatchers("/login").permitAll()
                .anyRequest().authenticated();
        http
        	.formLogin()
                .defaultSuccessUrl("/sample", true);
    }

    /**
     * 認証時に利用するデータソースを定義する設定メソッド
     * ここではDBから取得したユーザ情報をuserDetailsServiceへセットすることで認証時の比較情報としている
     */
    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception{
    //UserDetailsServiceを設定してDaoAuthenticationProviderを有効化する
     auth.userDetailsService(userDetailsService).
     //上記作成のエンコードを設定しハッシュ化する
     passwordEncoder(passwordEncoder());
    }
}

動作確認

ここまで準備出来たら起動してみて動作確認をしていきます。
起動すると変わらずログイン画面が表示されます。

そしてここでDBに登録するはずのパスワード候補の「1234」のハッシュ化後の数値が表示されているか確認します。(確認してDBへの登録が済みましたらこの部分のコードは消してしまっていいと思います。)
コンソール画面を確認すると、出てました。

ではこの文字列を使ってDBのレコードを準備していきます。

レコードの準備が出来ましたらログイン画面にもどって今登録したユーザー名とパスワードを入力します。

無事ログインに成功しました!

ログインに失敗するとこちらの画面に遷移します。
エラーメッセージも出してくれていますが今回のメッセージは英語ですね。

次回

無事にDBの内容をログインに使用することが出来ました!
今回まではSpringSecurityでデフォルトで用意されているログイン画面を使いましたが実務ではログイン画面は自作することが多いと思います。
ですので、次回はログイン画面のカスタマイズをしていきたいなと思います。

参考文献

Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発 (著 株式会社NTTデータ)

https://www.shoeisha.co.jp/book/detail/9784798142470