1

Spring を使用した REST API があります。インターセプターを作成しました:

@Component
public class CSRFInterceptor extends HandlerInterceptorAdapter {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // code here

        return true;
    }
}

行われるすべてのリクエストは、次の対応する Java クラスで JSON を使用しています。

public class CSRFTokenContainer<T> {
    private T data;
    private String csrf;

    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
    public String getCsrf() {
        return csrf;
    }
    public void setCsrf(String csrf) {
        this.csrf = csrf;
    }
}

私のコントローラーでは、たとえば次のように使用するとすべてうまく機能します。

@Controller
@RequestMapping("/persons")
public class PersonController {

    @RequestMapping(method=RequestMethod.POST)
    public @ResponseBody String create(@RequestBody CSRFTokenContainer<Person> account, HttpServletResponse response) {

        // do something

        return "test";
    }
}

今、私は次のことをしたいと思います: コントローラは、CSRF トークンなしで Person オブジェクトを受け取るだけです。CSRF トークンは Interceptor 内で処理される必要があります。これどうやってするの?主な問題は、Interceptor 内で CSRFTokenContainer オブジェクトを取得する方法がわからないことだと思います。その後、「データ」部分のみを含むようにリクエストを変更したいと思います。

いくつかのコード例がいいでしょう。

ありがとうございました!

4

1 に答える 1

0

私はこのようにCSRFの問題を解決しました:

サーバー側でトークンを作成し、JSP を介して GWT ホスト ページ内に配置します。トークンはセッションにも保存されます。

myPage.jsp:

<%@taglib prefix="t" uri="myTags" %>
<!doctype html>
<html>
    <head>
        ...
        <script>
            <t:csrfToken />
        </script>
        ...
    </head>
    ...
</html>

myTags.tld:

<?xml version="1.0" encoding="UTF-8"?>
<taglib xsi:schemaLocation="
    http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version="2.1">

    <tlib-version>1.0</tlib-version>
    <short-name>t</short-name>
    <uri>myTags</uri>

    <tag> 
        <name>csrfToken</name> 
        <tag-class>myapp.server.jsp.CSRFTokenTag</tag-class>
        <body-content>empty</body-content>
    </tag>  
</taglib>

CSRFTokenTag:

public class CSRFTokenTag extends TagSupport {
    private final SecureRandom random = new SecureRandom();

    private String generateToken() {
        final byte[] bytes = new byte[32];
        random.nextBytes(bytes);
        return Base64.encode(bytes);
    }

    @Override
    public int doStartTag() throws JspException {
        String token = generateToken();

        try {
            pageContext.getOut().write("var " + "myCSRFVarName" + " = \"" + token + "\";");
        } catch (IOException e) {}

        pageContext.getSession().setAttribute("csrfTokenSessionAttributeName", token);

        return SKIP_BODY;
    }

    @Override
    public int doEndTag() throws JspException {
        return EVAL_PAGE;
    }
}

GWT は JSNI 経由でトークンを読み取ります。

public class CSRFToken {
    private native static String get()/*-{
        return $wnd["myCSRFVarName"];
    }-*/;
}

また、すべてのリクエストで、Web アプリケーションはカスタム HTTP ヘッダー内でトークンを送信します。たとえば、次のようになります。

RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, "/rest/persons");
rb.setHeader("myCSRFTokenHeader", CSRFToken.get());
rb.setRequestData("someData");
rb.setCallback(new RequestCallback() {
    @Override
    public void onResponseReceived(Request request, Response response) {
        // ...
    }
    @Override
    public void onError(Request request, Throwable exception) {
        // ...
    }
});
rb.send();

Spring 内で、リクエストごとに送信されたヘッダーからトークンを読み取り、それをチェックするインターセプターを作成しました。

@Component
public class CSRFInterceptor extends HandlerInterceptorAdapter {    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String sessionCSRFToken = (String) request.getSession().getAttribute("csrfTokenSessionAttributeName");

        if(sessionCSRFToken != null && sessionCSRFToken.equals(request.getHeader("myCSRFTokenHeader"))) {
            return true;
        } else {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication required");
            return false;
        }
    }
}

完璧ではないかもしれませんが、かなりうまく機能しているようです。

于 2012-07-07T20:49:11.340 に答える