Spring MVCアプリケーションでSpring Security 3.2.3を使用していますが、予期しない動作が発生します。
ドキュメントはこちら によると、htmlのメタタグで${_csrf.token}
を使用できるはずです。
<meta name="_csrf" content="${_csrf.token}" />
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf.headerName}" />
JQueryを使用して「コンテンツ」の値を抽出し、AJAXを使用してリクエストヘッダーに配置します。
ただし、何らかの理由で、Spring Securityはこれを実際のトークンに「変換」せず、リテラル文字列「$ {_ csrf.token}」としてヘッダーに送信されるだけです。
ドキュメントに従って非表示の入力で${_csrf.token}
を使用する代替ルートを試してから、入力の値を確認することでトークンの評価を確認しようとしましたが、それでもまだプレーンテキスト "$ {_ csrf.token}"です。 。
Spring Securityが有効になっていないようですが、なんらかの構成が欠けていますか?私は現在、以下に示すように、最低限のSpring Security Java構成ではなくxml)を使用しています。
import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.*;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf();
}
}
デバッグ文を入れてからconfigureが呼び出されていることを知っているので、デフォルトで有効になっているはずなので、CSRF保護が実際に有効になっていると思います。
構文 "$ {}"はJSP式言語であることを理解しており、現在、それを使用してコンテキストをThymeleafを含むオブジェクトに評価することに成功しています。次に例を示します。
th:object="${context}"
だから私は次のようにメタタグの「コンテンツ」の前に「th:」を追加してみました:
<meta name="_csrf" th:content="${_csrf.token}"/>
ただし、これを評価できないという例外が発生します。
SpringEL式を評価する例外: "_csrf.token"
私の考えでは、式を適切に評価する方法を理解することがここでの鍵になると思います。
ようやくこの問題を解決しましたが、基本的にはSpring Securityを書き直す必要がありました。ここでそれはすべての栄光にあります。
最初に、私はEyal Lupuの素晴らしいブログ投稿 here の提案に従いましたが、AJAX要件のため、状況に合わせて調整する必要がありました。
Thymeleafの状況については、Thymeleafフォーラム-悪名高い問題7のアーカイブに重要な情報が隠されています。
https://github.com/thymeleaf/thymeleaf-spring/issues/7#issuecomment-27643488
Thymeleafの作成者自身による最後のコメントは、次のように述べています。
th:action
...この属性がタグに適用されているときを検出します-とにかく唯一の場所である必要があります-そのような場合はRequestDataValueProcessor.getExtraHiddenFields
(...)を呼び出し、返された終了タグの直前の非表示フィールド。
それが、トークンを機能させるために必要なキーフレーズでした。残念ながら、th:action
がgetExtraHiddenFields
もキックオフする理由は完全には明らかではありませんが、いずれにせよ、それが重要なのです。
したがって、Thymeleaf + Spring Security CSRF + AJAX POSTで苦労している人のために、これが私のステップです(これはかなり削減していますが、これを解決するための高レベルの概念です)。
SpringインターフェースRequestDataValueProcessorを実装し、Spring SecurityのXML構成に登録して、メソッドgetExtraHiddenFieldsをオーバーライドできるようにします。これにより、HTMLに非表示の入力フィールドを挿入できます(もちろんトークンを使用できます)。トークン自体は、Java.Util UUIDで生成されます。
JQueryを使用して、その非表示フィールドから値を読み取り、リクエストヘッダーの「X-CSRF-Token」属性を設定して、HTTP経由で送信されるようにします。フォーム送信を行っていないため、単に非表示の入力フィールドにトークンを残すことはできません。代わりにAJAX POSTを使用して、サーバ側。
SpringのHandlerInterceptorAdapterを拡張し、インターセプターとして登録することで、POSTメソッドが実行されるたびに、サーバー側の「preHandle」メソッドが呼び出され、リクエストトークンを比較できるようになります(前のステップのHTTPヘッダー)からセッションのトークン(同じである必要があります!)このチェックを行った後、リクエストの通過を許可するか、エラーを返すことができます。
私はあなたと同じ ソース記事 で始めたと思います、そしてあなたがしたのと同じ「あなたはできるはずです」同じ答えを追加します。私はそれを別の方法で戦った。私はThymeleafに私が望んでいた答えをくれました。
<meta name="_csrf" th:content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
Thymeleafは、要求されたSpring ELコンテンツで属性「コンテンツ」を置きました。次に、提供されたJavaScript/JQueryを使用して、メタタグからCSRFヘッダーに直接情報を抽出しました。
Thymeleaf-extras-springsecurity名前空間とその依存関係をプロジェクトに追加する前に、同様の問題がありました。 thymeleaf-extras-springsecurityを使用しても、メタタグを機能させることはできませんでした。しかし、私は隠し入力を使用してSpring Securityのcsrfトークンを正常に取得しました。私のために働く以下の指示があります:
htmlタグに、以下を追加します。
_xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"
_
Pom.xml(Mavenを使用している場合)に依存関係を追加する必要があります:thymeleaf-extras-springsecurity4。
次に、非表示の入力をページの本文内に追加して、csrfトークンを取得します。
_<input type="hidden" id= "csrf-token" th:name="${_csrf.parameterName}" th:content="${_csrf.token}" />
_
次に、それをjavascript/jquery内で次のように使用します。function f1() { var token1 = $('input#csrf-token').attr("content"); ... $.ajax({ ... type: "POST", beforeSend: function (request) { request.setRequestHeader("X-CSRF-TOKEN", token1); }, ...
これはすべて、スプリングセキュリティが有効になっていて、csrf保護をオフにしていないことを前提としています。
Web.xmlのspringSecurityFilterChain
の構成が正しくありません。正しい定義は次のとおりです。
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
Spring Securityはサーブレットフィルターのセットを使用して、提供する機能(CSRF保護を含む)を提供します。これらのフィルターはSpring Beanとして定義されます(つまり、Springアプリケーションコンテキストによってインスタンス化および管理されます)。 DelegatingFilterProxy
は特別なタイプのサーブレットフィルタで、登録されたサーブレットコンテキストでルートアプリケーションコンテキストを検出し、すべての呼び出しを同じ名前のBeanに委任します。
あなたの問題は別の問題であり、この問題にも遭遇しただけで、原因を解明するのに数時間かかりました。あなたが説明した問題の原因は、spring-security.xml内でcsrfサポートを有効にしていないことです。
この小さなスニペットは、security-config.xmlに入れる必要があります。
<!-- Static resources such as CSS and JS files are ignored by Spring Security -->
<security:http pattern="/static/**" security="none" />
<security:http use-expressions="true">
<!-- Enables Spring Security CSRF protection -->
<security:csrf/>
<!-- Configures the form login -->
<security:form-login
login-page="/login"
login-processing-url="/login/authenticate"
authentication-failure-url="/login?error=bad_credentials"
username-parameter="username"
password-parameter="password"/>
<!-- Configures the logout function -->
<security:logout
logout-url="/logout"
logout-success-url="/login"
delete-cookies="JESSIONID"/>
<!-- Anyone can access these urls -->
<security:intercept-url pattern="/auth/**" access="permitAll"/>
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/signin/**" access="permitAll"/>
<security:intercept-url pattern="/signup/**" access="permitAll"/>
<security:intercept-url pattern="/user/register/**" access="permitAll"/>
<!-- The rest of our application is protected. -->
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
<!-- Adds social authentication filter to the Spring Security filter chain. -->
<security:custom-filter ref="socialAuthenticationFilter" before="PRE_AUTH_FILTER" />
</security:http>
....
...
..
.
これを正しく設定して時間を節約してください...
チェリオ、フロ!
Thymeleafを使用する必要がない場合は、次のことをお勧めします。
これをページの上部に追加します。
<%@ taglib prefix="c" uri="http://Java.Sun.com/jsp/jstl/core"%>
これをログインフォームに追加します。
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
これらの依存関係をpom.xmlに追加します。
<dependency>
<groupId>org.Apache.Tomcat.embed</groupId>
<artifactId>Tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
多くの苦労の後、それは私のために働いた。