HA

使用thymeleaf和spring security提供的csrf方法

使用thymeleaf和spring security提供的csrf方法

只需要在html内的form在设置action时使用thymeleaf属性th:action即可

<form th:action="@{login}" method="post">
    <p>
        <label for="username">Username</label> <input type="text" id="username" name="username" />
    </p>
    <p>
        <label for="password">Password</label> <input type="password" id="password" name="password" />
    </p>
    <button type="submit" class="btn">Log in</button>
</form>

自定义csrf校验

  1. spring security配置文件中去掉自带的csrf校验
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
        ...
        http.csrf().disable();
        ... 
    }
    ...

	/***
	 * 说明见步骤2
	 * @see <a href=
	 *      "https://blog.eyallupu.com/2012/04/csrf-defense-in-spring-mvc-31.html">New
	 *      in Spring MVC 3.1: CSRF Protection using
	 *      RequestDataValueProcessor</a>
	 */
	@Bean
	public CSRFRequestDataValueProcessor requestDataValueProcessor() {
		return new CSRFRequestDataValueProcessor();
	}
}
  1. 实现RequestDataValueProcessor接口,关键方法是getExtraHiddenFields,然后把它添加到spring security配置文件中(参照步骤1中代码)
package springSecurity.csrf;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.servlet.support.RequestDataValueProcessor;

public class CSRFRequestDataValueProcessor implements RequestDataValueProcessor {

	@Override
	public String processAction(HttpServletRequest request, String action, String httpMethod) {
		return action;
	}

	@Override
	public String processFormFieldValue(HttpServletRequest request, String name, String value, String type) {
		return value;
	}

	@Override
	public Map<String, String> getExtraHiddenFields(HttpServletRequest request) {
		Map<String, String> hiddenFields = new HashMap<String, String>();
		hiddenFields.put(CSRFTokenManager.CSRF_PARAM_NAME, CSRFTokenManager.getTokenForSession(request.getSession()));
		return hiddenFields;
	}

	@Override
	public String processUrl(HttpServletRequest request, String url) {
		return url;
	}

}
  1. 添加自定义拦截器并添加到spring mvc配置文件中
package springSecurity.csrf;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import lombok.val;

public class CSRFHandlerInterceptor extends HandlerInterceptorAdapter {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		val me = request.getMethod();
		if (!request.getMethod().equalsIgnoreCase("POST")) {
			// Not a POST - allow the request
			return true;
		} else {
			// This is a POST request - need to check the CSRF token
			String sessionToken = CSRFTokenManager.getTokenForSession(request.getSession());
			String requestToken = CSRFTokenManager.getTokenFromRequest(request);
			if (sessionToken.equals(requestToken)) {
				return true;
			} else {
				response.sendError(HttpServletResponse.SC_FORBIDDEN, "Bad or missing CSRF value");
				return false;
			}
		}
	}

}
@Configuration
@EnableWebMvc
@EnableGlobalMethodSecurity
@ComponentScan("service,servlet,controller,springSecurity")
public class AppcationContextConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {
    ...
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(new CSRFHandlerInterceptor());
	}
    ...
}

工具类CSRFTokenManager

package springSecurity.csrf;

import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class CSRFTokenManager {

	static final String CSRF_PARAM_NAME = "CSRFToken";

	private final static String CSRF_TOKEN_FOR_SESSION_ATTR_NAME = CSRFTokenManager.class.getName() + ".tokenval";

	static String getTokenForSession(HttpSession session) {
		String token = null;
		// I cannot allow more than one token on a session - in the case of two
		// requests trying to
		// init the token concurrently.
		// Notice: in real life I wouldn't synchronize on the session instance.
		// This should be done on an attribute on the session. But for the
		// blog demo this is fine
		synchronized (session) {
			token = (String) session.getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
			if (null == token) {
				token = UUID.randomUUID().toString();
				session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token);
			}
		}
		return token;
	}

	static String getTokenFromRequest(HttpServletRequest request) {
		return request.getParameter(CSRF_PARAM_NAME);
	}

	private CSRFTokenManager() {
	};

}

参考New in Spring MVC 3.1: CSRF Protection using RequestDataValueProcessor