Spring Security でログイン認証時の入力パラメータをJSONにする方法

# はじめに
Spring Securityでの認証処理は、ログインフォームに入力された値をPOSTされることが前提です。
しかし、ログイン認証のパラメータをJSONにしたい場合は、デフォルトの仕様だとうまく動きません。

UsernamePasswordAuthenticationFilterを継承したフィルタを作成して行う方法などもありますが、こちらでは、手っ取り早く実装する方法を紹介したいと思います。

# やり方
本来の認証処理に近づけたい場合は、フィルタを使用する方法だと思いますが、こちらで紹介する方法は、公式サイトにあった方法を踏襲しています。
ここで紹介する例は、次のURLで紹介している方法を少し改変しています。

https://spring.pleiades.io/spring-security/reference/servlet/authentication/session-management.html#store-authentication-manually

```java:任意のコントローラークラス
@RestController
public class SampleLoginController {

    @Autowired
    private AuthenticationManager authenticationManager;
    
    private final SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
    
    @PostMapping("/login")
    public void login(@Valid @RequestBody LoginRequest loginRequest,
        BindingResult result, HttpServletRequest request, HttpServletResponse response) { 
    
        if (result.hasErrors()) {
            // 入力チェックでエラー
            // ...
            return;
        }
        
        // 認証情報を設定
        UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(
            loginRequest.getUsername(), loginRequest.getPassword()); 
    
        try {
            // 認証
            Authentication authentication = authenticationManager.authenticate(token); 
    
            // セッションの無効化
            request.getSession().invalidate();
        
            // 認証情報をセッションなどに設定
            SecurityContext context = securityContextHolderStrategy.createEmptyContext();
            context.setAuthentication(authentication); 
            securityContextHolderStrategy.setContext(context);
            new HttpSessionSecurityContextRepository().saveContext(context, request, response);
    
            // その他認証が通った後に行いたい処理がある場合はこちらに記述
            // ...
            
        } catch (AuthenticationException e) {
            // 認証NGの場合
            // ...
            
        }
    }

    class LoginRequest {
    
        @NotNull
        private String username;
        
        @NotNull
        private String password;
    
        // getters and setters
    }
}
```

処理をコントローラーに直接書いちゃうという方法です。
ソースの見通しはこちらの方がわかりやすいかな？と思います。
入力チェック処理を追加したり、認証処理が通った後に現セッションを一旦無効化したり、認証が通らなかった時の処理を追加しています。
このサンプルでは戻り値はvoidですが、戻り値を設定することで、認証が通った場合、認証がNGだった場合、それぞれにおいてJSONを返したりすることもできます。

Security Configは、以下のようにコントローラーにDIするauthenticationManagerを定義します。

```java:SecurityConfig
// デフォルトのAuthenticationManager
@Bean
public AuthenticationManager authenticationManager(final AuthenticationConfiguration authenticationConfiguration) throws Exception {
    return authenticationConfiguration.getAuthenticationManager();
}
```

そして、更に、未認証の状態で認証が必要な処理にアクセスされた場合の設定をSeurityConfigで行います。
```java:SecurityConfig
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http)
			throws Exception {

		// 未ログインでアクセスされた場合のURLを設定
		http.exceptionHandling(exception -> exception
				.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/unauthenticated")));

        // ...
    }
```

フォーム認証関連の設定はSecurityConfigでは一切行いません。
よって、httpSecurity#formLogin() での各種設定は行いません。
それ以外の設定は、ログインフォームでの認証の時と同じです。

# 最後に
こちらで紹介した方法を紹介しているページは見つからなかったのでここで紹介してみました。
本来のSpring Securityの認証処理に出来るだけ近づけたい場合は、フィルタを別途作成する方法かと思います。ネット上にもいくつか方法は紹介されています。
そちらの方法は、以下のサンプルが参考になると思います。

https://github.com/charon-r13b/spring-security-json-login-example