Java 如何强制Spring安全性更新XSRF-TOKEN cookie?
当用户进行身份验证时,Spring引导应用程序中的REST Spring安全Java 如何强制Spring安全性更新XSRF-TOKEN cookie?,java,spring,rest,spring-mvc,spring-security,Java,Spring,Rest,Spring Mvc,Spring Security,当用户进行身份验证时,Spring引导应用程序中的REST Spring安全/user服务无法立即更新XSRF-TOKENcookie。这会导致对/任何其他REST服务url的下一个请求返回无效CSRF证书错误,直到再次调用/user服务。如何解决此问题,以便REST/user服务在同一请求/响应事务中正确更新XSRF-TOKEN cookie,在该请求/响应事务中,REST首先对用户进行身份验证 后端REST/user服务被前端应用程序调用三次,但/user服务仅在第一次和第三次调用时返回匹配
/user
服务无法立即更新XSRF-TOKEN
cookie。这会导致对/任何其他REST服务url
的下一个请求返回无效CSRF证书
错误,直到再次调用/user
服务。如何解决此问题,以便REST/user
服务在同一请求/响应事务中正确更新XSRF-TOKEN cookie,在该请求/响应事务中,REST首先对用户进行身份验证
后端REST/user
服务被前端应用程序调用三次,但/user
服务仅在第一次和第三次调用时返回匹配的JSESSIONID/XSRF-TOKEN
cookie,而不是在第二次调用时返回
/
url模式,我认为这会调用/user
服务,服务器会使用与匿名用户关联的JSESSIONID
和XSRF-TOKEN
进行响应。FireFox开发者工具的网络选项卡将这些cookie显示为:
Response cookies:
JSESSIONID:"D89FF3AD2ACA7007D927872C11007BCF"
path:"/"
httpOnly:true
XSRF-TOKEN:"67acdc7f-5127-4ea2-9a7b-831e95957789"
path:"/"
然后,用户对公开访问的资源发出各种请求,而不会出错,FireFox开发者工具的网络选项卡显示这些相同的cookie值/user
服务的第二个请求是通过登录表单完成的,该表单发送有效的用户名和密码,/user
服务使用该用户名和密码对用户进行身份验证。但是/user
服务只返回更新的jsessionid cookie,而在此步骤中不更新xsrf令牌cookie。以下是FireFox开发者工具的网络选项卡中显示的cookie:
200 GET user
在FireFox的网络选项卡中包含以下cookie:
Response cookies:
JSESSIONID:"5D3B51A03B9AE218586591E67C53FB89"
path:"/"
httpOnly:true
AUTH1:"yes"
Request cookies:
JSESSIONID:"D89FF3AD2ACA7007D927872C11007BCF"
XSRF-TOKEN:"67acdc7f-5127-4ea2-9a7b-831e95957789"
请注意,响应包括一个新的JSESSIONID
,但不包括一个新的XSRF-TOKEN
。这会导致不匹配,导致对其他rest服务的后续请求中出现403
错误(由于无效的csrf令牌),直到第三次调用/user
服务解决此问题。我们是否有办法强制前面的200 get user
也返回新的XSRF-TOKEN
/user
服务的第三次调用使用的用户名和密码凭据与上面显示的第二次请求中使用的用户名和密码凭据非常相同,但是对/user
的第三次调用会导致XSRF_令牌
cookie得到正确更新,同时保留相同的正确JSESSIONID
。以下是FireFox开发者工具的网络选项卡显示的内容:
200 GET user
显示不匹配的请求强制更新响应中的XSRF-TOKEN
:
Response cookies:
XSRF-TOKEN:"ca6e869c-6be2-42df-b7f3-c1dcfbdb0ac7"
path:"/"
AUTH1:"yes"
Request cookies:
JSESSIONID:"5D3B51A03B9AE218586591E67C53FB89"
XSRF-TOKEN:"67acdc7f-5127-4ea2-9a7b-831e95957789"
/user
服务时,可以对下面的代码进行哪些具体更改,以强制更新XSRF-TOKEN
和JSESSIONID
cookies?我们是否在Spring中对后端/user
方法的代码进行了特定更改?还是在安全配置类中进行了更改?我们可以试着解决这个问题吗
后端/user
服务的代码和安全配置位于Spring Boot后端应用程序的主应用程序类中,该应用程序位于UiApplication.java
中,如下所示:
@SpringBootApplication
@Controller
@EnableJpaRepositories(basePackages = "demo", considerNestedRepositories = true)
public class UiApplication extends WebMvcConfigurerAdapter {
@Autowired
private Users users;
@RequestMapping(value = "/{[path:[^\\.]*}")
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
@RequestMapping("/user")
@ResponseBody
public Principal user(HttpServletResponse response, HttpSession session, Principal user) {
response.addCookie(new Cookie("AUTH1", "yes"));
return user;
}
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.US);
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
protected static class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter {
@Autowired
private Users users;
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(users);
}
}
@SuppressWarnings("deprecation")
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests()
.antMatchers("/registration-form").permitAll()
.antMatchers("/confirm-email**").permitAll()
.antMatchers("/submit-phone").permitAll()
.antMatchers("/check-pin").permitAll()
.antMatchers("/send-pin").permitAll()
.antMatchers("/index.html", "/", "/login", "/message", "/home", "/public*", "/confirm*", "/register*")
.permitAll().anyRequest().authenticated().and().csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
}
显示CSRF
错误的服务器日志的相关部分为:
2016-01-20 02:02:06.811 DEBUG 3995 --- [nio-9000-exec-5] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@70b8c8bb
2016-01-20 02:02:06.813 DEBUG 3995 --- [nio-9000-exec-5] o.s.security.web.FilterChainProxy : /send-pin at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
2016-01-20 02:02:06.813 DEBUG 3995 --- [nio-9000-exec-5] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for http://localhost:9000/send-pin
要解决此CSRF
错误,我需要对上述代码进行哪些具体更改
当后端/user
服务更改用户状态(登录、注销等)时,如何强制立即更新XSRF
cookie
注意:我猜测(基于我的研究),这个问题的解决方案将涉及更改以下Spring安全类的某些组合的配置,所有这些都在UiApplication.java
中定义,如下所示:
/user
服务返回的- 使/register端点公开,并提供注册用户的控制器
- 不要在授权标头中包含注册表的用户名/密码,因为这将导致Spring Security尝试验证凭据。而是将参数作为JSON或表单编码的参数包含在/register控制器处理的过程中
CsrfAuthenticationStrategy
使任何CsrfToken无效(以确保不可能发生会话固定攻击)。这就是触发要使用的新CsrfToken的原因
但是,问题是在执行身份验证之前调用了csrfTokenRepository
。这意味着当csrfTokenRepository
检查令牌是否已更改时,如果结果为false(它尚未更改)
要解决此问题,可以插入自定义的AuthenticationSuccessHandler
。例如:
公共类MyAuthenticat