1) Description of the bug
I can login but not logout.
2) Steps that I perform and that show how to get the error
I add this code to login.html:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
I add this code to the Spring Security configuration class:
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
//http.csrf().csrfTokenRepository(new HttpSessionCsrfTokenRepository());
3) What I expect to see I expect to log in and log out. I expect not to type the password every time I log into the site's control panel and to enjoy CSRF protection. 4) Code I added my example to this link: Protect my application from CSRF attacks with Spring Boot 2.6.X and JDK 1.11.XX in a persistent login application I add all the code of the classes below as well:
// 3) ?????????????
// Configurazione di Spring Security.
@Configuration
@EnableWebSecurity
public class ConfigurazioneSpringSecurity extends WebSecurityConfigurerAdapter {
// Metodi per la gestione dell'utente autenticato.
@Autowired
GestioneUtentiSpringSecurity gestioneUtentiSpringSecurity;
// Metodo per crittografare la password
@Bean
public static BCryptPasswordEncoder metodoCrittografia() {
return new BCryptPasswordEncoder();
}
@Autowired
public void crittografiaPassword(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(gestioneUtentiSpringSecurity).passwordEncoder(metodoCrittografia());
}
@Autowired
private DataSource dataSource;
// 2) ?????????????
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
db.setDataSource(dataSource);
return db;
}
// Se l'utente cambia la password il pulsante "Ricordami" continua a funzionare.
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
// Configurazione di Spring Security.
@Override
protected void configure(HttpSecurity http) throws Exception {
// 1) ?????????????
//http.csrf().disable();
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
//http.csrf().csrfTokenRepository(new HttpSessionCsrfTokenRepository());
http.authorizeRequests().antMatchers(
"/",
"/login",
"/benvenuto",
"/registrazione",
"/registrazione-eseguita",
"/pagine-applicazione"
).permitAll();
http.authorizeRequests().antMatchers("/area-riservata")
.access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(1L) + "')");
// ... ecc...
http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/errore-403");
http.authorizeRequests().and().formLogin()
.loginProcessingUrl("/pagina-login")
.loginPage("/login")
.defaultSuccessUrl("/")
.failureUrl("/login?errore=true")
.usernameParameter("username")
.passwordParameter("password")
.and().logout().logoutUrl("/pagina-logout")
.logoutSuccessUrl("/login?logout=true");
// 2) ?????????????
http.authorizeRequests().and()
.rememberMe().tokenRepository(this.persistentTokenRepository())
.tokenValiditySeconds(365 * 24 * 60 * 60);
}
}
// 3) ?????????????
// Creazione utente autenticato.
@Service
public class GestioneUtentiSpringSecurity implements UserDetailsService {
@Autowired
private UtenteRepository utenteRepository;
@Autowired
private RuoloRepository ruoloRepository;
@Autowired
//...
// Creazione utente autenticato
@Override
public UserDetails loadUserByUsername(String nomeUtente) throws UsernameNotFoundException {
// Si cerca l'utente nel database
Utente utente = trovaUtenteConPrivilegiDiAutenticazione(nomeUtente);
if (utente == null) {
// System.out.println("L'utente " + nomeUtente + " non è stato trovato!");
throw new UsernameNotFoundException("L'utente " + nomeUtente + " non è stato trovato nel database.");
}
// Si cercano i ruoli dell'utente
List<String> ruoliUtente = null;
try {
ruoliUtente = this.ruoloRepository.trovaRuoliUtente(utente.getId());
}catch (Exception b){
ruoliUtente = null;
}
// Si caricano in una lista di oggetti GrantedAuthority i ruoli di un dato utente.
// GrantedAuthority è una classe di Spring Security che contiene i privilegi di un utente.
List<GrantedAuthority> grantList = null;
try{
grantList = new ArrayList<GrantedAuthority>();
if (ruoliUtente != null) {
for (String ruolo : ruoliUtente) {
GrantedAuthority authority = new SimpleGrantedAuthority(ruolo);
grantList.add(authority);
}
}
}catch (Exception c){
grantList = null;
}
// Si crea un oggetto specifico di Spring Security che rappresenta l'utente autenticato. Questo oggetto contiene 3
// informazioni: nome utente, password e privilegi. Questi ultimi, in questa applicazione si fanno coincidere
// con i ruoli.
UserDetails userDetails = null;
if((utente != null) && (ruoliUtente != null) && (grantList != null)){
userDetails = (UserDetails) new User(utente.getNome(), utente.getPassword(), grantList);
}
return userDetails;
}
// Si cerca l'utente nel database
public Utente trovaUtenteConPrivilegiDiAutenticazione(String nomeUtente){
// ...
}
}
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.5</version>
<relativePath/> <!--/* cerca genitore dal repository */-->
</parent>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<packaging>war</packaging>
<name>...</name>
<description>...</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.1</version>
</dependency>
</dependencies>
<build>
<finalName>gestioneutenti</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<packagingExcludes>
WEB-INF/classes/it/...
</packagingExcludes>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
@Controller
public class ControlloPagineWeb {
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String paginaLogin(
Model model,
Principal principal,
HttpSession session,
HttpServletRequest request,
HttpServletResponse response
) {
variabiliGeneraliPerPaginaHTML(principal, model, session, request, response);
model.addAttribute("titolo", "Login");
model.addAttribute("messaggio", "Login");
return "login";
}
@RequestMapping(value = "/logout-eseguito", method = RequestMethod.GET)
public String logoutEseguitoConSuccesso(
Model model,
Principal principal,
HttpSession session,
HttpServletRequest request,
HttpServletResponse response
) {
variabiliGeneraliPerPaginaHTML(principal, model, session, request, response);
model.addAttribute("titolo", "Login");
model.addAttribute("messaggio", "Login");
model.addAttribute("logoutEseguito", "Logout eseguito correttamente.");
return "login";
}
}
D:\ALTRO\Java\jdk-11.0.13\bin\java.exe
...
21:23:02.945 [Thread-0] DEBUG org.springframework.boot.devtools.restart.classloader.RestartClassLoader - Created RestartClassLoader org.springframework.boot.devtools.restart.classloader.RestartClassLoader@6ad45cbe
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.5)
...
2022-03-29 21:23:09.245 ERROR 20124 --- [.1-8443-exec-10] org.apache.tomcat.util.net.NioEndpoint : Error running socket processor
java.lang.NullPointerException: null
at java.base/sun.security.ssl.HKDF.extract(HKDF.java:93) ~[na:na]
at java.base/sun.security.ssl.HKDF.extract(HKDF.java:119) ~[na:na]
at java.base/sun.security.ssl.ServerHello.setUpPskKD(ServerHello.java:1169) ~[na:na]
at java.base/sun.security.ssl.ServerHello$T13ServerHelloProducer.produce(ServerHello.java:547) ~[na:na]
at java.base/sun.security.ssl.SSLHandshake.produce(SSLHandshake.java:436) ~[na:na]
at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.goServerHello(ClientHello.java:1234) ~[na:na]
at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.consume(ClientHello.java:1170) ~[na:na]
at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.onClientHello(ClientHello.java:852) ~[na:na]
at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.consume(ClientHello.java:813) ~[na:na]
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392) ~[na:na]
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:478) ~[na:na]
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1081) ~[na:na]
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1068) ~[na:na]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1015) ~[na:na]
at org.apache.tomcat.util.net.SecureNioChannel.tasks(SecureNioChannel.java:429) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.net.SecureNioChannel.handshakeUnwrap(SecureNioChannel.java:493) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.net.SecureNioChannel.handshake(SecureNioChannel.java:217) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1719) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
Thanks for the support, I hope my report is useful
Comment From: eleftherias
Thanks for reaching out @fedegibut. I saw your post on Stack Overflow, let's continue the discussion there.