spring security + form login + redis session storage -> keep coming out anonymous User
codesejin opened this issue · comments
Describe the bug
A clear and concise description of what the bug is.
-
I have been struggling for days because after logging in and receiving the welcome message, the user appears as an anonymous user when moving to another browser page. I confirmed that sessions are stored inside Redis. However, I keep getting errors indicating that something other than the SecurityContext is being stored in HttpSessionSecurityContextRepository.
-
Also It shows that a LinkedHashMap is stored inside the SpringSecurityContextHolder.
I've also tried searching on Google, but I couldn't find the answer I'm looking for. I'm not sure what the cause is.
HttpSessionSecurityContextRepository & : 96f82b5f-49ee-4518-9f3a-da58f6212f7b
spring:session:sessions in Redis : e7ba4f70-34a6-4292-bf2e-cf9e419d8998
20240327 03:00:32.627 [http-nio-8080-exec-8] WARN o.s.s.w.c.HttpSessionSecurityContextRepository - SPRING_SECURITY_CONTEXT did not contain a SecurityContext but contained: '{authentication={authorities=[{authority=ROLE_ADMIN}], details={remoteAddress=0:0:0:0:0:0:0:1, sessionId=96f82b5f-49ee-4518-9f3a-da58f6212f7b}, authenticated=true, principal={password=null, username=sejinpark@email.com, authorities=[{authority=ROLE_ADMIN}], accountNonExpired=true, accountNonLocked=true, credentialsNonExpired=true, enabled=true}, credentials=null, name=sejinpark@email.com}}'; are you improperly modifying the HttpSession directly (you should always use SecurityContextHolder) or using the HttpSession attribute reserved for this class?
authentication = AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=e7ba4f70-34a6-4292-bf2e-cf9e419d8998], Granted Authorities=[ROLE_ANONYMOUS]]
To Reproduce
Steps to reproduce the behavior.
Expected behavior
A clear and concise description of what you expected to happen.
Sample
A link to a GitHub repository with a minimal, reproducible sample.
Reports that include a sample will take priority over reports that do not.
At times, we may require a sample, so it is good to try and include a sample up front.
this is the session stored in Redis
{
"creationTime": 1711475749704,
"lastAccessedTime": 1711475904642,
"maxInactiveInterval": 1800,
"sessionAttr": {
"SPRING_SECURITY_CONTEXT": {
"authentication": {
"authorities": [
{
"authority": "ROLE_ADMIN"
}
],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "96f82b5f-49ee-4518-9f3a-da58f6212f7b"
},
"authenticated": true,
"principal": {
"password": null,
"username": "sejinpark@email.com",
"authorities": [
{
"authority": "ROLE_ADMIN"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "sejinpark@email.com"
}
},
"SPRING_SECURITY_SAVED_REQUEST": {
"cookies": [
{
"name": "Idea-571775cc",
"value": "4869c9df-2414-4603-b718-76027a1adc42",
"attributes": {
"Max-Age": "-1",
"Secure": "false"
},
"version": 0,
"path": null,
"domain": null,
"maxAge": -1,
"comment": null,
"secure": false,
"httpOnly": false
},
{
"name": "SESSION",
"value": "YTYwZTlmMGUtZDA2YS00YTBlLTgxZTYtYzg0ZjlmYjZmNzdi",
"attributes": {
"Max-Age": "-1",
"Secure": "false"
},
"version": 0,
"path": null,
"domain": null,
"maxAge": -1,
"comment": null,
"secure": false,
"httpOnly": false
}
],
"locales": [
"ko_KR",
"ko",
"en_US",
"en"
],
"contextPath": "",
"method": "GET",
"pathInfo": null,
"queryString": null,
"requestURI": "/error",
"requestURL": "http://localhost:8080/error",
"scheme": "http",
"serverName": "localhost",
"servletPath": "/error",
"serverPort": 8080,
"parameterMap": {},
"headerNames": [
"accept",
"accept-encoding",
"accept-language",
"connection",
"cookie",
"host",
"sec-ch-ua",
"sec-ch-ua-mobile",
"sec-ch-ua-platform",
"sec-fetch-dest",
"sec-fetch-mode",
"sec-fetch-site",
"sec-fetch-user",
"upgrade-insecure-requests",
"user-agent"
],
"parameterNames": [],
"redirectUrl": "http://localhost:8080/error?continue"
}
}
}
@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final UserDetailsService userDetailsService;
private final AuthenticationSuccessHandler customAuthenticationSuccessHandler;
private final AuthenticationFailureHandler customAuthenticationFailureHandler;
private final AccessDeniedHandler customAccessDeniedHandler;
private static final String LOGIN_URL = "/api/v1/members/login";
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.headers(headerConfig ->
headerConfig.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)
)
.authorizeHttpRequests(authorizeRequests ->
authorizeRequests
// resource에 대해서는 모든 요청 허용
.requestMatchers(
PathRequest.toStaticResources().atCommonLocations()
).permitAll()
.requestMatchers(
"/",
"/api/v1/members/signup",
"/main",
"/api/v1/sse/**",
"/api/v1/event/**")
.permitAll()
.requestMatchers(
"/member").hasAnyRole("USER")
.requestMatchers("/admin").hasAnyRole("ADMIN")
// 그 외 모든 요청은 인증 완료
.anyRequest().authenticated()
);
http
.formLogin(form -> form
.loginProcessingUrl(LOGIN_URL)
.defaultSuccessUrl("/api/v1/")
.usernameParameter("email")
.passwordParameter("password")
.successHandler(customAuthenticationSuccessHandler)
.failureHandler(customAuthenticationFailureHandler)
.permitAll()
);
http
.logout(logout -> logout
.logoutUrl("/api/v1/members/logout")
.logoutSuccessUrl(LOGIN_URL)
.invalidateHttpSession(true)
.deleteCookies("SESSIONID")
);
http
.exceptionHandling(exceptionHandling -> exceptionHandling
.accessDeniedHandler(customAccessDeniedHandler)
);
http.
rememberMe(rememberMe -> rememberMe
.rememberMeCookieName("remember")
.tokenValiditySeconds(3600)
.userDetailsService(userDetailsService)
);
http
.sessionManagement(sessionManagement -> sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(1)
.maxSessionsPreventsLogin(false)
.expiredUrl(LOGIN_URL)
)
.securityContext((securityContext) -> {
securityContext.securityContextRepository(delegatingSecurityContextRepository());
securityContext.requireExplicitSave(true);
});
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncryptor();
}
@Bean
public DelegatingSecurityContextRepository delegatingSecurityContextRepository() {
return new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(),
new HttpSessionSecurityContextRepository()
);
}
}
@RequiredArgsConstructor
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
private final MemberRepository memberRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Member member = memberRepository.findMemberByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException(NOT_EXIST_MEMBER));
List<GrantedAuthority> roles = new ArrayList<>();
roles.add(new SimpleGrantedAuthority(member.getRole().name()));
return new MemberContext(member, roles);
}
}
@EqualsAndHashCode
public class MemberContext extends User {
private Member member;
public MemberContext(Member member, Collection<? extends GrantedAuthority> authorities) {
super(member.getEmail(),member.getPassword(), authorities);
this.member = member;
}
}
@Component
@RequiredArgsConstructor
public class CustomAuthenticationProvider implements AuthenticationProvider {
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
MemberContext memberContext = (MemberContext) userDetailsService.loadUserByUsername(username);
if (!passwordEncoder.matches(password, memberContext.getPassword())) {
throw new BadCredentialsException(DO_NOT_MATCHES_PSWD);
}
return new UsernamePasswordAuthenticationToken(memberContext, null, memberContext.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
@Configuration
@EnableRedisHttpSession
public class SessionConfig {
@Value("${spring.data.redis.host}")
private String redisHost;
@Value("${spring.data.redis.port}")
private String redisPort;
@Value("${spring.data.redis.password}")
private String redisPassword;
private static final String REDISSON_HOST_PREFIX = "redis://";
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(redisHost);
redisStandaloneConfiguration.setPort(Integer.parseInt(redisPort));
redisStandaloneConfiguration.setPassword(redisPassword);
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress(REDISSON_HOST_PREFIX + redisHost + ":" + Integer.parseInt(redisPort))
.setPassword(redisPassword)
.setSslEnableEndpointIdentification(false);
return Redisson.create(config);
}
/**
* Redis template
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(springSessionDefaultRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
return new Jackson2JsonRedisSerializer<>(Object.class);
}
}
- When attempting to log in, the log shows that authentication was successful.
20240327 10:21:40.390 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.s.ChangeSessionIdAuthenticationStrategy - Changed session id from 1cf15625-1fc7-42fa-89dc-fe1f32ebda45
20240327 10:21:40.390 [http-nio-8080-exec-1] DEBUG o.s.s.c.s.SessionRegistryImpl - Registering session 22cffd02-bf30-492c-b43c-a04707a284b1, for principal com.flab.offcoupon.config.security.service.MemberContext [Username=sejinpark@email.com, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN]]
20240327 10:21:40.391 [http-nio-8080-exec-1] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=com.flab.offcoupon.config.security.service.MemberContext [Username=sejinpark@email.com, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=1cf15625-1fc7-42fa-89dc-fe1f32ebda45], Granted Authorities=[ROLE_ADMIN]]] to HttpSession [org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@2a82af53]
20240327 10:21:40.391 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.UsernamePasswordAuthenticationFilter - Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=com.flab.offcoupon.config.security.service.MemberContext [Username=sejinpark@email.com, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=1cf15625-1fc7-42fa-89dc-fe1f32ebda45], Granted Authorities=[ROLE_ADMIN]]
20240327 10:21:40.391 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.r.TokenBasedRememberMeServices - Did not send remember-me cookie (principal did not set parameter 'remember-me')
20240327 10:21:40.391 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.r.TokenBasedRememberMeServices - Remember-me login not requested.
- When retrieving the authenticated user, the log indicates the process.
20240327 10:34:08.156 [http-nio-8080-exec-4] DEBUG o.s.s.w.FilterChainProxy - Securing GET /main
20240327 10:34:08.165 [http-nio-8080-exec-4] WARN o.s.s.w.c.HttpSessionSecurityContextRepository - SPRING_SECURITY_CONTEXT did not contain a SecurityContext but contained: '{authentication={authorities=[{authority=ROLE_ADMIN}], details={remoteAddress=0:0:0:0:0:0:0:1, sessionId=1cf15625-1fc7-42fa-89dc-fe1f32ebda45}, authenticated=true, principal={password=null, username=sejinpark@email.com, authorities=[{authority=ROLE_ADMIN}], accountNonExpired=true, accountNonLocked=true, credentialsNonExpired=true, enabled=true}, credentials=null, name=sejinpark@email.com}}'; are you improperly modifying the HttpSession directly (you should always use SecurityContextHolder) or using the HttpSession attribute reserved for this class?
20240327 10:34:08.173 [http-nio-8080-exec-4] DEBUG o.s.s.w.FilterChainProxy - Secured GET /main
20240327 10:34:08.292 [http-nio-8080-exec-4] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Set SecurityContextHolder to anonymous SecurityContext
20240327 10:34:08.413 [http-nio-8080-exec-5] DEBUG o.s.s.w.FilterChainProxy - Securing GET /api/v1/sse/connect
20240327 10:34:08.439 [http-nio-8080-exec-5] WARN o.s.s.w.c.HttpSessionSecurityContextRepository - SPRING_SECURITY_CONTEXT did not contain a SecurityContext but contained: '{authentication={authorities=[{authority=ROLE_ADMIN}], details={remoteAddress=0:0:0:0:0:0:0:1, sessionId=1cf15625-1fc7-42fa-89dc-fe1f32ebda45}, authenticated=true, principal={password=null, username=sejinpark@email.com, authorities=[{authority=ROLE_ADMIN}], accountNonExpired=true, accountNonLocked=true, credentialsNonExpired=true, enabled=true}, credentials=null, name=sejinpark@email.com}}'; are you improperly modifying the HttpSession directly (you should always use SecurityContextHolder) or using the HttpSession attribute reserved for this class?
20240327 10:34:08.439 [http-nio-8080-exec-5] DEBUG o.s.s.w.FilterChainProxy - Secured GET /api/v1/sse/connect
20240327 10:34:08.442 [http-nio-8080-exec-5] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Set SecurityContextHolder to anonymous SecurityContext
authentication = AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=22cffd02-bf30-492c-b43c-a04707a284b1], Granted Authorities=[ROLE_ANONYMOUS]]
20240327 10:34:08.466 [http-nio-8080-exec-5] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.springframework.security.core.context.SecurityContext (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.springframework.security.core.context.SecurityContext is in unnamed module of loader 'app')] with root cause
java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.springframework.security.core.context.SecurityContext (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.springframework.security.core.context.SecurityContext is in unnamed module of loader 'app')
at com.flab.offcoupon.controller.SseApiController.connect(SseApiController.java:31)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:261)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:189)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:917)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:829)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108)
at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231)
at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479)
at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340)
at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128)
at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:145)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:101)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:151)
at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:129)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter.doFilterInternal(DefaultLogoutPageGeneratingFilter.java:58)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:189)
at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:175)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82)
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323)
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224)
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191)
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)
at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$3(HandlerMappingIntrospector.java:195)
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)
at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74)
at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:225)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142)
at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:833)
20240327 10:34:08.478 [http-nio-8080-exec-5] DEBUG o.s.s.w.FilterChainProxy - Securing GET /error
20240327 10:34:08.478 [http-nio-8080-exec-5] WARN o.s.s.w.c.HttpSessionSecurityContextRepository - SPRING_SECURITY_CONTEXT did not contain a SecurityContext but contained: '{authentication={authorities=[{authority=ROLE_ADMIN}], details={remoteAddress=0:0:0:0:0:0:0:1, sessionId=1cf15625-1fc7-42fa-89dc-fe1f32ebda45}, authenticated=true, principal={password=null, username=sejinpark@email.com, authorities=[{authority=ROLE_ADMIN}], accountNonExpired=true, accountNonLocked=true, credentialsNonExpired=true, enabled=true}, credentials=null, name=sejinpark@email.com}}'; are you improperly modifying the HttpSession directly (you should always use SecurityContextHolder) or using the HttpSession attribute reserved for this class?
20240327 10:34:08.478 [http-nio-8080-exec-5] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Set SecurityContextHolder to anonymous SecurityContext
20240327 10:34:08.482 [http-nio-8080-exec-5] DEBUG o.s.s.w.DefaultRedirectStrategy - Redirecting to http://localhost:8080/login
20240327 10:34:08.490 [http-nio-8080-exec-6] DEBUG o.s.s.w.FilterChainProxy - Securing GET /login
Hi, @codesejin. Thanks for the report.
I see that there is a lot of configuration in your code snippets. Can you provide a minimal, reproducible sample that we can clone so we can be sure that we won't miss anything when debugging it?
Thank you for your response. Here is the GitHub link to my sample code.
I've recreated the code with minimal changes. Please let me know if there are any issues.
https://github.com/codesejin/security-test
When I debugged my code, it seemed like it wasn't accessing the HttpSessionSecurityContextRepository.
Hi @codesejin. Please refer to https://docs.spring.io/spring-session/reference/configuration/redis.html#serializing-session-using-json on how to configure Spring Session Redis to properly serialize sessions using JSON.
The error you are seeing is because Spring Session Redis does not know how to deserialize the session from Redis into a SecurityContext
object since there is no class information.
If you still think it is a bug in Spring Session, please provide a reproducible test or steps to reproduce the bug.