首页 > 解决方案 > Spring Security,注销:将参数从 /logout 传递到 /login

问题描述

我正在使用默认的 Spring Security 来处理注销/登录。我有一个控制器方法来处理/login.

当我注销时,我看到 Spring Security 将我重定向到app/login?logout. 这个 Spring-created 参数的存在(有时也存在app/login?error)允许我将我的登录处理程序编写为:

@GetMapping("/participant/login")
public ModelAndView  loginPage(HttpServletRequest request, HttpServletResponse response, 
        @RequestParam(value = "error", required = false) String error,
        @RequestParam(value = "logout", required = false) String logout) {
    log.info("Entering participant login page");
    ModelAndView mav = new ModelAndView(LOGIN_JSP);
    if (null != error) {
        // We're coming to the login page after an Error
        mav.addObject("info", "My generic error message");
    } else if(null != logout){
        // We're coming to the login page after a Logout
        mav.addObject("info", "My generic logout message");
    }
    // ...Otherwise, normal Login page, no extra information

现在的问题是,当我注销时,我需要将自定义参数传递给 /logout 并传递给 /login。目标是我需要接收一个参数/login,我可以像系统创建的errorlogout.

假设这个自定义参数是exitMsg.

从我的应用程序中,我发出这个 Spring Security Logout URL(注销是自动的,所以我没有特定的处理程序):

myapp.com/app/logout?exitMsg=MyMessage

马上,登录处理程序丢失了这个参数,而我没有它。

我考虑编写自己的/logout处理程序,在其中手动注销(使会话无效),然后使用此参数重定向到自己登录。这是这里的建议。但如果我这样做,我将失去获取 Spring 的自动 ?logout?error请求参数的能力。在自动场景中,我得到了它们,现在我没有。我只得到我自己指定的自定义参数。我需要保留?logout?error测试我自己的新参数。

任何想法高度赞赏。

弹簧安全配置:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/participant/**").authorizeRequests()
                .antMatchers("/participant/id/**").permitAll()
                .antMatchers("/participant/faq").permitAll()
                .antMatchers("/participant/forgetPassword").permitAll()
                .antMatchers("/participant/securityQuestions").permitAll()
                .antMatchers("/participant/securityCheck").permitAll()
                .antMatchers("/participant/resetPassword").permitAll()
                .antMatchers("/participant/**").authenticated()
            .and()
                .formLogin().loginPage("/participant/login").permitAll()
                .failureUrl("/participant/login?error").permitAll()
                .defaultSuccessUrl("/participant/home")
                .usernameParameter("username").passwordParameter("password")
            .and()
                .logout().logoutUrl("/participant/logout")
                .logoutSuccessUrl("/participant/login?logout").permitAll()
            .and()
                .csrf().disable();
    }

标签: javaspringspring-bootspring-mvcspring-security

解决方案


您需要logoutSuccessHandler代替如下所示的.logoutSuccessUrl("/login?logout")
配置logoutSuccessHandler

@Override
protected void configure(final HttpSecurity http) throws Exception
{
    http
        .authorizeRequests()
            .antMatchers("/resources/**", "/", "/login", "/api/**")
                .permitAll()
            .antMatchers("/app/admin/*")
                .hasRole("ADMIN")
            .antMatchers("/app/user/*")
                .hasAnyRole("ADMIN", "USER")
        .and().exceptionHandling().accessDeniedPage("/403")
        .and().formLogin()
            .loginPage("/login").usernameParameter("userName")
            .passwordParameter("password")
            .defaultSuccessUrl("/app/user/dashboard")
            .failureUrl("/login?error=true")
        .and().logout()
            .logoutSuccessHandler(new CustomLogoutSuccessHandler())
            .invalidateHttpSession(true)
        .and().csrf().disable();

    http.sessionManagement().maximumSessions(1).expiredUrl("/login?expired=true");
}

我考虑编写自己的 /logout 处理程序

实际上,这不是注销处理程序,而是注销发起程序。
使用 CustomLogoutSuccessHandler,您可以在其中获取请求参数,并且可以再次设置它,如下所示。

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;

public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler
{

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException
    {
        Cookie cookie = new Cookie("JSESSIONID", null);
        cookie.setPath(request.getContextPath());
        cookie.setMaxAge(0);
        response.addCookie(cookie);

        if(request.getParameter("expired") != null)
        {
            response.sendRedirect(request.getContextPath()+"/login?expired=true");
        }
        else
        {
            response.sendRedirect(request.getContextPath() + "/login?logout=true");
        }
    }
}

从我的应用程序中,我发出这个 Spring Security Logout URL(注销是自动的,所以我没有特定的处理程序)

servlet/spring security 中没有自动注销功能。只有我们能实现的是
1. 自动化客户端发送注销请求
2. 在服务器中我们可以设置会话的 maxInactiveInterval,这样可以通过删除 cookie/设置 cookie 的年龄到过去的日期来使会话失效。一旦会话因下一个请求而失效,spring 安全过滤器链中的过滤器之一会将其重定向到参数已过期的 /login 页面。/login?expired
如果您启动注销,spring security 将删除 cookie/使会话无效并使用参数注销重定向到 /login 页面。/login?logout
spring security中实现注销的配置有两种。

.and().logout()
.invalidateHttpSession(true)
//or
.deleteCookies("JSESSIONID")

编辑:看到OP的答案后。缺少信息在此处添加。OP 和我在临时答案中进行了长时间的聊天(该聊天和答案已被删除),我们在其中找到了LOGIN-WALL

“登录墙”是 bad* 配置的结果,您将在其中定义自定义登录页面,并将其配置为允许在身份验证后访问的资源。( access=isAuthenticated) 但来自 CustomLogoutSuccessHandler 如果我们在使会话弹簧安全阻止登录页面访问(401-Un Authorized)无效后重定向到登录页面,因为该页面仅允许经过身份验证的用户使用。在阻塞 spring security 之后,会处理重定向到配置中配置的页面。`.loginPage("/someloginpage")。它强制认为是注销发生正确,正确重定向到登录页面,但我在查询字符串中发送的参数没有进入登录页面。 如果我把这称为很难猜到的谜题,那也没什么错


推荐阅读