我有一个带有spring-boot 2.0.1的web应用程序,受spring-security保护。我使用PersistentTokenRepository
表示Remember-Me并将令牌存储在MySQL数据库中。
在服务器日志文件中,我看到很多带有CookieTheftException
的堆栈跟踪。这么多,我发现很难相信实际的Cookie会被盗,但会假设某种错误配置。通过添加一些分析代码,似乎只有移动浏览器会受到影响。
Servlet.service() for servlet [dispatcherServlet] in context with path [/r] threw exception
org.springframework.security.web.authentication.rememberme.CookieTheftException: Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.
at org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices.processAutoLoginCookie(PersistentTokenBasedRememberMeServices.java:119) ~[spring-security-web-5.0.4.RELEASE.jar!/:5.0.4.RELEASE]
手动测试无法重现这一点。删除会话cookie,但保留记住我的Cookie并向受限制的URL发出请求会导致正常的身份验证会话。
以下是我的安全配置的相关部分:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
@Configuration
public static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private RememberMeServices rememberMeServices;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe()
.key(rememberMeKey)
.rememberMeServices(rememberMeServices);
;
}
}
/**
* Key for RememberMeServices and RememberMeAuthenticationProvider.
*/
private static final String rememberMeKey = "...";
@Bean
public RememberMeServices rememberMeServices(UserDetailsService userDetailsService, PersistentTokenRepository persistentTokenRepository) {
PersistentTokenBasedRememberMeServices rememberMeServices = new AnalyzingPersistentTokenBasedRememberMeServices(
rememberMeKey, userDetailsService, persistentTokenRepository);
rememberMeServices.setTokenValiditySeconds((int) Duration.of(366L, ChronoUnit.DAYS).toSeconds());
return rememberMeServices;
}
@Bean
public PersistentTokenRepository persistentTokenRepository(JdbcTemplate jdbcTemplate) {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setJdbcTemplate(jdbcTemplate);
return tokenRepository;
}
}
AnalyzingPersistentTokenBasedRememberMeServices
是一个PersistentTokenBasedRememberMeServices
,processAutoLoginCookie
还有一些额外的记录。
另一个专长是,我使用自定义AuthenticationProvider
,并仅为RememberMe提供UserDetailsService
。但如上所述,手动测试工作正常。但是,用户报告说他们经常登出(会话超时为24小时)。
有没有人经历过这样的事情并有解决方案?我是否会错过一些关键配置?
答案 0 :(得分:1)
PersistentTokenBasedRememberMeServices
不适用于具有并发请求的应用程序,这些请求可能会发送相同的令牌系列。
请参阅这些近五年前未解决的错误报告:
https://github.com/spring-projects/spring-security/issues/2648
https://github.com/spring-projects/spring-security/issues/3079
使用TokenBasedRememberMeServices
没有这些问题。
答案 1 :(得分:0)
我最终使用Redisson实现了分布式锁定,并使用了一个分布式映射来保存一个快速过期的缓存,从而使一批并发请求中的第一个到达请求可以更新令牌,并允许将随后的请求传递给请注意,令牌最近已刷新并使用该新值。
答案 2 :(得分:0)
在Spring Security Web 4.2.13中,令牌延长机制的实现问题
protected UserDetails processAutoLoginCookie(String[] cookieTokens,...
.....
PersistentRememberMeToken newToken = new PersistentRememberMeToken(
token.getUsername(), token.getSeries(), generateTokenData(), new Date());
try {
tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(),
newToken.getDate());
addCookie(newToken, request, response);
}
catch (Exception e) {
logger.error("Failed to update token: ", e);
throw new RememberMeAuthenticationException(
"Autologin failed due to data access problem");
}
在刷新过程中,相同系列的并发请求令牌值可以在DB中重写几次,但在cookie中可以保存另一个值。
登录期间创建令牌的效果很好,在最坏的情况下,您在数据库中可以有很多持久令牌,但是其中一个可以工作=)
在我的项目中,我通过修改刷新算法来修复错误:
if (token.date + 1 < new Date()){
try {
PersistentRememberMeToken newToken = new PersistentRememberMeToken( token.getUsername(), generateSeriesData(), generateTokenData(), new Date())
tokenRepository.createNewToken(newToken)
addCookie(newToken, request, response)
}
catch (Exception e) {
logger.error("Failed to update token: ", e);
throw new RememberMeAuthenticationException(
"Autologin failed due to data access problem");
}
}
此代码最终会产生许多数据库记录,但是可以正常工作