Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Spring fest2020 spring-security

309 views

Published on

Spring Securityを触ったことがあるけど、裏で何が起こっているか分からず、モヤモヤしてる方にお勧めの資料です。
Spring Securityのキーとなる「Security Filter Chain」と「Security Context」についてじっくり説明しています。
本資料を読んでいただければ、Spring Securityのデバッグやカスタマイズがし易くなるはずです。
※読者の想定としてSpring Securityに触ったことがある方を想定しています。全くの初心者の方は、簡単な認証・認可を事前に動かしてみることをお勧めします。

Published in: Software
  • Be the first to comment

Spring fest2020 spring-security

  1. 1. 1 今こそ知りたいSpring Security 2020/12/17 日本Springユーザ会 土岐 孝平
  2. 2. 自己紹介 • 土岐 孝平 • 合同会社 現場指向 • Webアプリ「メモラキー」の運営 • システム開発の支援 • 研修の講師 • 書籍の執筆 2 [改訂新版]Spring入門https://www.memorarchy.com
  3. 3. 想定する聴講者 • なんとなく使ってはいるが、裏で何が起こってるのか わからず、もやもやしている • 少し触ったけど、難しそうなので自前にしようか悩ん でいる 3 Spring Securityに触ったことがある方が前提です
  4. 4. 本セッションの目的 • Spring Securityが裏で何をやっているかを知って、 問題が起きたときのデバッグや、カスタマイズ方法 の調査を円滑にする • キーとなる以下の2つを抑える – Security Filter Chain – Security Context 4 網羅的な機能の説明や、設定ノウハウの説明は行いません
  5. 5. Security Filter Chainとは? • Spring Securityが提供するFilter※の繋がり • Servlet Filterの仕組みを使って処理を挟み込む • 各Filterは、認証、URLパスの認可、CSRF防止などの役割 を持つ 5 ※ 個々のFilterは ServletのFilterイン タフェースを実装し ているが、Servletコ ンテナに登録されて いる訳ではない Filter1 FilterX DispatcherServlet Delegating FilterProxy ブラウザ ・・ ・ ・・・ Controller ・・ ・ ServletのFilter Chain ★Security Filter Chain Filter2 Spring MVCのサーブ レット
  6. 6. 主なFilter 6 クラス名 役割 CsrfFilter CSRFトークンの発行やチェックを行う HeaderWriterFilter セキュリティ関連のレスポンスヘッダを 設定する BasicAuthenticationFilter Basic認証を行う UsernamePasswordAuthenticationFilter フォーム認証を行う ExceptionTranslationFilter 後続の処理で発生した認証・認可の例 外をキャッチしてハンドリング(エラー画 面に遷移など)する FilterSecurityInterceptor URLのパスの認可を行う SecurityContextPersistenceFilter 「Security Context」を用意して、HTTP セッションやThreadLocalに格納する 次ページで説明
  7. 7. Security Context • 認証したユーザ情報(IDや権限など)を格納するオブジェクト • HTTPセッションに保持されるため、リクエストを跨ってユー ザ情報を参照できる • ThreadLocal(スレッド毎にデータを管理してくれる)にも保持 されるため、リクエスト(スレッド)の処理内の任意のタイミング でユーザ情報を参照可能 – Controllerの引数で受けとる – 画面にユーザ情報を表示するときに参照 – staticメソッドでプログラムの任意の箇所で参照 • SecurityContextHolder.getContext() – 認可時(URLのパス、メソッド、画面内の表示非表示)に参照 7
  8. 8. ThreadLocal ThreadLocalとSecurity Contextのイメージ 8 Aさん Bさん Bさんの スレッド Bさんの Security Context Aさんのリクエスト(スレッド)の処理 Bさんのリクエスト(スレッド)の処理 Aさんの スレッド Aさんの Security Context
  9. 9. Security Filter Chainの作成 • WebSecurityConfigurerAdapter を継承して @EnableWebSecurityを付けたJavaConfigのクラス (@Configurationを付けたクラス)を作成する • @EnableWebSecurityがインポートしている WebSecurityConfigurationクラスの中で、Security Filter Chainのオブジェクトが作成される • 標準的なFilterが自動で登録されるので、メソッドをオーバー ライドして個別の設定を行う 9 @Configuration@Configuration @EnableWebSecurity public class SampleSecurityConfig extends WebSecurityConfigurerAdapter { }
  10. 10. 個別の設定のサンプル 10 @Configuration @EnableWebSecurity public class SampleSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .mvcMatchers("/admin/**").hasAuthority("ADMIN") .anyRequest().authenticated().and() .httpBasic(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); auth .inMemoryAuthentication() .withUser("foo").password(encoder.encode("passfoo")).authorities("ADMIN").and() .withUser("bar").password(encoder.encode("passbar")).authorities("USER"); } } 「/admin」 で始まるパスは「ADMIN」権限が 必要、それ以外は認証されてればOK Basic認証を有効にする 「foo」ユーザは「ADMIN」権限、「bar」ユーザは 「USER」権限という認証情報をメモリ上に保管
  11. 11. 出来上がった主なオブジェクト 11 Authentication Manager UserDetailsService AccessDecision Manager Dispatcher Servlet Delegating FilterProxy ブラウザ ・・ ・ ・・ ・ Controller ServletのFilter Chain ★Security Filter Chain ID・パスワードのチェックを 行う。認証情報は UserDetailsServieから取得 権限のチェックを行う 認証情報を取得。今回はメ モリ上から取得 URLのパスの認可を行う BasicAuthenticationFilter ExceptionTranslationFilter SecurityContextPersistenceFilter FilterSecurityInterceptor Basic認証を行う
  12. 12. Security Filter Chainの動作イメージ • 認可が必要なURLに初回アクセスしたとき – ※主なFilterに絞っています 12 BasicAuthenticationFilter ExceptionTranslationFilter SecurityContextPersistenceFilter FilterSecurityInterceptor DispatcherServlet リクエスト(/admin/foo) レスポンス HTTPセッションにSecurity Context がないため、新しく用意する。 用意したSecurity Contextを、 ThreadLocalに入れる Authorization ID Context AuthorizationヘッダからユーザID・ パスワードを取得して認証する。 OKならユーザ情報をSecurity Contextに入れる スルー URLパスに設定された権限と、 Security Contextのユーザ情報の権 限を比較する。OKなら後続の処理 を呼び出す スルー 例外が投げられた訳 ではないのでスルー スルー ユーザ情報入りの Security ContextをHTTP セッションに入れる
  13. 13. フォーム認証のサンプル 13 @Configuration @EnableWebSecurity public class SampleSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .mvcMatchers("/admin/**").hasAuthority("ADMIN") .anyRequest().authenticated().and() .formLogin() .loginPage("/login").permitAll() .loginProcessingUrl("/login") .usernameParameter("username").passwordParameter("password") .failureUrl("/login?error"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { ・・・ 省略 } } フォーム認証を有効にする ログイン画面のパス。未認証のユーザ を自動的に遷移させる 認証リクエストのパス。POST前提 認証リクエストに乗せるユーザIDとパス ワードのパラメータ名ログイン失敗時に遷移 するパス
  14. 14. ログイン画面のサンプル 14 <!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <body> <form th:action="@{/login}" method="post"> <input type="text" name="username" /> <input type="text" name="password" /> <input type="submit" value="送信" /> </form> </body> </html> POSTメソッドを指定 ※テンプレートエンジンのThymeleafを使用
  15. 15. Security Filter Chainの動作イメージ1/3 • 未認証のユーザが、認可が必要なURLにアクセス 15 UsernamePasswordAuthenticationFilter ExceptionTranslationFilter SecurityContextPersistenceFilter FilterSecurityInterceptor DispatcherServlet ログイン画面にリダイレクトHTTPセッションにSecurity Context がないため、新しく用意する。 用意したSecurity Contextを、 ThreadLocalに入れる 認証のリクエストでは無いので スルー リクエスト(/admin/foo) スルー 未認証なので、 AccessDeniedExceptionをスロー AccessDeniedExceptionをキャッチ して、ログイン画面にリダイレクト するレスポンスを設定。その際、 リクエストの内容をHTTPセッショ ンに格納する(ログイン後に自動 で遷移するため) スルー 空のSecurity Contextを HTTPセッションに入れる。
  16. 16. Security Filter Chainの動作イメージ2/3 • 認証リクエストを送信 16 UsernamePasswordAuthenticationFilter ExceptionTranslationFilter SecurityContextPersistenceFilter FilterSecurityInterceptor /admin/fooにリダイレクトリクエスト(POSTの/login) HTTPセッションから空のSecurity Contextを取得して、ThreadLocal に入れる。 認証リクエストだと判断して、リク エストパラメータのID・パスワード で認証する。認証したユーザ情 報をSecurity Contextに入れる。 HTTPセッションに格納しておいた リクエストにリダイレクトするレス ポンスを設定。 DispatcherServlet ユーザ情報が入った Security ContextをHTTP セッションに入れる。
  17. 17. Security Filter Chainの動作イメージ3/3 • /admin/fooにアクセス 17 SecurityContextPersistenceFilter FilterSecurityInterceptor レスポンスリクエスト(/admin/foo) HTTPセッションからSecurity Context(ユーザ情報入り)を取得 して、ThreadLocalに入れる。 DispatcherServlet ユーザ情報が入った Security ContextをHTTP セッションに入れる。 UsernamePasswordAuthenticationFilter ExceptionTranslationFilter 認証のリクエストでは無いので スルー スルー URLパスに設定された権限と、 Security Contextのユーザ情報の 権限を比較する。OKなら後続の処 理を呼び出す スルー 例外が投げられた訳 ではないのでスルー スルー
  18. 18. Security Filter Chainのログ 1/2 • どのFilterが、どの順番で動いたかをログに出力す ることが可能 – デバッグに便利 • 設定方法 – ロガーの「org.springframework.security」をDEBUGレベルに する – application.propertiesでの設定例 18 logging.level.org.springframework.security=DEBUG
  19. 19. Security Filter Chainのログ 2/2 • 出力例 – 未認証のユーザが、認可が必要なURLにアクセスしたと きのログ(抜粋) 19 /admin/foo at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' /admin/foo at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' No HttpSession currently exists No SecurityContext was available from the HttpSession: null. A new one will be created. /admin/foo at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter' /admin/foo at position 4 of 12 in additional filter chain; firing Filter: 'CsrfFilter' /admin/foo at position 5 of 12 in additional filter chain; firing Filter: 'LogoutFilter' Request 'GET /admin/foo' doesn't match 'POST /logout' /admin/foo at position 6 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' Request 'GET /admin/foo' doesn't match 'POST /login' /admin/foo at position 7 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter' saved request doesn't match /admin/foo at position 8 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter' /admin/foo at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter' /admin/foo at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter' /admin/foo at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter' /admin/foo at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor' Secure object: FilterInvocation: URL: /admin/foo; Attributes: [hasAuthority('ADMIN')] Voter: org.springframework.security.web.access.expression.WebExpressionVoter@7d30e6, returned: -1 o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is anonymous); redirecting to authentication entry point Filterの数と処理 準が表示われる 特別な処理が行われた らログにでる
  20. 20. さいごに • Spring Securityの裏の動きのイメージが沸いて、デ バッグやカスタマイズの作業がし易くなれば幸いで す。 • おすすめのサイト – Spring Securityのマニュアルの「The Big Picture」の章 • https://docs.spring.io/spring- security/site/docs/current/reference/html5/#servlet- architecture 20
  21. 21. 21 ご清聴ありがとうございました
  22. 22. 22 ライセンスについて • JSUGマスコットアイコン(本スライド左下)が残されている場合に限り、本作品(またそれを元にした派生 作品)の複製・頒布・表示・上演を認めます。 • 非商用目的に限り、本作品(またそれを元にした派生作品)の複製・頒布・表示・上演を認めます。 • 本作品のライセンスを遵守する限り、派生作品を頒布することを許可します。

×