Spring Security + MyBatis ログイン機能作成メモ

## Eclipseで Spring スタータープロジェクト作成後、MyBatisでクラス自動作成
spring-boot-starter-securityのバージョンが上がり、WebSecurityConfigurerAdapterクラスが使えなくなったため整理。

- generatorConfig.xml
手動でやってるので若干違うかも・・・

```
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration >
  <!-- mysql connector -->
  <classPathEntry
      location="/Users/[USER]/.m2/repository/mysql/mysql-connector-java/8.0.23/mysql-connector-java-8.0.23.jar" />

  <context id="context1" >
  
    <!-- JDBC -->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
      connectionURL="jdbc:mysql://localhost:3306/TestDB"
      userId="root" password="">

      <!-- Table Configuration matched more than one table 対策 -->
      <!-- http://www.mybatis.org/generator/usage/mysql.html -->
      <property name="nullCatalogMeansCurrent" value="true"/>
      
    </jdbcConnection>
    
      <!-- 自動生成するエンティティ -->

      <javaModelGenerator
          targetPackage="com.example.demo.entity"
          targetProject="src/main/java/"
      />
      <sqlMapGenerator
          targetPackage="com.example.demo.entity"
          targetProject="src/main/java/"
      />
      <javaClientGenerator
          targetPackage="com.example.demo.entity"
          targetProject="src/main/java/"
          type="XMLMAPPER"
      />

    <!-- 自動生成対象のテーブル名 -->
    <table tableName="LoginUser" domainObjectName="LoginUser">
      <generatedKey column="id" sqlStatement="JDBC"/> <!-- auto increament 余計なXMLが入ってしまうため不要かも？ -->
    </table>

  </context>
</generatorConfiguration>
```

テスト用の対象テーブル
```
CREATE TABLE IF NOT EXISTS LoginUser (
  id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(64) NOT NULL,
  password VARCHAR(128) NOT NULL
);
```

## ライブラリのロードとセキュリティ用のクラス作成
　以下のサンプルはアノテーションとimplementsが正しければクラス名はなんでもいいです。

-pom.xml

```
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity6</artifactId>
		</dependency>
````

- SecurityConfig.java
ユーザー作成の時にパスワードをエンコードした処理をBCryptPasswordEncoderで定義しておく
これがないとユーザー認証ができないので注意

```java
@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean //パスワードのエンコード
    BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

	@Bean
	SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
	    
		//ログイン処理の実装
		http.formLogin(login -> login //フォーム認証の設定
		    .loginProcessingUrl("/login") //ログイン処理のパス
			.loginPage("/login") //ログインページの指定
			.defaultSuccessUrl("/hello") //ログイン成功後の遷移先（任意で「/hello」とする）
			.failureUrl("/login?error=true") //ログイン失敗時の遷移先（任意で「/login?error」とする）
			.permitAll()
		).logout(logout -> logout //ログアウトの設定記述開始
			.logoutSuccessUrl("/login") //ログアウト成功後のリダイレクト先URL
		).authorizeHttpRequests(authz -> authz //URLごとの認可設定
			.requestMatchers(PathRequest.toStaticResources().atCommonLocations())
			.permitAll() //css,jsなどへのアクセス許可
		    .anyRequest().authenticated() //ログインページ以外のURLへはログイン後のみアクセス可能
		);
		
		return http.build();
	}
}
```
SecurityFilterChainがログイン時に自動的に実行される

## 認証用クラスの作成

UserDetailsクラスをimplementsして作成
ログイン後のこのクラスのインスタンスが保持される
${#authentication.principal} でテンプレート内からアクセスできる

```java
public class AuthUserDetails implements UserDetails {

	// 認証するユーザー情報
	private final LoginUser loginUser;
	private final Collection<? extends GrantedAuthority> authorities;
	
	// コンストラクタ 認証したい任意のユーザー情報を指定
	public AuthUserDetails(LoginUser loginUser) {
		this.loginUser = loginUser;
		// 仮の処理
		List<String> list = new ArrayList<String>(Arrays.asList(loginUser.getUsername()));
		this.authorities = list
				.stream()
				.map(role -> new SimpleGrantedAuthority(role))
				.toList();
	}
	
	// テンプレート内で ${#authentication.principal.loginUser} アクセスできるようにするため
	public LoginUser getLoginUser() {
		return loginUser;
	}
	
	// 各メソッドをオーバーライド
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO 自動生成されたメソッド・スタブ
		return authorities;
	}
# 略
```

## 認証用サービスクラスの作成
UserDetailsServiceクラスをimplementsして作成
loadUserByUsernameメソッドをオーバーライドしてSQLを実行されるようにする
テーブルからユーザーを見つけたらフォームで送信したパスワードはフレームワーク側で認証してくれる

```java
@RequiredArgsConstructor
@Service
public class AuthUserDetailsServiceImpl implements UserDetailsService {

    @Autowired 
    private LoginUserMapper loginUserMapper;

    // loadUserByUsername のオーバーライド UserDetails オブジェクトを返却する
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    	
        // マッパーまたはレポジトリ内でこの処理は定義しておく       
        LoginUser loginUser = loginUserMapper.findByUsername(username);
        
    	AuthUserDetails user = new AuthUserDetails(loginUser);
		return user;
    }
}
```

メソッドの最後でUserDetailsクラスをimplementsして作成したクラスのインスタンスを返却する
ユーザー情報を認証以降も利用する場合は、上記クラス内で持てるようにプロパティ、getter/setterを用意しておく


## コントローラー、テンプレートの定義
- コントローラー
テンプレートを返すだけのシンプルな例

```java
@Controller
public class SecurityController {

    @GetMapping("/login") 
    public String login() {
        return "login";
    }
}
```

- ログイン用テンプレート　

```html        
<form th:action="@{/login}" method="post" name="Login_Form"
            class="form-signin">
    <h2 class="form-signin-heading">ログインしてください</h2>
    <hr class="colorgraph">
    <p th:if="${param.error}">ログインに失敗しました</p>
    <br>
    <input type="text" name="username" placeholder="username" value="hoge"/><br>
    <input type="password" name="password" placeholder="password" value="12345678"/> <br>
    <button name="Submit" value="Login" type="Submit">Login</button>
</form>
```

- ログイン後のテンプレート
　
```html
<body th:with="user=${#authentication.principal.loginUser}">
	<!-- 認証済みユーザー情報にアクセス -->

    <h1>ログイン成功</h1>
    <h2><span th:text="${user.username}"></span> さんHello!!!!</h2>

    <form th:action="@{/logout}" method="post">
    <button>Logout</button>
    </form>
</body>
```

## まとめ
アノテーションをつけてクラスを定義するだけで　Spring Security が全て自動的に認証してくれるのですごく楽です。

- https://github.com/katafuchix/SpringSecurityExample_user_login