最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

maven - Unable to send flash objects when redirecting if Spring session were to be saved on Redis - Stack Overflow

programmeradmin0浏览0评论

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
Add a comment  | 

1 Answer 1

Reset to default 0

It 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.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论