はじめに
本記事はSpringSecurity6.2.4の学習記録です。
やりたかったことは以下のみです。
- 任意で指定したリソースは認証・認可不要
はまったこと
指定リソース以外はログイン必須、指定リソースだけはログイン不要の2点を両立させることが出来ず数時間はまりました。
誤ったコード
package com.example.xxx.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/").permitAll()
.requestMatchers("sample").permitAll()
.anyRequest().authenticated()
)
.formLogin((form) -> form
.loginPage("/login")
.permitAll()
)
.logout((logout) -> logout.permitAll());
return http.build();
}
}
..............
.....
...
どこが間違ってんだ?!
そうなんです!!実は上記コード、記述自体は間違っていなかったんです。
最小構成で徐々に進めていこうという考えのもと進めていたため
ハンドラーメソッドとカスタムログインページを用意していなかった。
というのが上手く動作しなかった原因でした。
以下2つのファイルを用意することで上記コードも正常に機能します。
package com.example.xxx.controller.auth;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AuthController {
@GetMapping("/login")
public String login() {
return "auth/login";
}
}
<!DOCTYPE html>
<html lang="ja"
xmlns:th="http://www.thymeleaf.org"
>
<head>
<meta charset="UTF-8">
<title>ログインページ</title>
</head>
<body>
<section>
WIP:ログイン画面
</section>
</body>
</html>
じゃあ認証認可をカスタムする場合、必ずログインページを自前で用意しないといけないかというと、もちろん不要です。
そのままSpringSecurityが用意してくれているデフォルトログインページを利用することも可能です。
ではどこをどうしたら良かったのかというと以下コメントアウト部分の.loginPage("/login")
を削除するだけでOKです。
フォームログインで/login
を使用しますよーって自分で書いてるのに「それに対応するハンドラーメソッドが存在せず永遠にリダイレクト処理が実施されてエラーになっていた。」というのがオチでした。
package com.example.officesmile.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/").permitAll()
.requestMatchers("sample").permitAll()
.anyRequest().authenticated()
)
.formLogin((form) -> form
// .loginPage("/login")
.permitAll()
)
.logout((logout) -> logout.permitAll());
return http.build();
}
}
最後にそれぞれの意味をコメント付与した状態で再掲しておきます。
package com.example.xxx.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.formLogin((form) -> form // フォーム認証設定を開始する
.loginPage("/login") // ログインページ表示のハンドラーメソッドGET /login
.permitAll() // 認証無しでアクセス可能
)
.logout((logout) -> logout.permitAll()) // ログアウト処理のPOST /logout は認証無しでアクセス可能
.authorizeHttpRequests((requests) -> requests // URL毎の認可設定を開始する
.requestMatchers("/").permitAll() // "/" へはログインなしでもアクセス可能
.requestMatchers("/sample").permitAll() // "/sample" へはログインなしでもアクセス可能
.anyRequest().authenticated() // その他のURLへはログイン後アクセス可能
);
return http.build();
}
}
ちなみに意図的に記述順変えましたがlogin, logout, URL毎のような各設定毎の順番は気にしなくていいみたいです。(動作確認済み)
ただしURL毎の認可設定内の順番は上からみてるらしいので意図した設定が出来ているかどうかは注意が必要です。
最後に
元々やりたかったことと、追加で気になった以下2点を今後やってみようと思います。
- h2-consoleへのアクセスもログイン前からアクセスできるようにする。
- HTTP Method単位でのアクセス制御も可能かどうかの検証。(現状の考えとしては基本的にlogout以外はGETの制御のみでPOSTに対する制御はURLではなく、メソッドに対して制御する必要があるのではと思ってる。)