Spring 安全性:通过扩展 CsrfRepository 实现自定义 CSRF
Spring Security: Custom CSRF Implementation by extending CsrfRepository
我正在尝试通过实现 Spring 安全性提供的 CsrfRepository
接口,在我的 Spring 引导应用程序中创建自定义的 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 值并使用 public 密钥对其进行验证。问题是这个验证逻辑应该在哪里发生?我猜 loadToken()
方法可以具有验证签名的逻辑。这是正确的地点还是应该在其他地方举行?
有人可以提供一些关于如何以及在何处处理此问题的片段或示例吗?
不,验证逻辑应该在自定义 CsrfTokenRepository 实现的 generateToken(HttpServletRequest request) 中。
saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) 应该保存令牌(或者当传递的 'token' 参数为空时删除保存的令牌)和loadToken(HttpServletRequest request) 应该return 当前已保存的令牌(通过 saveToken 方法保存)request/session;
@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()
}
}
我正在尝试通过实现 Spring 安全性提供的 CsrfRepository
接口,在我的 Spring 引导应用程序中创建自定义的 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 值并使用 public 密钥对其进行验证。问题是这个验证逻辑应该在哪里发生?我猜 loadToken()
方法可以具有验证签名的逻辑。这是正确的地点还是应该在其他地方举行?
有人可以提供一些关于如何以及在何处处理此问题的片段或示例吗?
不,验证逻辑应该在自定义 CsrfTokenRepository 实现的 generateToken(HttpServletRequest request) 中。
saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) 应该保存令牌(或者当传递的 'token' 参数为空时删除保存的令牌)和loadToken(HttpServletRequest request) 应该return 当前已保存的令牌(通过 saveToken 方法保存)request/session;
@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()
}
}