Merge branch '6.0.x'
This commit is contained in:
commit
04b3d07319
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -35,6 +35,8 @@ import org.springframework.security.web.authentication.logout.LogoutSuccessHandl
|
||||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||||
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
|
import org.springframework.security.web.context.SecurityContextRepository;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
@ -326,6 +328,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
*/
|
*/
|
||||||
private LogoutFilter createLogoutFilter(H http) {
|
private LogoutFilter createLogoutFilter(H http) {
|
||||||
this.contextLogoutHandler.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
this.contextLogoutHandler.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
||||||
|
this.contextLogoutHandler.setSecurityContextRepository(getSecurityContextRepository(http));
|
||||||
this.logoutHandlers.add(this.contextLogoutHandler);
|
this.logoutHandlers.add(this.contextLogoutHandler);
|
||||||
this.logoutHandlers.add(postProcess(new LogoutSuccessEventPublishingLogoutHandler()));
|
this.logoutHandlers.add(postProcess(new LogoutSuccessEventPublishingLogoutHandler()));
|
||||||
LogoutHandler[] handlers = this.logoutHandlers.toArray(new LogoutHandler[0]);
|
LogoutHandler[] handlers = this.logoutHandlers.toArray(new LogoutHandler[0]);
|
||||||
|
@ -337,6 +340,14 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SecurityContextRepository getSecurityContextRepository(H http) {
|
||||||
|
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
|
||||||
|
if (securityContextRepository == null) {
|
||||||
|
securityContextRepository = new HttpSessionSecurityContextRepository();
|
||||||
|
}
|
||||||
|
return securityContextRepository;
|
||||||
|
}
|
||||||
|
|
||||||
private RequestMatcher getLogoutRequestMatcher(H http) {
|
private RequestMatcher getLogoutRequestMatcher(H http) {
|
||||||
if (this.logoutRequestMatcher != null) {
|
if (this.logoutRequestMatcher != null) {
|
||||||
return this.logoutRequestMatcher;
|
return this.logoutRequestMatcher;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.web.configurers;
|
package org.springframework.security.config.annotation.web.configurers;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.http.HttpHeaders;
|
import org.apache.http.HttpHeaders;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
@ -25,6 +27,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.mock.web.MockHttpSession;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
@ -39,6 +43,8 @@ import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.RememberMeServices;
|
import org.springframework.security.web.authentication.RememberMeServices;
|
||||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||||
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
|
import org.springframework.security.web.context.SecurityContextRepository;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||||
|
@ -48,6 +54,7 @@ import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||||
|
@ -324,6 +331,80 @@ public class LogoutConfigurerTests {
|
||||||
this.mvc.perform(post("/logout").with(csrf())).andExpect(status().isNotFound());
|
this.mvc.perform(post("/logout").with(csrf())).andExpect(status().isNotFound());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void logoutWhenCustomSecurityContextRepositoryThenUses() throws Exception {
|
||||||
|
CustomSecurityContextRepositoryConfig.repository = mock(SecurityContextRepository.class);
|
||||||
|
this.spring.register(CustomSecurityContextRepositoryConfig.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder logoutRequest = post("/logout")
|
||||||
|
.with(csrf())
|
||||||
|
.with(user("user"))
|
||||||
|
.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE);
|
||||||
|
this.mvc.perform(logoutRequest)
|
||||||
|
.andExpect(status().isFound())
|
||||||
|
.andExpect(redirectedUrl("/login?logout"));
|
||||||
|
// @formatter:on
|
||||||
|
int invocationCount = 2; // 1 from user() post processor and 1 from
|
||||||
|
// SecurityContextLogoutHandler
|
||||||
|
verify(CustomSecurityContextRepositoryConfig.repository, times(invocationCount)).saveContext(any(),
|
||||||
|
any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void logoutWhenNoSecurityContextRepositoryThenHttpSessionSecurityContextRepository() throws Exception {
|
||||||
|
this.spring.register(InvalidateHttpSessionFalseConfig.class).autowire();
|
||||||
|
MockHttpSession session = mock(MockHttpSession.class);
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder logoutRequest = post("/logout")
|
||||||
|
.with(csrf())
|
||||||
|
.with(user("user"))
|
||||||
|
.session(session)
|
||||||
|
.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE);
|
||||||
|
this.mvc.perform(logoutRequest)
|
||||||
|
.andExpect(status().isFound())
|
||||||
|
.andExpect(redirectedUrl("/login?logout"))
|
||||||
|
.andReturn();
|
||||||
|
// @formatter:on
|
||||||
|
verify(session).removeAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class InvalidateHttpSessionFalseConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.logout((logout) -> logout.invalidateHttpSession(false))
|
||||||
|
.securityContext((context) -> context.requireExplicitSave(true));
|
||||||
|
return http.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class CustomSecurityContextRepositoryConfig {
|
||||||
|
|
||||||
|
static SecurityContextRepository repository;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.logout(Customizer.withDefaults())
|
||||||
|
.securityContext((context) -> context
|
||||||
|
.requireExplicitSave(true)
|
||||||
|
.securityContextRepository(repository)
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class NullLogoutSuccessHandlerConfig {
|
static class NullLogoutSuccessHandlerConfig {
|
||||||
|
|
|
@ -12,6 +12,7 @@ The default is that accessing the URL `/logout` logs the user out by:
|
||||||
- Invalidating the HTTP Session
|
- Invalidating the HTTP Session
|
||||||
- Cleaning up any RememberMe authentication that was configured
|
- Cleaning up any RememberMe authentication that was configured
|
||||||
- Clearing the `SecurityContextHolder`
|
- Clearing the `SecurityContextHolder`
|
||||||
|
- Clearing the `SecurityContextRepository`
|
||||||
- Redirecting to `/login?logout`
|
- Redirecting to `/login?logout`
|
||||||
|
|
||||||
Similar to configuring login capabilities, however, you also have various options to further customize your logout requirements:
|
Similar to configuring login capabilities, however, you also have various options to further customize your logout requirements:
|
||||||
|
|
|
@ -27,6 +27,8 @@ import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||||
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
|
import org.springframework.security.web.context.SecurityContextRepository;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,6 +55,8 @@ public class SecurityContextLogoutHandler implements LogoutHandler {
|
||||||
|
|
||||||
private boolean clearAuthentication = true;
|
private boolean clearAuthentication = true;
|
||||||
|
|
||||||
|
private SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requires the request to be passed in.
|
* Requires the request to be passed in.
|
||||||
* @param request from which to obtain a HTTP session (cannot be null)
|
* @param request from which to obtain a HTTP session (cannot be null)
|
||||||
|
@ -76,6 +80,8 @@ public class SecurityContextLogoutHandler implements LogoutHandler {
|
||||||
if (this.clearAuthentication) {
|
if (this.clearAuthentication) {
|
||||||
context.setAuthentication(null);
|
context.setAuthentication(null);
|
||||||
}
|
}
|
||||||
|
SecurityContext emptyContext = this.securityContextHolderStrategy.createEmptyContext();
|
||||||
|
this.securityContextRepository.saveContext(emptyContext, request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInvalidateHttpSession() {
|
public boolean isInvalidateHttpSession() {
|
||||||
|
@ -114,4 +120,14 @@ public class SecurityContextLogoutHandler implements LogoutHandler {
|
||||||
this.clearAuthentication = clearAuthentication;
|
this.clearAuthentication = clearAuthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link SecurityContextRepository} to use. Default is
|
||||||
|
* {@link HttpSessionSecurityContextRepository}.
|
||||||
|
* @param securityContextRepository the {@link SecurityContextRepository} to use.
|
||||||
|
*/
|
||||||
|
public void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
|
||||||
|
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
|
||||||
|
this.securityContextRepository = securityContextRepository;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -149,13 +149,46 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
|
||||||
SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = WebUtils.getNativeResponse(response,
|
SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = WebUtils.getNativeResponse(response,
|
||||||
SaveContextOnUpdateOrErrorResponseWrapper.class);
|
SaveContextOnUpdateOrErrorResponseWrapper.class);
|
||||||
if (responseWrapper == null) {
|
if (responseWrapper == null) {
|
||||||
boolean httpSessionExists = request.getSession(false) != null;
|
saveContextInHttpSession(context, request);
|
||||||
SecurityContext initialContext = this.securityContextHolderStrategy.createEmptyContext();
|
return;
|
||||||
responseWrapper = new SaveToSessionResponseWrapper(response, request, httpSessionExists, initialContext);
|
|
||||||
}
|
}
|
||||||
responseWrapper.saveContext(context);
|
responseWrapper.saveContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveContextInHttpSession(SecurityContext context, HttpServletRequest request) {
|
||||||
|
if (isTransient(context) || isTransient(context.getAuthentication())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SecurityContext emptyContext = generateNewContext();
|
||||||
|
if (emptyContext.equals(context)) {
|
||||||
|
HttpSession session = request.getSession(false);
|
||||||
|
removeContextFromSession(context, session);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
boolean createSession = this.allowSessionCreation;
|
||||||
|
HttpSession session = request.getSession(createSession);
|
||||||
|
setContextInSession(context, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setContextInSession(SecurityContext context, HttpSession session) {
|
||||||
|
if (session != null) {
|
||||||
|
session.setAttribute(this.springSecurityContextKey, context);
|
||||||
|
if (this.logger.isDebugEnabled()) {
|
||||||
|
this.logger.debug(LogMessage.format("Stored %s to HttpSession [%s]", context, session));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeContextFromSession(SecurityContext context, HttpSession session) {
|
||||||
|
if (session != null) {
|
||||||
|
session.removeAttribute(this.springSecurityContextKey);
|
||||||
|
if (this.logger.isDebugEnabled()) {
|
||||||
|
this.logger.debug(LogMessage.format("Removed %s from HttpSession [%s]", context, session));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsContext(HttpServletRequest request) {
|
public boolean containsContext(HttpServletRequest request) {
|
||||||
HttpSession session = request.getSession(false);
|
HttpSession session = request.getSession(false);
|
||||||
|
@ -392,11 +425,8 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
|
||||||
// We may have a new session, so check also whether the context attribute
|
// We may have a new session, so check also whether the context attribute
|
||||||
// is set SEC-1561
|
// is set SEC-1561
|
||||||
if (contextChanged(context) || httpSession.getAttribute(springSecurityContextKey) == null) {
|
if (contextChanged(context) || httpSession.getAttribute(springSecurityContextKey) == null) {
|
||||||
httpSession.setAttribute(springSecurityContextKey, context);
|
HttpSessionSecurityContextRepository.this.saveContextInHttpSession(context, this.request);
|
||||||
this.isSaveContextInvoked = true;
|
this.isSaveContextInvoked = true;
|
||||||
if (this.logger.isDebugEnabled()) {
|
|
||||||
this.logger.debug(LogMessage.format("Stored %s to HttpSession [%s]", context, httpSession));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -27,12 +27,19 @@ import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
|
import org.springframework.security.web.context.SecurityContextRepository;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class SecurityContextLogoutHandlerTests {
|
public class SecurityContextLogoutHandlerTests {
|
||||||
|
|
||||||
|
@ -76,4 +83,35 @@ public class SecurityContextLogoutHandlerTests {
|
||||||
assertThat(beforeContext.getAuthentication()).isSameAs(beforeAuthentication);
|
assertThat(beforeContext.getAuthentication()).isSameAs(beforeAuthentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void logoutWhenSecurityContextRepositoryThenSaveEmptyContext() {
|
||||||
|
SecurityContextRepository repository = mock(SecurityContextRepository.class);
|
||||||
|
this.handler.setSecurityContextRepository(repository);
|
||||||
|
this.handler.logout(this.request, this.response, SecurityContextHolder.getContext().getAuthentication());
|
||||||
|
verify(repository).saveContext(eq(SecurityContextHolder.createEmptyContext()), any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void logoutWhenClearAuthenticationFalseThenSaveEmptyContext() {
|
||||||
|
SecurityContextRepository repository = mock(SecurityContextRepository.class);
|
||||||
|
this.handler.setSecurityContextRepository(repository);
|
||||||
|
this.handler.setClearAuthentication(false);
|
||||||
|
this.handler.logout(this.request, this.response, SecurityContextHolder.getContext().getAuthentication());
|
||||||
|
verify(repository).saveContext(eq(SecurityContextHolder.createEmptyContext()), any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWhenDefaultSecurityContextRepositoryThenHttpSessionSecurityContextRepository() {
|
||||||
|
SecurityContextRepository securityContextRepository = (SecurityContextRepository) ReflectionTestUtils
|
||||||
|
.getField(this.handler, "securityContextRepository");
|
||||||
|
assertThat(securityContextRepository).isInstanceOf(HttpSessionSecurityContextRepository.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setSecurityContextRepositoryWhenNullThenException() {
|
||||||
|
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||||
|
.isThrownBy(() -> this.handler.setSecurityContextRepository(null))
|
||||||
|
.withMessage("securityContextRepository cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -52,6 +52,7 @@ import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContextImpl;
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
import org.springframework.security.core.context.TransientSecurityContext;
|
import org.springframework.security.core.context.TransientSecurityContext;
|
||||||
|
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
@ -748,6 +749,53 @@ public class HttpSessionSecurityContextRepositoryTests {
|
||||||
assertThat(session).isNull();
|
assertThat(session).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void saveContextWhenSecurityContextEmptyThenRemoveAttributeFromSession() {
|
||||||
|
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
SecurityContext emptyContext = SecurityContextHolder.createEmptyContext();
|
||||||
|
MockHttpSession session = (MockHttpSession) request.getSession(true);
|
||||||
|
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, emptyContext);
|
||||||
|
repo.saveContext(emptyContext, request, response);
|
||||||
|
Object attributeAfterSave = session
|
||||||
|
.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
|
||||||
|
assertThat(attributeAfterSave).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void saveContextWhenSecurityContextEmptyAndNoSessionThenDoesNotCreateSession() {
|
||||||
|
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
SecurityContext emptyContext = SecurityContextHolder.createEmptyContext();
|
||||||
|
repo.saveContext(emptyContext, request, response);
|
||||||
|
assertThat(request.getSession(false)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void saveContextWhenSecurityContextThenSaveInSession() {
|
||||||
|
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
SecurityContext context = createSecurityContext(PasswordEncodedUser.user());
|
||||||
|
repo.saveContext(context, request, response);
|
||||||
|
Object savedContext = request.getSession()
|
||||||
|
.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
|
||||||
|
assertThat(savedContext).isEqualTo(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void saveContextWhenTransientAuthenticationThenDoNotSave() {
|
||||||
|
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||||
|
context.setAuthentication(new SomeTransientAuthentication());
|
||||||
|
repo.saveContext(context, request, response);
|
||||||
|
assertThat(request.getSession(false)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
private SecurityContext createSecurityContext(UserDetails userDetails) {
|
private SecurityContext createSecurityContext(UserDetails userDetails) {
|
||||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(userDetails,
|
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(userDetails,
|
||||||
userDetails.getPassword(), userDetails.getAuthorities());
|
userDetails.getPassword(), userDetails.getAuthorities());
|
||||||
|
|
Loading…
Reference in New Issue