春季安全性:通过扩展CsrfRepository的自定义CSRF实现

时间:2019-07-09 11:33:51

标签: spring-boot spring-security csrf

我正在尝试通过实现Spring Security提供的CsrfRepository接口在Spring Boot应用程序中创建自定义的CSRF实现。

以下是我的自定义存储库的外观:

public class CustomCookieCsrfTokenRepository implements CsrfTokenRepository {

    static final String DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN";

    static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf";

    static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";

    @Override
    public CsrfToken generateToken(HttpServletRequest request) {
        return new DefaultCsrfToken(this.DEFAULT_CSRF_HEADER_NAME, this.DEFAULT_CSRF_PARAMETER_NAME, createNewToken());
    }

    @Override
    public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
        String tokenValue = token == null ? "" : token.getToken();
        Cookie cookie = new Cookie(this.DEFAULT_CSRF_COOKIE_NAME, tokenValue);
        cookie.setSecure(request.isSecure());
        response.addCookie(cookie);
    }

    @Override
    public CsrfToken loadToken(HttpServletRequest request) {
        Cookie cookie = WebUtils.getCookie(request, this.DEFAULT_CSRF_COOKIE_NAME);
        if (cookie == null) {
            return null;
        }
        String token = cookie.getValue();
        if (!StringUtils.hasLength(token)) {
            return null;
        }
        return new DefaultCsrfToken(this.DEFAULT_CSRF_HEADER_NAME, this.DEFAULT_CSRF_PARAMETER_NAME, token);
    }

    private String createNewToken() {
        String unsignedToken = UUID.randomUUID().toString();
        return RSAUtil.signMessage(unsignedToken, privateKey);
    }
}

问题:如您所见,我想使用私钥对Cookie值进行签名,并使用公钥对其进行验证。问题是该验证逻辑应在哪里进行?我猜测loadToken()方法可以具有验证签名的逻辑。这是正确的地方还是应该在其他地方发生?

有人可以提供一些有关如何以及在何处处理此问题的摘要或示例吗?

1 个答案:

答案 0 :(得分:2)

否,验证逻辑应位于自定义CsrfTokenRepository实现的 generateToken(HttpServletRequest请求)中。
saveToken(CsrfToken令牌,HttpServletRequest请求,HttpServletResponse响应)应该保存令牌(或在传递的“令牌”参数为null时删除保存的令牌)和 loadToken(HttpServletRequest请求)应返回当前请求/会话的现有已保存令牌(已通过saveToken方法保存);

@Component
public class CustomCsrfTokenRepository implements CsrfTokenRepository {

    static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf";
    static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";

    private String parameterName = DEFAULT_CSRF_PARAMETER_NAME;

    private String headerName = DEFAULT_CSRF_HEADER_NAME;

    private String cookieName = "USER_INFO";

    private static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = CustomCsrfTokenRepository2.class
        .getName().concat(".CSRF_TOKEN");

    private String sessionAttributeName = DEFAULT_CSRF_TOKEN_ATTR_NAME;

    @Override
    public CsrfToken generateToken(HttpServletRequest request) {
        Cookie cookie = WebUtils.getCookie(request, this.cookieName);
        if (cookie == null) {
            return new DefaultCsrfToken(this.headerName, this.parameterName,
                createNewToken());
        }
        String cookieValue = cookie.getValue();
        String token = cookieValue.split("\\|")[0];
        if (!StringUtils.hasLength(token)) {
            return new DefaultCsrfToken(this.headerName, this.parameterName,
                createNewToken());
        }
        return new DefaultCsrfToken(this.headerName, this.parameterName, token);
    }

    @Override
    public void saveToken(CsrfToken token, HttpServletRequest request,
        HttpServletResponse response) {
        if (token == null) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                session.removeAttribute(this.sessionAttributeName);
            }
        }
        else {
            HttpSession session = request.getSession();
            session.setAttribute(this.sessionAttributeName, token);
        }
    }

    @Override
    public CsrfToken loadToken(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return null;
        }
        return (CsrfToken) session.getAttribute(this.sessionAttributeName);
    }

    private String createNewToken() {
        return UUID.randomUUID().toString();
    }

}



并且您需要在HttpSecurity配置中设置您的customCsrfRepoImpl bean,如下所示

@Configuration
@EnableWebSecurity
public class SecurityConfigurarion extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {

    @Autowired
    private CsrfTokenRepository customCsrfTokenRepository; //your custom csrfToken repository impl class 

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {

        httpSecurity
            .csrf().csrfTokenRepository(customCsrfTokenRepository) //set your custom csrf impl in httpSecurity
            .and()
            .authorizeRequests()
                .antMatchers(permittedUrlsArr).permitAll()
                .anyRequest().authenticated()
                .and()
            .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .and()
            .logout()

    }

}