Spring SecurityでSpringアプリケーションを保護する方法

Spring Securityフレームワークは、認証と認可を通じてアプリケーションを保護します。デフォルトの状態では、Spring Securityはアプリケーションの各HTTPリクエストパス(またはページ)に単一のグローバルユーザーの認証を要求します。

このフレームワークは非常に柔軟性もあります。これにより、アプリケーションの各HTTPリクエストパスと異なるユーザーに対してカスタマイズされたセキュリティルールを作成できます。そのため、ユーザーの認証を必要としないページ(ホームページなど)のセキュリティ制限を解除できます。そして、特定のユーザータイプのロールと権限を設定します。

Spring Securityをアプリケーションに追加する

Spring Securityをアプリケーションに追加するには2つの方法があります。Spring Initializrを使用して新しいSpring Bootアプリケーションを生成するときに依存関係として選択するか、プロジェクトを生成した後に依存関係セクションのビルド仕様ファイルに追加します。

Gradleプロジェクトのオプションのいずれかを選択した場合は、依存関係ファイルはbuild.gradleです。ただし、Mavenを選択した場合は、そのファイルはpom.xmlです。

build.gradleファイルには、次の依存関係を含める必要があります。

dependencies {implementation 'org.springframework.boot:spring-boot-starter-security'}

pom.xmlファイルには、次の依存関係を含める必要があります。

org.springframework.bootspring-boot-starter-security

記事で使用されているサンプルアプリケーションは、このGitHubリポジトリで入手でき、MITライセンスに基づいて無料で使用できます。

Spring Securityを使用する

Spring Securityの依存関係をアプリケーションに追加したら、すぐにフレームワークの使用を開始できます。アプリケーションを実行してから、Spring Bootのホームページ(またはアプリケーションの任意のページ)に移動します。サンプルアプリケーションは、次の初期コントローラーを使用して、Spring Bootのデフォルトのlocalhost:8080リクエストを制御します。

package com.springSecurityDemo.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class WebController {@GetMapping("/")public String home() {return "Welcome!";}}

上記の単一のコントローラークラスを追加した後にアプリケーションを実行すると、次の初期ビューが生成されます。

localhost:8080/loginページに自動的に移動され、アプリケーションの他のページにアクセスする前にこのページに移動することに注意してください。この段階では、デフォルトのユーザー名(ユーザー)と自動生成されたパスワード(コンソールにあります)を提供する必要があります。コンソールには次のような行が生成されます。

Using generated security password: c4070465-4c65-4e72-8c3f-3800e631ba81

アプリケーションを再起動するたびに、自動生成されたパスワードは変更されますが、ユーザー名は同じままです。デフォルトのユーザー名とパスワードを入力すると、アプリケーションの適切なビューに移動します。

Spring Securityをカスタマイズする

アプリケーションのセキュリティをカスタマイズするには、Spring Securityのデフォルト設定をオーバーライドする必要があります。しかし、その前に(Spring Webがすでにインストールされていることを前提として)、このサンプルアプリケーションにはいくつかの他の依存関係が必要です。

  • Spring Data JPA
  • MySQL JDBCドライバー
  • Thymeleaf
  • Lombok

Thymeleafフレームワークは、さまざまなビューを生成します。Lombokは、オブジェクトクラスのコードを削減するのに役立ちます。JPAライブラリとMySQLドライバーを使用すると、アプリケーションでMySQLデータベースを使用できますが、使い慣れたデータベースを使用することもできます。データベースを使用するということは、リソースファイルの下のapplications.propertiesファイルを構成することを意味します。

spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/spring_securityspring.datasource.username=rootspring.datasource.password=1234spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.jpa.hibernate.ddl-auto=update

上記の構成コードを使用すると、rootのユーザー名とパスワード(1234)でspring_securityという名前のローカルMySQLデータベースに接続できます。このデータを、データベース名と資格情報に合わせて更新する必要があります。

追加の依存関係を追加してデータベースを作成した後、アプリケーションに表示するビューの数を決定できます。また、各ページのセキュリティがどのようになっているかを知る必要があります。サンプルアプリケーションには6つのビューがあります。

  • ホームページ
  • 登録ページ
  • ログインページ
  • ログアウトページ
  • ユーザーページ
  • エラーページ

ユーザー認証が必要なビューは、ユーザーページのみです。このページは、最初に登録してからアプリケーションにサインインしたユーザーのみがアクセスできます。Spring Bootのデフォルトのパッケージに加えて、アプリケーションにさらに4つのパッケージを作成する必要があります。

登録コントローラークラス

コントローラーパッケージには、HTTPリクエストを処理するクラスが含まれます。ページの機能に応じて、通常は各HTTPリクエストを1つのコントローラークラスにまとめることができます。WebControllerクラスの場合と同様です。ただし、登録ビューにはよりユニークな機能があるため、プライベートコントローラークラスを持つことができます。

@Controller@RequestMapping("/register")public class RegistrationController {private UserRepository userRepo;private PasswordEncoder passwordEncoder;public RegistrationController( UserRepository userRepo, PasswordEncoder passwordEncoder) {this.userRepo = userRepo;this.passwordEncoder = passwordEncoder;}@GetMappingpublic String registerForm() {return "registration";}@PostMappingpublic String processRegistration(RegistrationForm form) {userRepo.save(form.toUser(passwordEncoder));return "redirect:/login";}}

RegistrationControllerクラスは、アプリケーションのセキュリティ側面へのゲートウェイです。@RequestMappingアノテーションは、このコントローラーが処理するリクエストのタイプ(localhost:8080/registerへのリクエスト)を指定します。

@GetMappingアノテーションは、アプリケーションが/registerのリクエストを受け取った場合、registrationForm()メソッドがそのリクエストを処理して登録ビューを返す必要があることを示すだけです。

訪問者が登録ボタンをクリックすると、@PostMappingアノテーションが機能します。processRegistration()メソッドを使用すると、UserRepositoryクラスを使用して、RegistrationFormクラスから取得したユーザーデータをデータベースに投稿できます。ただし、このデータを格納する前に、processRegistration()メソッドはSpringのPasswordEncoderインターフェースを使用してユーザーのパスワードを暗号化します。

新しいセキュリティ設定を作成する

Spring 3.1以降、開発者はJavaを使用してSpring Securityの設定を作成できるようになりました。つまり、XMLではなくクラスです。これらの構成クラスに必要な主なものは、@Configurationアノテーションです。

@Configurationpublic class SecurityConfiguration {}

@Configurationアノテーションは、上記のクラスが構成クラスであることを示します。これらのクラスは、Springがアプリケーションのさまざまなコンポーネント(またはBean)を作成して管理するために使用するコンテナーであるSpringアプリケーションコンテキストにBeanを提供します。SecurityConfigurationクラスの最初のBeanはpasswordEncoder Beanです。

@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}

RegistrationControllerクラスは、新しいパスワードをデータベースに保存する前にpasswordEncoder Beanを使用して暗号化します。SecurityConfigurationクラスに追加する必要があるもう1つの重要なBeanは、userDetailsService Beanです。

@Beanpublic UserDetailsService userDetailsService(UserRepository userRepo) {return username -> {Customer customer = userRepo.findByUsername(username);if (customer != null)return customer;throw new UsernameNotFoundException("Customer '" + username + "' not found");};}

userDetailsService Beanは、Spring SecurityのUserDetailsServiceインターフェースを使用して、顧客のログインセッション中に認証のためにユーザーのユーザー名とパスワードを取得します。したがって、顧客がログインビューでログインボタンをクリックするとすぐに、userDetailsService Beanが起動します。

userDetailsService Beanは、UserRepositoryを通じて、データベース内の既存の顧客すべてにアクセスできます。このインターフェースは、UserRepositoryを使用して一致するユーザー名とパスワードを持つユーザーを特定し、この顧客のすべての属性をオブジェクトとして返します。

返されたオブジェクトが顧客である場合、この顧客はアプリケーションにアクセスできます。それ以外の場合は、ページが自動的に更新され、ユーザーは有効な資格情報を入力できます。

フィルターチェーン

Spring SecurityのSecurityFilterChainインターフェースは、Spring Securityの設定で重要な役割を果たす、便利なアプリケーションプログラミングインターフェース(API)です。このインターフェースは、Spring SecurityのHttpSecurityクラスと連携して、特定のHTTPリクエストのフィルターチェーンを作成します。

@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) -> authorize.requestMatchers("/user").hasAuthority("USER").anyRequest().permitAll()).formLogin(formLogin -> formLogin.loginPage("/login").defaultSuccessUrl("/user", true)).logout(logout -> logout.logoutSuccessUrl("/logout"));return http.build();}

上記のfilterChain Beanは、SecurityFilterChain APIを使用して、いくつかのタスクを実行します。まず、HttpSecurityクラスを使用して、USERのロールを持つユーザーのみがlocalhost:8080/userにアクセスできるように指定します。そして、ユーザーは登録後にこのロールを取得します。これは、各新しい顧客オブジェクトが実装するgetAuthorities()メソッドのおかげです。

@Overridepublic Collection

フィルターチェーンは、アプリケーション内の他のすべてのURLへの認証されていないアクセスを許可します。filterChain Beanは、HttpSecurityクラスオブジェクトのformLogin()メソッドとlogout()メソッドも利用します。

これらのメソッドを使用すると、ユーザーがタスクを実行した後に、特定のページに自動的に誘導できます。そのため、正しい資格情報を入力して/loginページのログインボタンをクリックしたユーザーは、/userページに自動的に誘導されます。

最後に、filterChain Beanはフィルターチェーンを構築して返し、承認されたユーザーがアプリケーションにアクセスできるようにします。SecurityConfigurationクラスの3つのBeanはすべて連携してアプリケーションを保護します。

ただし、filterChain Beanは、各HTTPリクエストの承認レベルを指定するという、より重要な役割を果たします。アプリケーションにページを追加し始めると、filterChain Beanを使用してセキュリティレベルを設定できます。

Spring Securityの大きな利点

Spring Securityを使用すると、アプリケーションへのアクセス権を持つユーザーだけでなく、ユーザーが持つことができるアクセス権の種類(ユーザーロール機能を通じて)も完全に制御できます。アクセス制御は、どのアプリケーションにとっても最も重要な側面の1つです。アクセス制御の障壁が限られているために、一般ユーザーにアプリケーションへのフィルタリングされていないアクセスを許可すると、コストのかかる間違いが生じる可能性があります。