Open6

Microsoft Entra ID OAuth 2.0 Login with Spring Security

まっぴぃまっぴぃ

application.yaml のポイント

  • issuer-uri は御作法なので設定忘れない
  • provider で設定した値は registration の provider での指定に使用する (この例だと azure-active-directory)
  • registration で設定する値は、サービスプリンシパルのリダイレクト URI で使用される値になるので注意 (この例だと spring-boot-sample)

application.yaml
spring:
  security:
    oauth2:
      client:
        provider:
          azure-active-directory:
            issuer-uri: https://login.microsoftonline.com/<テナントID>/v2.0 # 御作法
        registration:
          spring-boot-sample: # ここはリダイレクトURIの一部に使われる値になる
            provider: azure-active-directory # 上段の provider で定義している値を指定
            client-id: <サービスプリンシパルのクライアントID>
            client-secret: <サービスプリンシパルのシークレット値>
            scope:
              - openid
              - email
              - profile
まっぴぃまっぴぃ

SecurityConfig の SecurityFilterChain でアクセスするページの認可を制御したい場合は

  • authorizeHttpRequests でアクセス先のページの認可を定義
  • oauth2Login はいったんデフォルト踏襲(後述でカスタマイズ)
WebSecurityConfig.java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
                        .requestMatchers("/").permitAll()
                        .requestMatchers("/welcome").authenticated()
                        .requestMatchers("/admin").hasRole("Admin")
                        .anyRequest().denyAll())
                .oauth2Login(Customizer.withDefaults());

        return http.build();
    }
}
まっぴぃまっぴぃ

OAuth2 Loginでログインページをカスタマイズしたい場合は、oauth2Loginを書き換え、ログインページのhtmlでログインボタンの遷移先を/oauth2/authorization/{registrationで設定した値}にする

oauth2Login については、

  • loginPage は /login/oauth2 固定、かつ @Controller で @GetMapping 指定を忘れない
  • loginProcessingUrl はリダイレクトURLのURI
  • defaultSuccessUrl はログイン成功時に遷移するページの指定
  • authorizationEndpoint の指定はオプションだが、デフォルト設定を踏襲
  • 最後の permitAll() を忘れると永遠とログイン認証のリダイレクトが走るので指定を忘れない
WebSecurityConfig.java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                ... // 省略
                )
                .oauth2Login(oauth2 -> oauth2
                        .loginPage("/login/oauth2")
                        .loginProcessingUrl("/login/oauth2/code/*")
                        .defaultSuccessUrl("/welcome")
                        .authorizationEndpoint(Customizer.withDefaults())
                        .permitAll());

        return http.build();

ログインページの例

login.html
<a href="/oauth2/authorization/spring-boot-sample" class="btn btn-microsoft btn-user btn-block">
    <i class="fab fa-fw"></i> Login with Microsoft (work or school)
 </a>
まっぴぃまっぴぃ

Spring Security 5.8 から、未認証時のアクセス時のリクエストキャッシュの動作が代わり、?continueパラメータが認証後につくようになった
このため、本来存在しないURLにアクセスした場合、

  1. ログインページにリダイレクト
  2. 認証
  3. {本来存在しないURL}?continue へアクセス
    という無駄な処理を生む原因になっている

これを防ぐには、NullRequestCacheを実装し、未認証時からのアクセスは全てdefaultSuccessUrlに飛ばすという方法がある

WebSecurityConfig.java
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    RequestCache nullRequestCache = new NullRequestCache();
    http
                .requestCache(cache -> cache
                        .requestCache(nullRequestCache))
                .authorizeHttpRequests(auth -> auth
                ... // 省略
                )
                .oauth2Login(oauth2 -> oauth2
                ... // 省略
                );

    return http.build();
}