使用spring security时如何在速度宏中获取csrf标记

时间:2014-09-02 13:49:43

标签: java spring velocity

我正在尝试为启用spring web security的应用程序创建自定义登录屏幕,我无法弄清楚如何将csrf标记传递给velocity(不,我目前无法使用JSP)。

模型看起来像这样:

@RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView login(
    @RequestParam(value = "error", required = false) String error,
    @RequestParam(value = "logout", required = false) String logout
    ModelAndView model = new ModelAndView();
    if (error != null) {
        model.addObject("error", "Invalid username or password!");
    }
    if (logout != null) {
        model.addObject("msg", "You've been logged out successfully.");
    }
    model.setViewName("login");
    return model;
}

速度模板的相关部分看起来像(从jsp示例中获取和修改):

    <form name='loginForm' action="/login" method='POST'>
      <table>
        <tr>
            <td>User:</td>
            <td><input type='text' name='username' value=''></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input type='password' name='password' /></td>
        </tr>
        <tr>
            <td colspan='2'><input name="submit" type="submit" value="submit" /></td>
        </tr>
      </table>
      <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    </form>

当然,${_csrf.parameterName}${_csrf.token}变量都是空的,所以这只有在我禁用csrf保护的情况下才有效。所以我的主要问题是:如何在模型中(或其他任何地方)填充它们?

2 个答案:

答案 0 :(得分:5)

我找到了解决方案,主要的一点是csrfFilter将csrf令牌注入到HttpServletRequest中,只需在处理请求映射的方法中添加HttpServletRequest参数即可获取HttpServletRequest对象。 p>

所以需要做的改变是:

@RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView login(
    @RequestParam(value = "error", required = false) String error,
    @RequestParam(value = "logout", required = false) String logout,
    HttpServletRequest request
){
...
    CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
    if (csrfToken != null) {
        model.addObject("_csrf",csrfToken);
    }
...

答案 1 :(得分:1)

为了分享我的一点点,我最初开始使用@P.Péter的解决方案,这很好。但是随着我的应用程序变得越来越多,我觉得使用该片段对于我需要保护的每个表单都没有csrf入侵过于繁琐,所以这就是我所做的,所以我不必在我的应用程序中重复。 / p>

<p>This is a paragraph.</p>

<select name="" id="legal-form">
  <option value="v1">v1</option>
  <option value="v2">v2</option>
  <option value="v3">v3</option>
</select>


$('#legal-form').change(function(){
        var x;
        if($(this).val() == 'v2'){ 
          x = $("p").detach();
        } else { 
          $("body").prepend(x);
        }; 
});

POINT - 我们的想法是使用一个@ControllerAdvice public class CsrfControllerAdvice { @Autowired private HttpServletRequest request; @ModelAttribute("_csrf") public CsrfToken appendCSRFToken(){ //HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); return (CsrfToken) request.getAttribute(CsrfToken.class.getName()); } } 来调用任何Spring Controller,使用@ControllerAdvice注释将CsrfToken附加到生成的View。

注1 - @ModelAttribute("<attribute-name>")模型属性附加了所有视图,因此如果您要将_csrf处理限制为所选的网址或视图,请参阅{{3关于如何做到非常好的样本。

注意2 - 请注意我是如何注释掉以下行的?

_csrf

那是因为在我的情况下,Autowired //HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); 实例足以支持我的场景。但是,某些情况可能会保证您使用注释掉的实例,例如,当您需要从应用程序的某个部分获取请求对象时,不一定是请求作用域...请参阅{{3}中指出的@Samit G的答案}了解更多信息。