Spring Boot学習④～パスワードのハッシュ化（SpringSecurity）～

前→https://qiita.com/19960417akiho/items/2aa16ce876c4937b0cc3

以下の動画を参考に、ハンズオン形式でSpring BootでWebアプリを制作していきます。

https://www.youtube.com/watch?v=Qt3bjKItFac&list=PLD6WyeaFzSSbS0q1unoPpBpnBNyzPWo3j&index=11

今回はPart10。

パスワードのハッシュ化をSpringSecurityを用いて実装します。

まず、SpringSecurityを使うには、pom.xmlに情報を追加しなければなりません。

# pom.xml

``` pom.xml
		<!-- PasswordEncoder(Spring Security)-->
		<dependency>
    		<groupId>org.springframework.security</groupId>
    		<artifactId>spring-security-core</artifactId>
		</dependency>
```

これでSpringSecurityの機能を用いることができます。

つぎに、configパッケージを作って、そのなかにBeanDifine.javaクラスを作っていきます。

正直Beanってなに？って感じですが、とりあえず動画の通りに実装してみて、疑問点をChatGPTに聞いてみます。

# BeanDifine.java
``` BeanDifine.java
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class BeanDifine {
	@Bean
	PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
}
```

まず@Configurationですが、これは@Beanとセットで付与するアノテーションだそうです。
クラスには@Configuration、その中のメソッドには@Beanって感じです。

動画の中では、
「@Beanを付与したメソッドはDIコンテナに登録され、DI注入（＠RequiredArgsConstructor配下でprivate finalしてインスタンス化）することができます」
と説明されています

頑張って調べてもいまいち理解できなかったので、以下は自分なりの理解です。

まず、@Beanとアノテーションがつけられたメソッドは特別扱いされます。

どう特別扱いされるかというと、そのメソッドの戻り値をキーとして、@RequiredArgsConstructorが付与されたクラスでも使うことができます。

今回で言うと、PasswordEncoderという型をキーとして、LoginControllerクラスでも使うことができるようになります。

LoginControllerクラスを見てみましょう

# LoginController.java

``` LoginController.java
@Controller
@RequiredArgsConstructor
public class LoginController {
	
	/** PasswordEncoder*/
	private final PasswordEncoder passwordEncoder;


	@PostMapping("/login")
	public String login(Model model, LoginForm form) {
		var userInfo = service.searchUserById(form.getLoginId());
		var isCorrectUserAuth = userInfo.isPresent()
				&& passwordEncoder.matches(form.getPassword(), userInfo.get().getPassword());
		if(isCorrectUserAuth) {
			return "redirect:/menu";
		}else {
			//　TODO　エラーメッセージはプロパティファイルで一元管理する
			model.addAttribute("errorMsg","IDとPASSの組み合わせが間違っています。");
			return "login";
		}
	}
}
```

今回の実装に関係がある部分だけを記載しています。

まず、PasswordEncoder型のpasswordEncoderをフィールド化しています。

さっきの@Beanアノテーションがつけられていたのは、PasswordEncoderを戻り値にもつメソッドでした。

なので、passwordEncoderというフィールド内にpasswordEncoder()が装備されるというわけです。

また、psswrodEncoderにはprivate finalが修飾子としてついているので、@RequiredArgsConstructorによってLoginControllerの引数として組み込まれます。

つまり、LoginControllerがインスタンス化されるときに、引数のPasswordEncoder passwordEncoderもインスタンス化され、passwordEncoder()で実装したBCryptPasswordEncoderもインスタンス化されます。

これで、LoginControllerクラスの中でもBCryptPasswordEncoderが実装されたpasswordEncoderというインスタンスを利用することができます。

これでBCryptPasswordEncoderクラスがもっているハッシュ化に関するメソッドが使えます。

DBの方のパスワードは事前にハッシュ化しているので、単純に入力された生のパスワードとDBのパスワードを比較してもfalseが返ってきます。

`&& passwordEncoder.matches(form.getPassword(), userInfo.get().getPassword());`

上記のコードが生のパスワードとハッシュ化されたパスワードの比較です。

一つめの引数はformインスタンスが持っている生のパスワードを引っ張ってきて、二つ目の引数にはuserInfoインスタンスの情報からハッシュ化されたパスワードのみを引っ張ってきています。

次→https://qiita.com/19960417akiho/items/9bfc85a6c790a1fe9e22