如何处理JSON并修改Spring Interceptor中的请求?

时间:2012-07-05 14:06:17

标签: json spring spring-mvc interceptor

我有一个使用Spring的REST API。我创建了一个拦截器:

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

        // code here

        return true;
    }
}

每个请求都使用JSON和以下相应的Java类:

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";
    }
}

现在我想执行以下操作:Controller应该只接收没有CSRF令牌的Person对象。应该在拦截器内处理CSRF令牌。我怎样才能做到这一点?我认为主要的问题是,我不知道如何在Interceptor中获取我的CSRFTokenContainer对象。之后我想修改请求只包含“数据”部分。

一些代码示例会很好。

谢谢!

1 个答案:

答案 0 :(得分:0)

我用这种方式解决了CSRF问题:

我在服务器端创建令牌,并通过JSP将其放在GWT主页中。令牌也存储在Session:

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;
        }
    }
}

它可能并不完美,但似乎效果很好!