サーブレット API の統合
Servlet 2.5+ 統合
このセクションでは、Spring Security が Servlet 2.5 仕様とどのように統合されるかについて説明します。
HttpServletRequest.getRemoteUser()
HttpServletRequest.getRemoteUser()
[Oracle] (英語) は、SecurityContextHolder.getContext().getAuthentication().getName()
の結果を返します。これは通常、現在のユーザー名です。これは、アプリケーションで現在のユーザー名を表示する場合に役立ちます。さらに、これを null としてチェックして、ユーザーが認証されているか匿名であるかを判断できます。ユーザーが認証されているかどうかを知ることは、特定の UI 要素を表示する必要があるかどうかを判断できます(たとえば、ユーザーが認証されている場合にのみ表示されるログアウトリンク)。
HttpServletRequest.getUserPrincipal()
HttpServletRequest.getUserPrincipal()
[Oracle] (英語) は、SecurityContextHolder.getContext().getAuthentication()
の結果を返します。これは、これが Authentication
であることを意味します。これは通常、ユーザー名ベースおよびパスワードベースの認証を使用する場合の UsernamePasswordAuthenticationToken
のインスタンスです。これは、ユーザーに関する追加情報が必要な場合に役立ちます。例: ユーザーの名前と名前を含むカスタム UserDetails
を返すカスタム UserDetailsService
を作成した可能性があります。この情報は、次の方法で取得できます。
Java
Kotlin
Authentication auth = httpServletRequest.getUserPrincipal();
// assume integrated custom UserDetails called MyCustomUserDetails
// by default, typically instance of UserDetails
MyCustomUserDetails userDetails = (MyCustomUserDetails) auth.getPrincipal();
String firstName = userDetails.getFirstName();
String lastName = userDetails.getLastName();
val auth: Authentication = httpServletRequest.getUserPrincipal()
// assume integrated custom UserDetails called MyCustomUserDetails
// by default, typically instance of UserDetails
val userDetails: MyCustomUserDetails = auth.principal as MyCustomUserDetails
val firstName: String = userDetails.firstName
val lastName: String = userDetails.lastName
通常、アプリケーション全体でこれほど多くのロジックを実行するのは悪い習慣です。代わりに、Spring Security とサーブレット API のカップリングを減らすために一元化する必要があります。 |
HttpServletRequest.isUserInRole(String)
HttpServletRequest.isUserInRole(String)
[Oracle] (英語) は、SecurityContextHolder.getContext().getAuthentication().getAuthorities()
に isUserInRole(String)
に渡されたロールを持つ GrantedAuthority
が含まれているかどうかを判別します。通常、ユーザーは ROLE_
プレフィックスをこのメソッドに渡さないでください。これは、自動的に追加されるためです。例: 現在のユーザーが権限 "ROLE_ADMIN" を持っているかどうかを確認する場合は、次を使用できます。
Java
Kotlin
boolean isAdmin = httpServletRequest.isUserInRole("ADMIN");
val isAdmin: Boolean = httpServletRequest.isUserInRole("ADMIN")
これは、特定の UI コンポーネントを表示する必要があるかどうかを判断できます。例: 現在のユーザーが管理者である場合にのみ、管理者リンクを表示できます。
Servlet 3+ 統合
次のセクションでは、Spring Security が統合される Servlet 3 メソッドについて説明します。
HttpServletRequest.authenticate(HttpServletResponse)
HttpServletRequest.authenticate(HttpServletResponse)
[Oracle] (英語) メソッドを使用して、ユーザーが認証されていることを確認できます。それらが認証されていない場合、構成された AuthenticationEntryPoint
を使用して、ユーザーに認証(ログインページにリダイレクト)をリクエストします。
HttpServletRequest.login(String,String)
HttpServletRequest.login(String,String)
[Oracle] (英語) メソッドを使用して、現在の AuthenticationManager
でユーザーを認証できます。例: 以下は、user
のユーザー名と password
のパスワードで認証を試みます。
Java
Kotlin
try {
httpServletRequest.login("user","password");
} catch(ServletException ex) {
// fail to authenticate
}
try {
httpServletRequest.login("user", "password")
} catch (ex: ServletException) {
// fail to authenticate
}
Spring Security で失敗した認証の試行を処理する場合は、 |
HttpServletRequest.logout()
HttpServletRequest.logout()
[Oracle] (英語) メソッドを使用して、現在のユーザーをログアウトできます。
通常、これは、SecurityContextHolder
がクリアされ、HttpSession
が無効になり、"RememberMe" 認証がクリーンアップされるなどのことを意味します。ただし、構成された LogoutHandler
の実装は、Spring Security の構成によって異なります。HttpServletRequest.logout()
が呼び出された後も、レスポンスの書き込みはユーザーが担当していることに注意してください。通常、これにはウェルカムページへのリダイレクトが含まれます。
AsyncContext.start(Runnable)
AsyncContext.start(Runnable)
[Oracle] (英語) メソッドは、クレデンシャルが新しい Thread
に確実に伝達されるようにします。Spring Security の同時実行サポートを使用することにより、Spring Security は AsyncContext.start(Runnable)
をオーバーライドして、Runnable の処理時に現在の SecurityContext
が使用されるようにします。次の例は、現在のユーザーの認証を出力します。
Java
Kotlin
final AsyncContext async = httpServletRequest.startAsync();
async.start(new Runnable() {
public void run() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
try {
final HttpServletResponse asyncResponse = (HttpServletResponse) async.getResponse();
asyncResponse.setStatus(HttpServletResponse.SC_OK);
asyncResponse.getWriter().write(String.valueOf(authentication));
async.complete();
} catch(Exception ex) {
throw new RuntimeException(ex);
}
}
});
val async: AsyncContext = httpServletRequest.startAsync()
async.start {
val authentication: Authentication = SecurityContextHolder.getContext().authentication
try {
val asyncResponse = async.response as HttpServletResponse
asyncResponse.status = HttpServletResponse.SC_OK
asyncResponse.writer.write(String.valueOf(authentication))
async.complete()
} catch (ex: Exception) {
throw RuntimeException(ex)
}
}
非同期サーブレットのサポート
Java ベースの構成を使用する場合は、準備ができています。XML 構成を使用する場合は、いくつかの更新が必要です。最初のステップは、少なくとも 3.0 スキーマを使用するように web.xml
ファイルを更新したことを確認することです。
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>
次に、springSecurityFilterChain
が非同期リクエストを処理するように設定されていることを確認する必要があります。
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
これで、Spring Security は、SecurityContext
が非同期リクエストでも伝播されることを保証します。
それで、どのように機能しますか? あまり興味がない場合は、このセクションの残りの部分をスキップしてください。このほとんどはサーブレット仕様に組み込まれていますが、非同期リクエストで正しく機能するように Spring Security が行う微調整が少しあります。Spring Security 3.2 より前は、HttpServletResponse
がコミットされるとすぐに SecurityContextHolder
からの SecurityContext
が自動的に保存されていました。これにより、非同期環境で問題が発生する可能性があります。次の例を考えてみましょう。
Java
Kotlin
httpServletRequest.startAsync();
new Thread("AsyncThread") {
@Override
public void run() {
try {
// Do work
TimeUnit.SECONDS.sleep(1);
// Write to and commit the httpServletResponse
httpServletResponse.getOutputStream().flush();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}.start();
httpServletRequest.startAsync()
object : Thread("AsyncThread") {
override fun run() {
try {
// Do work
TimeUnit.SECONDS.sleep(1)
// Write to and commit the httpServletResponse
httpServletResponse.outputStream.flush()
} catch (ex: java.lang.Exception) {
ex.printStackTrace()
}
}
}.start()
課題は、この Thread
が Spring Security に認識されていないため、SecurityContext
がそれに伝播されないことです。これは、HttpServletResponse
をコミットするときに、SecurityContext
がないことを意味します。Spring Security が HttpServletResponse
をコミットするときに SecurityContext
を自動的に保存すると、ログインしているユーザーが失われます。
バージョン 3.2 以降、Spring Security は十分にスマートであり、HttpServletRequest.startAsync()
が呼び出されるとすぐに HttpServletResponse
をコミットするときに SecurityContext
を自動的に保存しなくなりました。
Servlet 3.1+ 統合
次のセクションでは、Spring Security が統合される Servlet 3.1 メソッドについて説明します。
HttpServletRequest#changeSessionId()
HttpServletRequest.changeSessionId() (Javadoc) は、Servlet 3.1 以降のセッション固定攻撃から保護するためのデフォルトの方法です。