最新の安定バージョンについては、Spring Security 6.3.1 を使用してください!

永続的な認証

ユーザーが保護されたリソースを初めてリクエストすると、資格情報の入力を求められます。資格情報の入力を求める最も一般的な方法の 1 つは、ユーザーをログインページにリダイレクトすることです。保護されたリソースをリクエストする認証されていないユーザーの要約された HTTP 交換は、次のようになります。

例 1: 認証されていないユーザーが保護されたリソースをリクエストする
GET / HTTP/1.1
Host: example.com
Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b
HTTP/1.1 302 Found
Location: /login

ユーザーはユーザー名とパスワードを送信します。

送信されたユーザー名とパスワード
POST /login HTTP/1.1
Host: example.com
Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b

username=user&password=password&_csrf=35942e65-a172-4cd4-a1d4-d16a51147b3e

ユーザーを認証すると、セッション固定攻撃を防ぐために、ユーザーは新しいセッション ID に関連付けられます。

認証されたユーザーは新しいセッションに関連付けられています
HTTP/1.1 302 Found
Location: /
Set-Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8; Path=/; HttpOnly; SameSite=Lax

後続のリクエストには、セッションの残りの部分でユーザーを認証するために使用されるセッション Cookie が含まれます。

クレデンシャルとして提供される認証済みセッション
GET / HTTP/1.1
Host: example.com
Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8

SecurityContextRepository

Spring Security では、ユーザーと将来のリクエストの関連付けは SecurityContextRepository (Javadoc) を使用して行われます。

HttpSecurityContextRepository

SecurityContextRepository のデフォルトの実装は、SecurityContext を HttpSession に関連付ける HttpSessionSecurityContextRepository (Javadoc) です。ユーザーは、ユーザーを別の方法で後続のリクエストに関連付けるか、まったく関連付けない場合は、HttpSessionSecurityContextRepository を SecurityContextRepository の別の実装に置き換えることができます。

NullSecurityContextRepository

SecurityContext を HttpSession に関連付けることが望ましくない場合(つまり、OAuth で認証する場合)、NullSecurityContextRepository (Javadoc) は SecurityContextRepository の実装であり、何もしません。

RequestAttributeSecurityContextRepository

RequestAttributeSecurityContextRepository (Javadoc) は、SecurityContext をリクエスト属性として保存して、SecurityContext をクリアする可能性のあるディスパッチ型間で発生する単一のリクエストに対して SecurityContext が確実に利用できるようにします。

例: クライアントがリクエストを行い、認証された後、エラーが発生したと仮定します。サーブレットコンテナーの実装に応じて、エラーは、確立された SecurityContext がクリアされてから、エラーディスパッチが行われることを意味します。エラーディスパッチが行われると、SecurityContext は確立されません。これは、SecurityContext が何らかの形で永続化されない限り、エラーページが SecurityContext を認証または現在のユーザーの表示に使用できないことを意味します。

RequestAttributeSecurityContextRepository を使用する
  • Java

  • XML

public SecurityFilterChain filterChain(HttpSecurity http) {
	http
		// ...
		.securityContext((securityContext) -> securityContext
			.securityContextRepository(new RequestAttributeSecurityContextRepository())
		);
	return http.build();
}
<http security-context-repository-ref="contextRepository">
	<!-- ... -->
</http>
<b:bean name="contextRepository"
	class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />

SecurityContextPersistenceFilter

SecurityContextPersistenceFilter (Javadoc) は、SecurityContextRepository を使用するリクエスト間で SecurityContext を永続化するロールを果たします。

securitycontextpersistencefilter

number 1 アプリケーションの残りの部分を実行する前に、SecurityContextPersistenceFilter は SecurityContextRepository から SecurityContext をロードし、それを SecurityContextHolder に設定します。

number 2 次に、アプリケーションが実行されます。

number 3 最後に、SecurityContext が変更された場合は、SecurityContextPersistenceRepository を使用して SecurityContext を保存します。これは、SecurityContextPersistenceFilter を使用する場合、SecurityContextHolder を設定するだけで、SecurityContext が SecurityContextRepository を使用して永続化されることを意味します。

場合によっては、SecurityContextPersistenceFilter メソッドが完了する前に、レスポンスがコミットされてクライアントに書き込まれます。例: リダイレクトがクライアントに送信された場合、レスポンスはすぐにクライアントに書き戻されます。これは、セッション ID をすでに書き込まれたレスポンスに含めることができなかったため、ステップ 3 で HttpSession を確立できないことを意味します。発生する可能性のある別の状況は、クライアントが正常に認証された場合、SecurityContextPersistenceFilter が完了する前にレスポンスがコミットされ、SecurityContextPersistenceFilter が完了する前にクライアントが 2 番目のリクエストを行うと、2 番目のリクエストに誤った認証が存在する可能性があります。

これらの問題を回避するために、SecurityContextPersistenceFilter は HttpServletRequest と HttpServletResponse の両方をラップして、SecurityContext が変更されたかどうかを検出し、変更された場合は、レスポンスがコミットされる直前に SecurityContext を保存します。

SecurityContextHolderFilter

SecurityContextHolderFilter (Javadoc) は、SecurityContextRepository を使用するリクエスト間で SecurityContext をロードするロールを果たします。

securitycontextholderfilter

number 1 アプリケーションの残りの部分を実行する前に、SecurityContextHolderFilter は SecurityContextRepository から SecurityContext をロードし、それを SecurityContextHolder に設定します。

number 2 次に、アプリケーションが実行されます。

SecurityContextPersisteneFilter とは異なり、SecurityContextHolderFilter は SecurityContext のみをロードし、SecurityContext は保存しません。つまり、SecurityContextHolderFilter を使用する場合は、SecurityContext を明示的に保存する必要があります。

SecurityContext の明示的な保存
  • Java

  • XML

public SecurityFilterChain filterChain(HttpSecurity http) {
	http
		// ...
		.securityContext((securityContext) -> securityContext
			.requireExplicitSave(true)
		);
	return http.build();
}
<http security-context-explicit-save="true">
	<!-- ... -->
</http>