How to obtain csrf token in a velocity macro when using spring security

丶灬走出姿态 提交于 2020-01-02 10:30:10

问题


I am trying to create a custom login screen for a spring web security enabled application, and I cannot figure out how to pass the csrf token to velocity (no, I cannot use JSP at the moment).

The model looks something like this:

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

And the relevant section of the velocity template looks like (taken and modified from a jsp example):

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

Of course, the ${_csrf.parameterName} and ${_csrf.token} variables are empty, so this only works if I disable csrf protection. So my main question is: how do I fill them in the model (or anywhere else)?


回答1:


I have found the solution, the main point is that the csrf token is injected into the HttpServletRequest by the CsrfFilter, and you can get the HttpServletRequest object by just adding a HttpServletRequest parameter to your method that handles the request mapping.

So the changes that needed to be done are:

@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);
    }
...



回答2:


Just to share my little bit, I initially started by using @P.Péter's solution which was fine. but as my application grew to have so many forms, i decided it was too cumbersome using that snippet for every form that I needed to protect from csrf intrusions, so here's what I did so I don't have to repeat across my application.

@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());
    }
}

POINT - The idea is to use a @ControllerAdvice which gets called on entering any Spring Controller to attach the CsrfToken to the resulting View using the @ModelAttribute("<attribute-name>") annotation.

NOTE 1 - This _csrf model attribute gets attached for all Views, hence if you want to limit the _csrf processing to selected URLs or Views, see this resource here for very nice samples on how to do that.

NOTE 2 - Notice how I commented out the following line?

//HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

That's because in my case the Autowired HttpServletRequest instance is sufficient for my scenario. However some situations may warrant that you use the commented out instance e.g when you need to obtain the request object from some part of your application that isn't necessarily request scoped... See @Samit G's answer as pointed out in this thread here for more information.



来源:https://stackoverflow.com/questions/25625056/how-to-obtain-csrf-token-in-a-velocity-macro-when-using-spring-security

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!