このセクションでは、 Spring Security がサーブレット API とどのように統合されるかについて説明します。 servletapi-xml: GitHub (英語) サンプルアプリケーションは、これらの各メソッドの使用方法を示しています。
HttpServletRequest.getRemoteUser(): Oracle (英語) は、通常は現在のユーザー名である SecurityContextHolder.getContext().getAuthentication().getName()
の結果を返します。これは、アプリケーションで現在のユーザー名を表示する場合に役立ちます。さらに、これが null であるかどうかのチェックを使用して、ユーザーが認証済みか匿名かを示すことができます。ユーザーが認証されているかどうかを知ることは、特定の UI 要素を表示するかどうかを判断できます(つまり、ユーザーが認証された場合にのみログアウトリンクを表示する必要があります)。
HttpServletRequest.getUserPrincipal(): Oracle (英語) は SecurityContextHolder.getContext().getAuthentication()
の結果を返します。これは、ユーザー名とパスワードベースの認証を使用する場合、通常 UsernamePasswordAuthenticationToken
のインスタンスである Authentication
であることを意味します。これは、ユーザーに関する追加情報が必要な場合に役立ちます。例: ユーザーの姓名を含むカスタム UserDetails
を返すカスタム UserDetailsService
を作成した可能性があります。次の方法でこの情報を取得できます。
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();
![]() | メモ |
---|---|
通常、アプリケーション全体でこれほど多くのロジックを実行するのは悪い習慣です。代わりに、Spring Security とサーブレット API のカップリングを減らすために一元化する必要があります。 |
HttpServletRequest.isUserInRole(String): Oracle (英語) は、 SecurityContextHolder.getContext().getAuthentication().getAuthorities()
に、 isUserInRole(String)
に渡されたロールを持つ GrantedAuthority
が含まれているかどうかを判別します。通常、このメソッドは自動的に追加されるため、ユーザーはこのメソッドに「ROLE_」プレフィックスを渡さないでください。例: 現在のユーザーが権限 "ROLE_ADMIN" を持っているかどうかを確認したい場合は、次を使用できます。
boolean isAdmin = httpServletRequest.isUserInRole("ADMIN");
これは、特定の UI コンポーネントを表示する必要があるかどうかを判断できます。例: 現在のユーザーが管理者である場合にのみ、管理者リンクを表示できます。
次のセクションでは、Spring Security が統合される Servlet 3 メソッドについて説明します。
HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse): Oracle (英語) メソッドを使用して、ユーザーが認証されていることを確認できます。認証されていない場合、構成された AuthenticationEntryPoint を使用して、ユーザーに認証をリクエストします(つまり、ログインページにリダイレクトします)。
HttpServletRequest.login(String,String): Oracle (英語) メソッドを使用して、現在の AuthenticationManager
でユーザーを認証できます。例: 以下は、ユーザー名「user」とパスワード「password」で認証を試みます。
try { httpServletRequest.login("user","password"); } catch(ServletException e) { // fail to authenticate }
![]() | メモ |
---|---|
Spring Security に失敗した認証試行を処理させたい場合は、ServletException をキャッチする必要はありません。 |
HttpServletRequest.logout(): Oracle (英語) メソッドを使用して、現在のユーザーをログアウトできます。
通常、これは、SecurityContextHolder がクリアされ、HttpSession が無効化され、「自分を記憶」認証がクリーンアップされることを意味します。ただし、構成された LogoutHandler 実装は、Spring Security 構成によって異なります。HttpServletRequest.logout() が呼び出された後も、レスポンスの書き込みを担当していることに注意することが重要です。通常、これにはようこそページへのリダイレクトが含まれます。
資格情報を新しいスレッドに確実に伝達する AsynchContext.start(Runnable): Oracle (英語) メソッド。Spring Security の同時実行サポートを使用して、Spring Security は AsyncContext.start(Runnable)をオーバーライドして、Runnable の処理時に現在の SecurityContext が使用されるようにします。例: 以下は、現在のユーザーの認証を出力します:
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 e) { throw new RuntimeException(e); } } });
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 が自動的に保存されました。これにより、非同期環境で問題が発生する可能性があります。例: 以下を検討してください。
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 e) { e.printStackTrace(); } } }.start();
課題は、このスレッドが Spring Security に認識されていないため、SecurityContext がこのスレッドに伝播されないことです。これは、HttpServletResponse をコミットするときに SecuriytContext がないことを意味します。Spring Security が HttpServletResponse のコミット時に SecurityContext を自動的に保存すると、ログインしているユーザーが失われます。
バージョン 3.2 以降、Spring Security は HttpServletRequest.startAsync() が呼び出された直後に HttpServletResponse をコミットする際に SecurityContext を自動的に保存しなくなるほどスマートです。
次のセクションでは、Spring Security が統合される Servlet 3.1 メソッドについて説明します。
HttpServletRequest.changeSessionId() (Javadoc) は、Servlet 3.1 以降でのセッション固定攻撃から保護するためのデフォルトの方法です。
Spring Security は、Spring Data 統合を提供し、クエリ内で現在のユーザーを参照できるようにします。結果のフィルタリングはスケーリングされないため、ページ化された結果をサポートするためにクエリにユーザーを含める必要があるだけでなく、必要です。
このサポートを使用するには、 org.springframework.security:spring-security-data
依存関係を追加し、型 SecurityEvaluationContextExtension
の Bean を提供します。Java 構成では、これは次のようになります。
@Bean public SecurityEvaluationContextExtension securityEvaluationContextExtension() { return new SecurityEvaluationContextExtension(); }
XML 構成では、これは次のようになります。
<bean class="org.springframework.security.data.repository.query.SecurityEvaluationContextExtension"/>
これで、Spring Security をクエリ内で使用できます。例:
@Repository public interface MessageRepository extends PagingAndSortingRepository<Message,Long> { @Query("select m from Message m where m.to.id = ?#{ principal?.id }") Page<Message> findInbox(Pageable pageable); }
これにより、 Authentication.getPrincipal().getId()
が Message
の受信者と等しいかどうかが確認されます。この例では、プリンシパルが id プロパティを持つオブジェクトにカスタマイズされていることを前提としていることに注意してください。 SecurityEvaluationContextExtension
Bean を公開することにより、すべての一般的なセキュリティ表現がクエリ内で利用可能になります。
ほとんどの環境では、セキュリティは Thread
ごとに保存されます。これは、新しい Thread
で作業が行われると、 SecurityContext
が失われることを意味します。Spring Security は、ユーザーがこれをはるかに簡単にするためのインフラストラクチャを提供します。Spring Security は、マルチスレッド環境で Spring Security を操作するための低レベルの抽象化を提供します。実際、これが Spring Security が「AsyncContext.start(Runnable)」およびセクション 15.6.4: “Spring MVC 非同期統合 ” との統合に基づいて構築しているものです。
Spring Security の同時実行性サポートの中で最も基本的な構成要素の 1 つは DelegatingSecurityContextRunnable
です。デリゲート用に指定された SecurityContext
で SecurityContextHolder
を初期化するために、デリゲート Runnable
をラップします。その後、 SecurityContextHolder
をクリアすることを保証する Runnable デリゲートを呼び出します。 DelegatingSecurityContextRunnable
は次のようになります。
public void run() { try { SecurityContextHolder.setContext(securityContext); delegate.run(); } finally { SecurityContextHolder.clearContext(); } }
非常にシンプルですが、SecurityContext をあるスレッドから別のスレッドにシームレスに転送します。ほとんどの場合、SecurityContextHolder はスレッドごとに動作するため、これは重要です。例: Spring Security の「<global-method-security>」サポート と呼ばれるセクションを使用して、サービスの 1 つを保護した可能性があります。これで、現在の Thread
の SecurityContext
を、保護されたサービスを呼び出す Thread
に簡単に転送できます。これを行う方法の例を以下に示します。
Runnable originalRunnable = new Runnable() { public void run() { // invoke secured service } }; SecurityContext context = SecurityContextHolder.getContext(); DelegatingSecurityContextRunnable wrappedRunnable = new DelegatingSecurityContextRunnable(originalRunnable, context); new Thread(wrappedRunnable).start();
上記のコードは次の手順を実行します。
Runnable
を作成します。Spring Security を認識していないことに注意してください SecurityContext
を SecurityContextHolder
から取得し、 DelegatingSecurityContextRunnable
を初期化する DelegatingSecurityContextRunnable
を使用してスレッドを作成する SecurityContextHolder
から SecurityContext
で DelegatingSecurityContextRunnable
を作成することは非常に一般的であるため、ショートカットコンストラクターがあります。次のコードは上記のコードと同じです。
Runnable originalRunnable = new Runnable() { public void run() { // invoke secured service } }; DelegatingSecurityContextRunnable wrappedRunnable = new DelegatingSecurityContextRunnable(originalRunnable); new Thread(wrappedRunnable).start();
このコードは簡単に使えますが、Spring Security を使っているという知識が必要です。次のセクションでは、Spring Security を使っているという事実を隠すために DelegatingSecurityContextExecutor
をどのように利用できるかを見ていきます。
前のセクションで、 DelegatingSecurityContextRunnable
を使用するのは簡単であることがわかりましたが、Spring Security を使用するためには Spring Security に注意する必要があるため、理想的ではありませんでした。 DelegatingSecurityContextExecutor
を使用して、Spring Security を使用しているという知識からコードを保護する方法を見てみましょう。
DelegatingSecurityContextExecutor
の設計は、代理 Runnable
の代わりに代理 Executor
を受け入れることを除いて、 DelegatingSecurityContextRunnable
の設計と非常に似ています。以下に使用方法の例を示します。
SecurityContext context = SecurityContextHolder.createEmptyContext(); Authentication authentication = new UsernamePasswordAuthenticationToken("user","doesnotmatter", AuthorityUtils.createAuthorityList("ROLE_USER")); context.setAuthentication(authentication); SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor(); DelegatingSecurityContextExecutor executor = new DelegatingSecurityContextExecutor(delegateExecutor, context); Runnable originalRunnable = new Runnable() { public void run() { // invoke secured service } }; executor.execute(originalRunnable);
コードは次の手順を実行します。
DelegatingSecurityContextExecutor
に使用する SecurityContext
を作成します。この例では、 SecurityContext
を手動で作成するだけです。ただし、 SecurityContext
をどこでどのように取得するかは重要ではありません(つまり、必要に応じて SecurityContextHolder
から取得できます)。 Runnable
の実行を担当する delegateExecutor を作成する DelegatingSecurityContextRunnable
で execute メソッドに渡される Runnable をラップする DelegatingSecurityContextExecutor
を作成します。次に、ラップされた Runnable を delegateExecutor に渡します。この例では、 DelegatingSecurityContextExecutor
に送信されるすべての Runnable に同じ SecurityContext
が使用されます。これは、昇格した特権を持つユーザーが実行する必要があるバックグラウンドタスクを実行している場合に便利です。 SecurityContext
と DelegatingSecurityContextExecutor
を作成する代わりに、すでに初期化された DelegatingSecurityContextExecutor
のインスタンスを注入できます。@Autowired private Executor executor; // becomes an instance of our DelegatingSecurityContextExecutor public void submitRunnable() { Runnable originalRunnable = new Runnable() { public void run() { // invoke secured service } }; executor.execute(originalRunnable); }
これで、コードは SecurityContext
が Thread
に伝搬されていることを認識せず、次に originalRunnable
が実行され、次に SecurityContextHolder
がクリアされます。この例では、同じユーザーが各スレッドの実行に使用されています。 originalRunnable
を処理するために executor.execute(Runnable)
(つまり、現在ログインしているユーザー)を呼び出したときに SecurityContextHolder
のユーザーを使用したい場合はどうなるでしょうか? これを行うには、 DelegatingSecurityContextExecutor
コンストラクターから SecurityContext
引数を削除します。例:
SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor(); DelegatingSecurityContextExecutor executor = new DelegatingSecurityContextExecutor(delegateExecutor);
executor.execute(Runnable)
が実行されると、 SecurityContext
が最初に SecurityContextHolder
によって取得され、次に SecurityContext
を使用して DelegatingSecurityContextRunnable
が作成されます。これは、 executor.execute(Runnable)
コードの呼び出しに使用されたのと同じユーザーで Runnable
を実行していることを意味します。
Java コンカレント API と Spring タスク抽象化の両方との追加統合については、Javadoc を参照してください。前のコードを理解すると、それらは非常に自明です。
Spring Security は、Spring Security 関連クラスを永続化するための Jackson サポートを追加しました。これにより、分散セッション(つまり、セッションレプリケーション、Spring Session など)で作業する際に Spring Security 関連クラスを直列化するパフォーマンスを向上させることができます。
使用するには、 SecurityJackson2Modules.getModules(ClassLoader)
を Jackson モジュール (英語) として登録します。
ObjectMapper mapper = new ObjectMapper(); ClassLoader loader = getClass().getClassLoader(); List<Module> modules = SecurityJackson2Modules.getModules(loader); mapper.registerModules(modules); // ... use ObjectMapper as normally ... SecurityContext context = new SecurityContextImpl(); // ... String json = mapper.writeValueAsString(context);
Spring Security は、エンドユーザーに表示される可能性が高い例外メッセージのローカライズをサポートしています。アプリケーションが英語を話すユーザー向けに設計されている場合、デフォルトではすべてのセキュリティメッセージは英語であるため、何もする必要はありません。他のロケールをサポートする必要がある場合、知る必要があるすべてがこのセクションに含まれています。
認証の失敗や拒否されたアクセスに関連するメッセージ(認可の失敗)など、すべての例外メッセージをローカライズできます。開発者またはシステムデプロイヤに焦点を当てた例外とログメッセージ(不適切な属性、インターフェース契約違反、不適切なコンストラクターの使用、起動時間検証、デバッグレベルのログ記録を含む)はローカライズされず、代わりに Spring Security のコード内に英語でハードコードされています
spring-security-core-xx.jar
に同梱されている org.springframework.security
パッケージには、 messages.properties
ファイルと、いくつかの共通言語のローカライズバージョンが含まれています。Spring Security クラスは Spring の MessageSourceAware
インターフェースを実装し、アプリケーションコンテキストの起動時にメッセージリゾルバーが依存性注入されることを想定しているため、これは ApplicationContext
によって参照される必要があります。通常、アプリケーションコンテキスト内で Bean を登録して、メッセージを参照するだけです。以下に例を示します。
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basename" value="classpath:org/springframework/security/messages"/> </bean>
messages.properties
は、標準のリソースバンドルに従って名前が付けられ、Spring Security メッセージでサポートされるデフォルト言語を表します。このデフォルトファイルは英語です。
messages.properties
ファイルをカスタマイズする場合、または他の言語をサポートする場合は、ファイルをコピーし、それに応じて名前を変更し、上記の Bean 定義内に登録する必要があります。このファイル内には多数のメッセージキーがないため、ローカライズは主要なイニシアチブと見なされるべきではありません。このファイルのローカライズを実行する場合は、JIRA タスクをログに記録し、適切な名前のローカライズされたバージョンの messages.properties
を添付して、コミュニティと作業を共有することを検討してください。
Spring Security は、実際に適切なメッセージを検索するために、Spring のローカライゼーションサポートに依存しています。これが機能するためには、受信リクエストのロケールが Spring の org.springframework.context.i18n.LocaleContextHolder
に保存されていることを確認する必要があります。 Spring MVC の DispatcherServlet
はアプリケーションに対してこれを自動的に行いますが、Spring Security のフィルターはこの前に呼び出されるため、 LocaleContextHolder
はフィルターが呼び出される前に正しい Locale
を含むように設定する必要があります。これを自分でフィルターで行うことができます( web.xml
で Spring Security フィルターの前に来る必要があります)か、Spring の RequestContextFilter
を使用できます。Spring でのローカライズの使用の詳細については、 Spring Framework のドキュメントを参照してください。
「contacts」サンプルアプリケーションは、ローカライズされたメッセージを使用するように設定されています。
Spring Security は、Spring MVC とのオプションの統合をいくつか提供します。このセクションでは、統合についてさらに詳しく説明します。
![]() | メモ |
---|---|
Spring Security 4.0 以降、 |
Spring Security と Spring MVC の統合を有効にするには、 @EnableWebSecurity
アノテーションを構成に追加します。
![]() | メモ |
---|---|
Spring Security は、Spring MVC の WebMvcConfigurer を使用した構成を提供します。つまり、 |
Spring Security は、 MvcRequestMatcher
を使用した URL で Spring MVC がどのように一致するかを深く統合します。これは、セキュリティルールがリクエストの処理に使用されるロジックと一致することを確認できます。
MvcRequestMatcher
を使用するには、Spring Security 構成を DispatcherServlet
と同じ ApplicationContext
に配置する必要があります。Spring Security の MvcRequestMatcher
は、 mvcHandlerMappingIntrospector
という名前の HandlerMappingIntrospector
Bean が、マッチングの実行に使用される Spring MVC 構成によって登録されることを想定しているため、これが必要です。
web.xml
の場合、これは設定を DispatcherServlet.xml
に配置する必要があることを意味します。
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- All Spring Configuration (both MVC and Security) are in /WEB-INF/spring/ --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/*.xml</param-value> </context-param> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- Load from the ContextLoaderListener --> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
DispatcherServlet
の ApplicationContext
に配置された WebSecurityConfiguration
の下。
public class SecurityInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return null; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[] { RootConfiguration.class, WebMvcConfiguration.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
![]() | メモ |
---|---|
|
次のようにマップされているコントローラーを考えます。
@RequestMapping("/admin") public String admin() {
このコントローラーメソッドへのアクセスを管理ユーザーに制限したい場合、開発者は HttpServletRequest
で次と照合することで認可ルールを提供できます。
protected configure(HttpSecurity http) throws Exception { http .authorizeRequests(authorizeRequests -> authorizeRequests .antMatchers("/admin").hasRole("ADMIN") ); }
または XML
<http> <intercept-url pattern="/admin" access="hasRole('ADMIN')"/> </http>
どちらの構成でも、URL /admin
では、認証されたユーザーが管理ユーザーである必要があります。ただし、Spring MVC 構成によっては、URL /admin.html
も admin()
メソッドにマップされます。さらに、Spring MVC 構成に応じて、URL /admin/
も admin()
メソッドにマップされます。
問題は、セキュリティルールが /admin
のみを保護していることです。Spring MVC のすべての順列に追加のルールを追加できますが、これは非常に冗長で面倒です。
代わりに、Spring Security の MvcRequestMatcher
を活用できます。次の構成は、Spring MVC を使用して URL で照合することにより、Spring MVC が照合する URL と同じ URL を保護します。
protected configure(HttpSecurity http) throws Exception { http .authorizeRequests(authorizeRequests -> authorizeRequests .mvcMatchers("/admin").hasRole("ADMIN") ); }
または XML
<http request-matcher="mvc"> <intercept-url pattern="/admin" access="hasRole('ADMIN')"/> </http>
Spring Security は、Spring MVC 引数の現在の Authentication.getPrincipal()
を自動的に解決できる AuthenticationPrincipalArgumentResolver
を提供します。 @EnableWebSecurity
を使用すると、Spring MVC 構成にこれが自動的に追加されます。XML ベースの構成を使用する場合は、これを自分で追加する必要があります。例:
<mvc:annotation-driven> <mvc:argument-resolvers> <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" /> </mvc:argument-resolvers> </mvc:annotation-driven>
AuthenticationPrincipalArgumentResolver
が適切に構成されたら、Spring MVC レイヤーで Spring Security から完全に切り離すことができます。
UserDetails
を実装する Object
を返すカスタム UserDetailsService
と独自の CustomUser
Object
がある状況を考えてみます。現在認証されているユーザーの CustomUser
には、次のコードを使用してアクセスできます。
@RequestMapping("/messages/inbox") public ModelAndView findMessagesForUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); CustomUser custom = (CustomUser) authentication == null ? null : authentication.getPrincipal(); // .. find messages for this user and return them ... }
Spring Security 3.2 以降、アノテーションを追加することで、引数をより直接解決できます。例:
import org.springframework.security.core.annotation.AuthenticationPrincipal; // ... @RequestMapping("/messages/inbox") public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) { // .. find messages for this user and return them ... }
場合によっては、何らかの方法でプリンシパルを変換する必要があります。例: CustomUser
をファイナルにする必要がある場合、拡張できませんでした。この状況では、 UserDetailsService
は UserDetails
を実装する Object
を返し、 CustomUser
にアクセスするために getCustomUser
という名前のメソッドを提供します。例: 次のようになります。
public class CustomUserUserDetails extends User { // ... public CustomUser getCustomUser() { return customUser; } }
次に、 Authentication.getPrincipal()
をルートオブジェクトとして使用する SpEL 式を使用して、 CustomUser
にアクセスできます。
import org.springframework.security.core.annotation.AuthenticationPrincipal; // ... @RequestMapping("/messages/inbox") public ModelAndView findMessagesForUser(@AuthenticationPrincipal(expression = "customUser") CustomUser customUser) { // .. find messags for this user and return them ... }
SpEL 式で Bean を参照することもできます。例: JPA を使用してユーザーを管理しており、現在のユーザーのプロパティを変更して保存する場合は、以下を使用できます。
import org.springframework.security.core.annotation.AuthenticationPrincipal; // ... @PutMapping("/users/self") public ModelAndView updateName(@AuthenticationPrincipal(expression = "@jpaEntityManager.merge(#this)") CustomUser attachedCustomUser, @RequestParam String firstName) { // change the firstName on an attached instance which will be persisted to the database attachedCustomUser.setFirstName(firstName); // ... }
@AuthenticationPrincipal
を独自のアノテーションのメタアノテーションにすることで、Spring Security への依存をさらに削除できます。以下に、 @CurrentUser
という名前のアノテーションでこれを行う方法を示します。
![]() | メモ |
---|---|
Spring Security への依存を削除するために、 |
@Target({ElementType.PARAMETER, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @AuthenticationPrincipal public @interface CurrentUser {}
@CurrentUser
が指定されたため、それを使用して、現在認証されているユーザーの CustomUser
を解決するためのシグナルを送ることができます。また、Spring Security への依存関係を単一のファイルに分離しました。
@RequestMapping("/messages/inbox") public ModelAndView findMessagesForUser(@CurrentUser CustomUser customUser) { // .. find messages for this user and return them ... }
Spring Web MVC 3.2 + は、非同期リクエスト処理 (英語) に優れたサポートを提供します。追加の構成を行わなくても、Spring Security は自動的に SecurityContext
を Thread
にセットアップし、コントローラーから返される Callable
を実行します。例: 次のメソッドでは、 Callable
が作成されたときに使用可能であった SecurityContext
で Callable
が自動的に実行されます。
@RequestMapping(method=RequestMethod.POST) public Callable<String> processUpload(final MultipartFile file) { return new Callable<String>() { public Object call() throws Exception { // ... return "someView"; } }; }
![]() | SecurityContext と Callable の関連付け |
---|---|
技術的に言えば、Spring Security は |
コントローラーによって返される DeferredResult
との自動統合はありません。これは、 DeferredResult
がユーザーによって処理され、自動的に統合する方法がないためです。ただし、並行性サポートを使用して、Spring Security との透過的な統合を提供できます。
Spring Security は、Spring MVC フォームタグ (英語) を使用するフォーム内に CSRF トークンを自動的 に 組み込みます。例: 次の JSP:
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:form="http://www.springframework.org/tags/form" version="2.0"> <jsp:directive.page language="java" contentType="text/html" /> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <!-- ... --> <c:url var="logoutUrl" value="/logout"/> <form:form action="${logoutUrl}" method="post"> <input type="submit" value="Log out" /> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> </form:form> <!-- ... --> </html> </jsp:root>
次のような HTML を出力します。
<!-- ... --> <form action="/context/logout" method="post"> <input type="submit" value="Log out"/> <input type="hidden" name="_csrf" value="f81d4fae-7dec-11d0-a765-00a0c91e6bf6"/> </form> <!-- ... -->
Spring Security は、Spring MVC 引数の現在の CsrfToken
を自動的に解決できる CsrfTokenArgumentResolver
を提供します。 @EnableWebSecurity を使用することにより、Spring MVC 構成にこれが自動的に追加されます。XML ベースの構成を使用する場合は、これを自分で追加する必要があります。
CsrfTokenArgumentResolver
が適切に構成されたら、 CsrfToken
を静的な HTML ベースのアプリケーションに公開できます。
@RestController public class CsrfController { @RequestMapping("/csrf") public CsrfToken csrf(CsrfToken token) { return token; } }
CsrfToken
を他のドメインからシークレットにしておくことが重要です。つまり、クロスオリジン共有 (CORS) を使用している場合、 CsrfToken
を外部ドメインに公開しないでください。
Spring Security 4 は、Spring の WebSocket サポートを保護するためのサポートを追加しました。このセクションでは、Spring Security の WebSocket サポートの使用方法について説明します。
![]() | メモ |
---|---|
https://github.com/spring-projects/spring-session/tree/master/samples/boot/websocket (英語) で WebSocket セキュリティの完全な実用サンプルを見つけることができます。 |
Spring Security 4.0 は、Spring メッセージング抽象化による WebSockets の認可サポートを導入しました。Java 構成を使用して認証を設定するには、 AbstractSecurityWebSocketMessageBrokerConfigurer
を継承して MessageSecurityMetadataSourceRegistry
を設定するだけです。例:
@Configuration public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {![]()
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) { messages .simpDestMatchers("/user/**").authenticated()
} }
これにより、次のことが保証されます。
受信 CONNECT メッセージには、同一生成元ポリシーを実施するための有効な CSRF トークンが必要です | |
SecurityContextHolder には、受信リクエストの simpUser ヘッダー属性内のユーザーが入力されます。 | |
メッセージには適切な認可が必要です。具体的には、「/user/」で始まる受信メッセージには ROLE_USER が必要です。認可の詳細については、セクション 15.7.3: “WebSocket 認証 ” を参照してください。 |
Spring Security は、WebSockets を保護するための XML 名前空間サポートも提供します。比較可能な XML ベースの構成は次のようになります。
<websocket-message-broker>![]()
![]()
<intercept-message pattern="/user/**" access="hasRole('USER')" /> </websocket-message-broker>
これにより、次のことが保証されます。
受信 CONNECT メッセージには、同一生成元ポリシーを実施するための有効な CSRF トークンが必要です | |
SecurityContextHolder には、受信リクエストの simpUser ヘッダー属性内のユーザーが入力されます。 | |
メッセージには適切な認可が必要です。具体的には、「/user/」で始まる受信メッセージには ROLE_USER が必要です。認可の詳細については、セクション 15.7.3: “WebSocket 認証 ” を参照してください。 |
WebSockets は、WebSocket 接続が確立されたときに HTTP リクエストで見つかった同じ認証情報を再利用します。これは、 HttpServletRequest
上の Principal
が WebSockets に引き渡されることを意味します。Spring Security を使用している場合、 HttpServletRequest
の Principal
は自動的にオーバーライドされます。
より具体的には、ユーザーが WebSocket アプリケーションに対して認証されたことを確認するには、HTTP ベースの Web アプリケーションを認証するように Spring Security をセットアップすることだけが必要です。
Spring Security 4.0 は、Spring メッセージング抽象化による WebSockets の認可サポートを導入しました。Java 構成を使用して認証を設定するには、 AbstractSecurityWebSocketMessageBrokerConfigurer
を継承して MessageSecurityMetadataSourceRegistry
を設定するだけです。例:
@Configuration public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer { @Override protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) { messages .nullDestMatcher().authenticated().simpSubscribeDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/app/**").hasRole("USER")
.simpSubscribeDestMatchers("/user/**", "/topic/friends/*").hasRole("USER")
.simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll()
.anyMessage().denyAll();
} }
これにより、次のことが保証されます。
宛先のないメッセージ(つまり、メッセージタイプが MESSAGE または SUBSCRIBE 以外のもの)では、ユーザーの認証が必要になります | |
誰でも /user/queue/errors にサブスクライブできます | |
「/app/」で始まる宛先を持つすべてのメッセージには、ロール ROLE_USER が必要です。 | |
SUBSCRIBE タイプの「/user/」または「/topic/friends/」で始まるメッセージには、ROLE_USER が必要です | |
タイプ MESSAGE または SUBSCRIBE の他のメッセージは拒否されます。6 のため、この手順は必要ありませんが、特定のメッセージタイプでどのように一致するかを示しています。 | |
その他のメッセージは拒否されます。これは、メッセージを見逃さないようにするための良いアイデアです。 |
Spring Security は、WebSockets を保護するための XML 名前空間サポートも提供します。比較可能な XML ベースの構成は次のようになります。
<websocket-message-broker><intercept-message type="CONNECT" access="permitAll" /> <intercept-message type="UNSUBSCRIBE" access="permitAll" /> <intercept-message type="DISCONNECT" access="permitAll" /> <intercept-message pattern="/user/queue/errors" type="SUBSCRIBE" access="permitAll" />
<intercept-message pattern="/app/**" access="hasRole('USER')" />
![]()
<intercept-message pattern="/user/**" access="hasRole('USER')" /> <intercept-message pattern="/topic/friends/*" access="hasRole('USER')" />
<intercept-message type="MESSAGE" access="denyAll" /> <intercept-message type="SUBSCRIBE" access="denyAll" /> <intercept-message pattern="/**" access="denyAll" />
</websocket-message-broker>
これにより、次のことが保証されます。
タイプ CONNECT、UNSUBSCRIBE、または DISCONNECT のメッセージでは、ユーザーの認証が必要です。 | |
誰でも /user/queue/errors にサブスクライブできます | |
「/app/」で始まる宛先を持つすべてのメッセージには、ロール ROLE_USER が必要です。 | |
SUBSCRIBE タイプの「/user/」または「/topic/friends/」で始まるメッセージには、ROLE_USER が必要です | |
タイプ MESSAGE または SUBSCRIBE の他のメッセージは拒否されます。6 のため、この手順は必要ありませんが、特定のメッセージタイプでどのように一致するかを示しています。 | |
宛先を持つ他のメッセージは拒否されます。これは、メッセージを見逃さないようにするための良いアイデアです。 |
アプリケーションを適切に保護するには、Spring の WebSocket サポートを理解することが重要です。
メッセージの SUBSCRIBE タイプと MESSAGE タイプの違いと、Spring 内での動作方法を理解することが重要です。
チャットアプリケーションを検討してください。
クライアントが「/topic/system/notifications」にサブスクライブできるようにしたいのですが、その宛先に MESSAGE を送信できるようにしたくありません。「/topic/system/notifications」への MESSAGE の送信を許可した場合、クライアントはそのエンドポイントにメッセージを直接送信し、システムになりすますことができます。
一般的に、アプリケーションでは、ブローカープレフィックス(「/topic/」または「/queue/」)で始まる宛先に送信される MESSAGE を拒否するのが一般的です。
宛先がどのように変換されるかを理解することも重要です。
チャットアプリケーションを検討してください。
SimpMessageSendingOperations.convertAndSendToUser("toUser", "/queue/messages", message)
を使用して受信者にメッセージを送信します。上記のアプリケーションでは、クライアントが「/queue/user/messages-<sessionid>」に変換される「/user/queue」をリッスンできるようにしたいと考えています。ただし、クライアントが「/queue/*」をリッスンできるようにしたくないため、クライアントはすべてのユーザーのメッセージを見ることができます。
一般に、ブローカープレフィックス(「/topic/」または「/queue/」)で始まるメッセージに送信された SUBSCRIBE をアプリケーションが拒否するのが一般的です。もちろん、例外を提供
Spring には、メッセージがシステムをどのように流れるかを説明する Flow のメッセージというタイトルのセクションが含まれています。Spring Security は clientInboundChannel
のみを保護することに注意することが重要です。Spring Security は clientOutboundChannel
を保護しようとしません。
これの最も重要な理由はパフォーマンスです。受信するすべてのメッセージには、通常、さらに多くのメッセージが送信されます。発信メッセージを保護する代わりに、エンドポイントへのサブスクリプションを保護することをお勧めします。
ブラウザーが WebSocket 接続に対して同一生成元ポリシー (英語) を強制しないことを強調することが重要です。これは非常に重要な考慮事項です。
次のシナリオを検討してください。ユーザーが bank.com にアクセスして、アカウントの認証を行います。同じユーザーがブラウザーで別のタブを開き、evil.com にアクセスします。Same Origin Policy は、evil.com が bank.com に対してデータを読み書きできないようにします。
WebSockets では、同一生成元ポリシーは適用されません。実際、bank.com が明示的に禁止しない限り、evil.com はユーザーに代わってデータの読み取りと書き込みを行うことができます。つまり、ユーザーが webSocket でできること(つまり、送金)はすべて、evil.com がそのユーザーに代わって行うことができます。
SockJS は WebSockets をエミュレートしようとするため、Same Origin Policy もバイパスします。これは、SockJS を使用する場合、開発者がアプリケーションを外部ドメインから明示的に保護する必要があることを意味します。
幸い、Spring 4.1.5 Spring の WebSocket および SockJS のサポートにより、現在のドメインへのアクセスが制限されてい ます。Spring Security が提供する追加の保護レイヤー追加多層防御 (英語) を。
デフォルトでは、Spring Security は、すべての CONNECT メッセージタイプで CSRF トークンを必要とします。これにより、CSRF トークンにアクセスできるサイトのみが接続できるようになります。 同じ起源のみが CSRF トークンにアクセスできるため、外部ドメインは接続を確立できません。
通常、CSRF トークンを HTTP ヘッダーまたは HTTP パラメーターに含める必要があります。ただし、SockJS はこれらのオプションを許可しません。代わりに、トークンを Stomp ヘッダーに含める必要があります
アプリケーションは 、 _csrf という名前のリクエスト属性にアクセスすること により、CSRF トークンを取得できます。例: 以下は、JSP の CsrfToken
へのアクセスを許可します。
var headerName = "${_csrf.headerName}"; var token = "${_csrf.token}";
静的 HTML を使用している場合、REST エンドポイントで CsrfToken
を公開できます。例: 以下は、URL /csrf で CsrfToken
を公開します
@RestController public class CsrfController { @RequestMapping("/csrf") public CsrfToken csrf(CsrfToken token) { return token; } }
JavaScript は、エンドポイントに対して REST 呼び出しを行い、レスポンスを使用して headerName とトークンを設定できます。
これで、トークンを Stomp クライアントに含めることができます。例:
... var headers = {}; headers[headerName] = token; stompClient.connect(headers, function(frame) { ... }
SockJS は、古いブラウザーをサポートするフォールバックトランスポートを提供します。フォールバックオプションを使用する場合、SockJS が Spring Security と連携できるように、いくつかのセキュリティ制約を緩和する必要があります。
SockJS は 、iframe を活用: GitHub (英語) するトランスポート: GitHub (英語) を使用する場合があります。デフォルトでは、Spring Security はクリックジャック攻撃を防ぐためにサイトのフレーム化を拒否します。SockJS フレームベースのトランスポートを機能させるには、Spring Security を構成して、同じオリジンがコンテンツをフレーム化できるようにする必要があります。
frame-options 要素を使用して X-Frame-Options をカスタマイズでき ます。例: 以下は、Spring Security に同じドメイン内の iframe を許可する「X-Frame-Options:SAMEORIGIN」を使用するよう指示します。
<http> <!-- ... --> <headers> <frame-options policy="SAMEORIGIN" /> </headers> </http>
同様に、フレームオプションをカスタマイズして、次を使用して Java 構成内で同じオリジンを使用できます。
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .frameOptions(frameOptions -> frameOptions .sameOrigin() ) ); } }
SockJS は、HTTP ベースのトランスポートの CONNECT メッセージで POST を使用します。通常、CSRF トークンを HTTP ヘッダーまたは HTTP パラメーターに含める必要があります。ただし、SockJS はこれらのオプションを許可しません。代わりに、「CSRF を Stomp ヘッダーに追加する」で説明され ているように、トークンを Stomp ヘッダーに含める必要があります。
また、Web レイヤーで CSRF 保護を緩和する必要があることも意味します。具体的には、接続 URL の CSRF 保護を無効にします。すべての URL の CSRF 保護を無効にする必要はありません。そうしないと、サイトは CSRF 攻撃に対して脆弱になります。
CSRF RequestMatcher を提供することにより、これを簡単に実現できます。Java 構成により、これは非常に簡単になります。例: ストンプエンドポイントが「/chat」の場合、次の構成を使用して「/chat/」で始まる URL のみの CSRF 保護を無効にできます。
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf // ignore our stomp endpoints since they are protected using Stomp headers .ignoringAntMatchers("/chat/**") ) .headers(headers -> headers // allow same origin to frame our site to support iframe SockJS .frameOptions(frameOptions -> frameOptions .sameOrigin() ) ) .authorizeRequests(authorizeRequests -> ... ) ...
XML ベースの構成を使用している場合、csr [ メール保護 ] を使用できます。例:
<http ...> <csrf request-matcher-ref="csrfMatcher"/> <headers> <frame-options policy="SAMEORIGIN"/> </headers> ... </http> <b:bean id="csrfMatcher" class="AndRequestMatcher"> <b:constructor-arg value="#{T(org.springframework.security.web.csrf.CsrfFilter).DEFAULT_CSRF_MATCHER}"/> <b:constructor-arg> <b:bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher"> <b:bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher"> <b:constructor-arg value="/chat/**"/> </b:bean> </b:bean> </b:constructor-arg> </b:bean>
Spring Framework は 、CORS のファーストクラスサポートを提供します。プリフライトリクエストには Cookie(つまり JSESSIONID
)が含まれないため、CORS は Spring Security の前に処理する必要があります。リクエストに Cookie が含まれておらず、Spring Security が最初の場合、リクエストは(リクエストに Cookie がないため)ユーザーが認証されていないと判断し、拒否します。
CORS が最初に処理されるようにする最も簡単な方法は、 CorsFilter
を使用することです。ユーザーは、以下を使用して CorsConfigurationSource
を提供することにより、 CorsFilter
と Spring Security を統合できます。
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http // by default uses a Bean by the name of corsConfigurationSource .cors(withDefaults()) ... } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("https://example.com")); configuration.setAllowedMethods(Arrays.asList("GET","POST")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
または XML
<http> <cors configuration-source-ref="corsSource"/> ... </http> <b:bean id="corsSource" class="org.springframework.web.cors.UrlBasedCorsConfigurationSource"> ... </b:bean>
Spring MVC の CORS サポートを使用している場合、 CorsConfigurationSource
の指定を省略することができ、Spring Security は Spring MVC に提供された CORS 構成を活用します。
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http // if Spring MVC is on classpath and no CorsConfigurationSource is provided, // Spring Security will use CORS configuration provided to Spring MVC .cors(withDefaults()) ... } }
または XML
<http> <!-- Default to Spring MVC's CORS configuration --> <cors /> ... </http>
Spring Security には独自の taglib があり、JSP でセキュリティ情報にアクセスし、セキュリティ制約を適用するための基本的なサポートを提供します。
タグを使用するには、JSP でセキュリティ taglib を宣言する必要があります。
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
このタグは、その内容を評価する必要があるかどうかを決定するために使用されます。Spring Security 3.0 では、[10] の 2 つの方法で使用できます。最初のアプローチでは、タグの access
属性で指定された Web セキュリティ式を使用し ます。式の評価は、アプリケーションコンテキストで定義された SecurityExpressionHandler<FilterInvocation>
に委譲されます(このサービスが利用可能であることを確認するには、 <http>
名前空間構成で Web 式を有効にする必要があります)。たとえば
<sec:authorize access="hasRole('supervisor')"> This content will only be visible to users who have the "supervisor" authority in their list of <tt>GrantedAuthority</tt>s. </sec:authorize>
Spring Security の PermissionEvaluator と組み合わせて使用すると、タグを使用して権限を確認することもできます。例:
<sec:authorize access="hasPermission(#domain,'read') or hasPermission(#domain,'write')"> This content will only be visible to users who have read or write permission to the Object found as a request attribute named "domain". </sec:authorize>
一般的な要件は、ユーザーが実際にクリックすることを許可されている場合にのみ特定のリンクを表示することです。何かが許可されるかどうかを事前に判断するにはどうすればよいですか? このタグは、特定の URL を属性として定義できる代替モードで動作することもできます。ユーザーがその URL の呼び出しを許可されている場合、タグの本文が評価され、そうでない場合はスキップされます。次のようなものを持っているかもしれません
<sec:authorize url="/admin"> This content will only be visible to users who are authorized to send requests to the "/admin" URL. </sec:authorize>
このタグを使用するには、アプリケーションコンテキストに WebInvocationPrivilegeEvaluator
のインスタンスも存在する必要があります。名前空間を使用している場合、名前空間は自動的に登録されます。これは DefaultWebInvocationPrivilegeEvaluator
のインスタンスで、指定された URL のダミー Web リクエストを作成し、セキュリティインターセプターを呼び出して、リクエストが成功するか失敗するかを確認します。これにより、 <http>
名前空間構成内の intercept-url
宣言を使用して定義したアクセス制御セットアップに委譲でき、JSP 内で情報(必要なロールなど)を複製する必要がなくなります。このアプローチは、より具体的な一致のために、HTTP メソッドを提供する method
属性と組み合わせることもできます。
タグの評価のブール結果(アクセスの許可または拒否)は、 var
属性を変数名に設定することにより、ページコンテキストスコープ変数に格納でき、他の場所で条件を複製および再評価する必要がありません。ページ。
認可されていないユーザーのためにページ内のリンクを非表示にしても、ユーザーが URL にアクセスすることを妨げません。たとえば、ブラウザーに直接入力するだけです。テストプロセスの一環として、バックエンドでリンクが本当に保護されていることを確認するために、非表示の領域を明らかにすることができます。システムプロパティ spring.security.disableUISecurity
を true
に設定すると、 authorize
タグは引き続き実行されますが、その内容は非表示になりません。デフォルトでは、 <span class="securityHiddenUI">…</span>
タグでコンテンツを囲みます。これにより、異なる背景色などの特定の CSS スタイルで「隠された」コンテンツを表示できます。たとえば、このプロパティを有効にして「チュートリアル」サンプルアプリケーションを実行してみてください。
デフォルトの span
タグから周囲のテキストを変更する場合(または空の文字列を使用して完全に削除する場合)、プロパティ spring.security.securedUIPrefix
および spring.security.securedUISuffix
を設定することもできます。
このタグは、セキュリティコンテキストに保存されている現在の Authentication
オブジェクトへのアクセスを許可します。オブジェクトのプロパティを JSP で直接レンダリングします。そのため、たとえば、 Authentication
の principal
プロパティが Spring Security の UserDetails
オブジェクトのインスタンスである場合、 <sec:authentication property="principal.username" />
を使用すると現在のユーザーの名前がレンダリングされます。
もちろん、この種のことのために JSP タグを使用する必要はなく、ビューの中にできるだけ少ないロジックを保持することを好む人もいます。MVC コントローラーの Authentication
オブジェクトにアクセスし( SecurityContextHolder.getContext().getAuthentication()
を呼び出すことにより)、データをモデルに直接追加して、ビューでレンダリングすることができます。
このタグは、Spring Security の ACL モジュールで使用する場合にのみ有効です。指定されたドメインオブジェクトに必要なアクセス許可のコンマ区切りリストをチェックします。現在のユーザーがこれらすべての権限を持っている場合、タグの本文が評価されます。そうでない場合はスキップされます。例は
![]() | 注意 |
---|---|
一般に、このタグは非推奨と見なされる必要があります。代わりにセクション 15.9.2: “ 承認タグ ” を使用してください。 |
<sec:accesscontrollist hasPermission="1,2" domainObject="${someObject}"> This will be shown if the user has all of the permissions represented by the values "1" or "2" on the given object. </sec:accesscontrollist>
許可は、アプリケーションコンテキストで定義された PermissionFactory
に渡され、ACL Permission
インスタンスに変換されます。そのため、ファクトリでサポートされている任意の形式を使用できます。整数である必要はなく、 READ
や WRITE
などの文字列でもかまいません。 PermissionFactory
が見つからない場合、 DefaultPermissionFactory
のインスタンスが使用されます。アプリケーションコンテキストからの AclService
は、提供されたオブジェクトの Acl
インスタンスをロードするために使用されます。 Acl
は、すべてが許可されているかどうかを確認するために必要な権限で呼び出されます。
このタグは、 authorize
タグと同様に、 var
属性もサポートします。
CSRF 保護が有効になっている場合、このタグは、CSRF 保護トークンの正しい名前と値を持つ非表示フォームフィールドを挿入します。CSRF 保護が有効になっていない場合、このタグは何も出力しません。
通常、Spring Security は、使用する <form:form>
タグに対して CSRF フォームフィールドを自動的に挿入しますが、何らかの理由で <form:form>
を使用できない場合、 csrfInput
は便利な代替です。
このタグは、通常は他の入力フィールドを配置する HTML <form></form>
ブロック内に配置する必要があります。このタグを Spring <form:form></form:form>
ブロック内に配置しないでください。Spring Security は Spring フォームを自動的に処理します。
<form method="post" action="/do/something"> <sec:csrfInput /> Name:<br /> <input type="text" name="name" /> ... </form>
CSRF 保護が有効になっている場合、このタグは、CSRF 保護トークンフォームフィールドとヘッダー名、および CSRF 保護トークン値を含むメタタグを挿入します。これらのメタタグは、アプリケーションの JavaScript 内で CSRF 保護を採用できます。
csrfMetaTags
は、通常は他のメタタグを配置する HTML <head></head>
ブロック内に配置する必要があります。このタグを使用すると、JavaScript を使用してフォームフィールド名、ヘッダー名、トークン値に簡単にアクセスできます。この例では、タスクを簡単にするために JQuery が使用されています。
<!DOCTYPE html> <html> <head> <title>CSRF Protected JavaScript Page</title> <meta name="description" content="This is the description for this page" /> <sec:csrfMetaTags /> <script type="text/javascript" language="javascript"> var csrfParameter = $("meta[name='_csrf_parameter']").attr("content"); var csrfHeader = $("meta[name='_csrf_header']").attr("content"); var csrfToken = $("meta[name='_csrf']").attr("content"); // using XMLHttpRequest directly to send an x-www-form-urlencoded request var ajax = new XMLHttpRequest(); ajax.open("POST", "https://www.example.org/do/something", true); ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded data"); ajax.send(csrfParameter + "=" + csrfToken + "&name=John&..."); // using XMLHttpRequest directly to send a non-x-www-form-urlencoded request var ajax = new XMLHttpRequest(); ajax.open("POST", "https://www.example.org/do/something", true); ajax.setRequestHeader(csrfHeader, csrfToken); ajax.send("..."); // using JQuery to send an x-www-form-urlencoded request var data = {}; data[csrfParameter] = csrfToken; data["name"] = "John"; ... $.ajax({ url: "https://www.example.org/do/something", type: "POST", data: data, ... }); // using JQuery to send a non-x-www-form-urlencoded request var headers = {}; headers[csrfHeader] = csrfToken; $.ajax({ url: "https://www.example.org/do/something", type: "POST", headers: headers, ... }); <script> </head> <body> ... </body> </html>
CSRF 保護が有効になっていない場合、 csrfMetaTags
は何も出力しません。