I'm performing a basic username and password check during login and if valid I will persist that session to a Redis server. I decided to use this approach because my requirements specify that the user must be kicked out from his other sessions when he logs in. Here is my pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.1</version>
<relativePath/>
</parent>
...
<dependencies>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
...
</project>
And here is my LoginController
:
@Controller
public class LoginController {
@GetMapping("login")
public String getLogin() {
return "login";
}
@PostMapping("login")
public RedirectView postLogin(@Valid @ModelAttribute LoginRequest loginRequest, HttpServletRequest request,
RedirectAttributes redirectAttributes) {
String alertMessage = null;
String username = loginRequest.getUsername();
User user = userService.selectByUsernameAndPassword(username, loginRequest.getPassword());
if (Objects.isNull(user)) {
alertMessage = "Invalid username/password."
} else if (user.getEnableFlag() != EnableToggle.ENABLE) {
alertMessage = "User account is disabled.";
}
if (StringUtils.isBlank(alertMessage)) {
HttpSession currentSession = request.getSession();
// Logic to kick the user from other devices goes here that uses .springframework.session:spring-session-data-redis:3.4.1 currentSession.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
return new RedirectView("/", true);
} else {
redirectAttributes.addFlashAttribute("username", username);
redirectAttributes.addFlashAttribute("alert", alertMessage);
return new RedirectView("/login", true);
}
}
}
It checks if the username and password match, and whether the account is enabled or not. If the check fails, it redirects back to the /login
page, flashing the respective alertMessage
using the redirectAttributes.addFlashAttribute
method.
My problem happens when I add this dependency in my pom.xml
. I need to add this dependency because the kicking of the user's previous sessions largely depends on this package.
<dependency>
<groupId>.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>3.4.1</version>
</dependency>
Now, if I visit the /login
page and attempt an invalid authentication, instead of redirecting back to /login
and flashing the alertMessage
, it encounters this exception:
Caused by: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class .springframework.web.servlet.FlashMap (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; .springframework.web.servlet.FlashMap is in unnamed module of loader 'app')
at .springframework.web.servlet.support.AbstractFlashMapManager.getExpiredFlashMaps(AbstractFlashMapManager.java:130) ~[spring-webmvc-6.2.1.jar:6.2.1]
at .springframework.web.servlet.support.AbstractFlashMapManager.retrieveAndUpdate(AbstractFlashMapManager.java:99) ~[spring-webmvc-6.2.1.jar:6.2.1]
at .springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-6.2.1.jar:6.2.1]
at .springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.2.1.jar:6.2.1]
... 42 common frames omitted
If I comment out the lines that use redirectAttributes.addFlashAttribute
, it will successfully redirect back to /login
if the authentication fails but will not be able to show the alertMessage
. What causes this?
I can use URL query strings when redirecting just to send the message as an alternative but I believe that's just evading the error.
I'm performing a basic username and password check during login and if valid I will persist that session to a Redis server. I decided to use this approach because my requirements specify that the user must be kicked out from his other sessions when he logs in. Here is my pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.1</version>
<relativePath/>
</parent>
...
<dependencies>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
...
</project>
And here is my LoginController
:
@Controller
public class LoginController {
@GetMapping("login")
public String getLogin() {
return "login";
}
@PostMapping("login")
public RedirectView postLogin(@Valid @ModelAttribute LoginRequest loginRequest, HttpServletRequest request,
RedirectAttributes redirectAttributes) {
String alertMessage = null;
String username = loginRequest.getUsername();
User user = userService.selectByUsernameAndPassword(username, loginRequest.getPassword());
if (Objects.isNull(user)) {
alertMessage = "Invalid username/password."
} else if (user.getEnableFlag() != EnableToggle.ENABLE) {
alertMessage = "User account is disabled.";
}
if (StringUtils.isBlank(alertMessage)) {
HttpSession currentSession = request.getSession();
// Logic to kick the user from other devices goes here that uses .springframework.session:spring-session-data-redis:3.4.1 currentSession.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
return new RedirectView("/", true);
} else {
redirectAttributes.addFlashAttribute("username", username);
redirectAttributes.addFlashAttribute("alert", alertMessage);
return new RedirectView("/login", true);
}
}
}
It checks if the username and password match, and whether the account is enabled or not. If the check fails, it redirects back to the /login
page, flashing the respective alertMessage
using the redirectAttributes.addFlashAttribute
method.
My problem happens when I add this dependency in my pom.xml
. I need to add this dependency because the kicking of the user's previous sessions largely depends on this package.
<dependency>
<groupId>.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>3.4.1</version>
</dependency>
Now, if I visit the /login
page and attempt an invalid authentication, instead of redirecting back to /login
and flashing the alertMessage
, it encounters this exception:
Caused by: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class .springframework.web.servlet.FlashMap (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; .springframework.web.servlet.FlashMap is in unnamed module of loader 'app')
at .springframework.web.servlet.support.AbstractFlashMapManager.getExpiredFlashMaps(AbstractFlashMapManager.java:130) ~[spring-webmvc-6.2.1.jar:6.2.1]
at .springframework.web.servlet.support.AbstractFlashMapManager.retrieveAndUpdate(AbstractFlashMapManager.java:99) ~[spring-webmvc-6.2.1.jar:6.2.1]
at .springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-6.2.1.jar:6.2.1]
at .springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.2.1.jar:6.2.1]
... 42 common frames omitted
If I comment out the lines that use redirectAttributes.addFlashAttribute
, it will successfully redirect back to /login
if the authentication fails but will not be able to show the alertMessage
. What causes this?
I can use URL query strings when redirecting just to send the message as an alternative but I believe that's just evading the error.
Share Improve this question edited Feb 2 at 22:23 President James K. Polk 42k28 gold badges109 silver badges145 bronze badges asked Feb 2 at 18:42 GideonGideon 1,5942 gold badges26 silver badges63 bronze badges 1- If you use ' spring-boot-starter-parent' than it does not make sense to define for each component it's own literal version number, because they are inherited from the parent... that means they can be removed.. – khmarbaise Commented Feb 4 at 8:40
1 Answer
Reset to default 0It looks like Redis
serialises and stores Flashmap
but deserialises it to LinkedHashMap
. You could check if the following works:
redirectAttributes.addFlashAttribute("username", String.valueOf(username));
redirectAttributes.addFlashAttribute("alert", String.valueOf(alertMessage));
A cleaner solution would be to use RedisSerializer
. Refer to this related SO link as well.