JAVA-12754: Moved 2 articles to new module spring-security-web-login-2
This commit is contained in:
parent
b7033fadfd
commit
0db8c73f75
@ -0,0 +1,9 @@
|
||||
## Spring Security Login - 2
|
||||
|
||||
This module contains articles about login/logout mechanisms with Spring Security.
|
||||
|
||||
## Relevant articles:
|
||||
|
||||
- [Manual Logout With Spring Security](https://www.baeldung.com/spring-security-manual-logout)
|
||||
- [How to Disable Spring Security Logout Redirects](https://www.baeldung.com/spring-security-disable-logout-redirects)
|
||||
- More articles: [[<-- prev]](/spring-security-modules/spring-security-web-login)
|
60
spring-security-modules/spring-security-web-login-2/pom.xml
Normal file
60
spring-security-modules/spring-security-web-login-2/pom.xml
Normal file
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-security-web-login-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>spring-security-web-login-2</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf.extras</groupId>
|
||||
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,13 @@
|
||||
package com.baeldung.logoutredirects;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class LogoutApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(LogoutApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.baeldung.logoutredirects.securityconfig;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests(authz -> authz.mvcMatchers("/login")
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.logout(logout -> logout.permitAll()
|
||||
.logoutSuccessHandler((request, response, authentication) -> {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.baeldung.manuallogout;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ManualLogoutApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ManualLogoutApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package com.baeldung.manuallogout;
|
||||
|
||||
import static org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter.Directive.CACHE;
|
||||
import static org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter.Directive.COOKIES;
|
||||
import static org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter.Directive.EXECUTION_CONTEXTS;
|
||||
import static org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter.Directive.STORAGE;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.authentication.logout.HeaderWriterLogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SimpleSecurityConfiguration {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(SimpleSecurityConfiguration.class);
|
||||
|
||||
@Order(4)
|
||||
@Configuration
|
||||
public static class LogoutOnRequestConfiguration extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.antMatcher("/request/**")
|
||||
.authorizeRequests(authz -> authz.anyRequest()
|
||||
.permitAll())
|
||||
.logout(logout -> logout.logoutUrl("/request/logout")
|
||||
.addLogoutHandler((request, response, auth) -> {
|
||||
try {
|
||||
request.logout();
|
||||
} catch (ServletException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@Order(3)
|
||||
@Configuration
|
||||
public static class DefaultLogoutConfiguration extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.antMatcher("/basic/**")
|
||||
.authorizeRequests(authz -> authz.anyRequest()
|
||||
.permitAll())
|
||||
.logout(logout -> logout.logoutUrl("/basic/basiclogout"));
|
||||
}
|
||||
}
|
||||
|
||||
@Order(2)
|
||||
@Configuration
|
||||
public static class AllCookieClearingLogoutConfiguration extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.antMatcher("/cookies/**")
|
||||
.authorizeRequests(authz -> authz.anyRequest()
|
||||
.permitAll())
|
||||
.logout(logout -> logout.logoutUrl("/cookies/cookielogout")
|
||||
.addLogoutHandler(new SecurityContextLogoutHandler())
|
||||
.addLogoutHandler((request, response, auth) -> {
|
||||
for (Cookie cookie : request.getCookies()) {
|
||||
String cookieName = cookie.getName();
|
||||
Cookie cookieToDelete = new Cookie(cookieName, null);
|
||||
cookieToDelete.setMaxAge(0);
|
||||
response.addCookie(cookieToDelete);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@Order(1)
|
||||
@Configuration
|
||||
public static class ClearSiteDataHeaderLogoutConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
private static final ClearSiteDataHeaderWriter.Directive[] SOURCE = { CACHE, COOKIES, STORAGE, EXECUTION_CONTEXTS };
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.antMatcher("/csd/**")
|
||||
.authorizeRequests(authz -> authz.anyRequest()
|
||||
.permitAll())
|
||||
.logout(logout -> logout.logoutUrl("/csd/csdlogout")
|
||||
.addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(SOURCE))));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
server.port=8081
|
||||
|
||||
logging.level.root=INFO
|
||||
|
||||
logging.level.org.springframework.security=DEBUG
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
@ -0,0 +1,34 @@
|
||||
package com.baeldung.logoutredirects;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@WebMvcTest()
|
||||
public class LogoutApplicationUnitTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@WithMockUser(value = "spring")
|
||||
@Test
|
||||
public void whenLogout_thenDisableRedirect() throws Exception {
|
||||
|
||||
this.mockMvc.perform(post("/logout").with(csrf()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$").doesNotExist())
|
||||
.andExpect(unauthenticated())
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package com.baeldung.manuallogout;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@WebMvcTest()
|
||||
public class ManualLogoutIntegrationTest {
|
||||
|
||||
private static final String CLEAR_SITE_DATA_HEADER = "Clear-Site-Data";
|
||||
public static final int EXPIRY = 60 * 10;
|
||||
public static final String COOKIE_NAME = "customerName";
|
||||
public static final String COOKIE_VALUE = "myName";
|
||||
public static final String ATTRIBUTE_NAME = "att";
|
||||
public static final String ATTRIBUTE_VALUE = "attvalue";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@WithMockUser(value = "spring")
|
||||
@Test
|
||||
public void givenLoggedUserWhenUserLogoutThenSessionClearedAndNecessaryCookieCleared() throws Exception {
|
||||
|
||||
this.mockMvc.perform(post("/basic/basiclogout").secure(true)
|
||||
.with(csrf()))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(unauthenticated())
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@WithMockUser(value = "spring")
|
||||
@Test
|
||||
public void givenLoggedUserWhenUserLogoutThenSessionClearedAndAllCookiesCleared() throws Exception {
|
||||
|
||||
MockHttpSession session = new MockHttpSession();
|
||||
session.setAttribute(ATTRIBUTE_NAME, ATTRIBUTE_VALUE);
|
||||
|
||||
Cookie randomCookie = new Cookie(COOKIE_NAME, COOKIE_VALUE);
|
||||
randomCookie.setMaxAge(EXPIRY); // 10 minutes
|
||||
|
||||
MockHttpServletRequest requestStateAfterLogout = this.mockMvc.perform(post("/cookies/cookielogout").secure(true)
|
||||
.with(csrf())
|
||||
.session(session)
|
||||
.cookie(randomCookie))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(unauthenticated())
|
||||
.andExpect(cookie().maxAge(COOKIE_NAME, 0))
|
||||
.andReturn()
|
||||
.getRequest();
|
||||
|
||||
HttpSession sessionStateAfterLogout = requestStateAfterLogout.getSession();
|
||||
assertNull(sessionStateAfterLogout.getAttribute(ATTRIBUTE_NAME));
|
||||
}
|
||||
|
||||
@WithMockUser(value = "spring")
|
||||
@Test
|
||||
public void givenLoggedUserWhenUserLogoutThenClearDataSiteHeaderPresent() throws Exception {
|
||||
|
||||
this.mockMvc.perform(post("/csd/csdlogout").secure(true)
|
||||
.with(csrf()))
|
||||
.andDo(print())
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(header().exists(CLEAR_SITE_DATA_HEADER))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@WithMockUser(value = "spring")
|
||||
@Test
|
||||
public void givenLoggedUserWhenUserLogoutOnRequestThenSessionCleared() throws Exception {
|
||||
|
||||
this.mockMvc.perform(post("/request/logout").secure(true)
|
||||
.with(csrf()))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(unauthenticated())
|
||||
.andReturn();
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="15 seconds" debug="false">
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>[%d{ISO8601}]-[%thread] %-5level %logger - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
Loading…
x
Reference in New Issue
Block a user