Spring Security注销不起作用 - 不清除安全上下文,并且经过身份验证的用户仍然存在

Str*_*uls 11 java spring spring-mvc spring-security

我知道,有很多关于这个主题的文章,但我有一个问题,我找不到任何解决方案.

我有一个经典的spring security java配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private AuctionAuthenticationProvider auctionAuthenticationProvider;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(auctionAuthenticationProvider);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic();

    ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequest = http.authorizeRequests();

    configureAdminPanelAccess(authorizeRequest);
    configureFrontApplicationAccess(authorizeRequest);
    configureCommonAccess(authorizeRequest);

    http.csrf()
        .csrfTokenRepository(csrfTokenRepository()).and()
        .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);

    http.logout()
        .clearAuthentication(true)
        .invalidateHttpSession(true);
}
...
}
Run Code Online (Sandbox Code Playgroud)

另外,我有两个控制器方法,我通过AJAX从我的Web应用程序登录/注销.

当我想注销时,我首先调用这个方法,我期望清除用户会话并清除安全上下文中的所有内容.

@Override
@RequestMapping(value = "/logout", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Boolean> logout(final HttpServletRequest request, final HttpServletResponse response) {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth != null){
        new SecurityContextLogoutHandler().logout(request, response, auth);
    }

    return new ResponseEntity<>(Boolean.TRUE, HttpStatus.OK);
}
Run Code Online (Sandbox Code Playgroud)

在此之后,我重新加载我的客户端Web应用程序,每次重新加载时,我通过调用以下控制器方法检查用户是否通过身份验证:

@Override
@RequestMapping(value = "/user", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<UserDetails> user() {
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    if (principal instanceof UserDetails) {
        return new ResponseEntity<>((UserDetails) principal, HttpStatus.OK);
    }

    return null;
}
Run Code Online (Sandbox Code Playgroud)

在这里,我会收到最后一个经过身份验证的用户.似乎在之前的注销方法中,Spring注销不起作用.

请记住,我尝试使用以下代码注销,但没有成功:

   @Override
   @RequestMapping(value = "/logout", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Boolean> logout(final HttpServletRequest request) {
     try {
         request.logout();

         return new ResponseEntity<>(Boolean.TRUE, HttpStatus.OK);
     } catch (ServletException ex) {
         if (LOG.isDebugEnabled()) {
             LOG.debug("There is a problem with the logout of the user", ex);
         }
    }
Run Code Online (Sandbox Code Playgroud)

您是否知道我在配置和注销过程中遗漏了什么?

Fre*_*ezY 33

从你的问题,我看到你正在尝试创建自己的注销,你也尝试使用默认的春季注销.我建议你应该选择一种方法,只是不要混合它们.有两种方法我可以告诉你从春天注销:

第一:默认的春季安全注销

.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/logout.done").deleteCookies("JSESSIONID")
.invalidateHttpSession(true) 
Run Code Online (Sandbox Code Playgroud)

从上面的示例中,您只需要/logout在注销用户时调用url.无需创建任何@Controller处理该注销,而不是spring将有助于将用户注销.您还可以在此添加您想要使其无效的其他内容.

第二:以编程方式注销

@RequestMapping(value = {"/logout"}, method = RequestMethod.POST)
public String logoutDo(HttpServletRequest request,HttpServletResponse response){
HttpSession session= request.getSession(false);
    SecurityContextHolder.clearContext();
         session= request.getSession(false);
        if(session != null) {
            session.invalidate();
        }
        for(Cookie cookie : request.getCookies()) {
            cookie.setMaxAge(0);
        }

    return "logout";
}
Run Code Online (Sandbox Code Playgroud)

如果您使用此注销,则不需要在spring security config中包含第一个方法.通过使用此方法,您可以在注销完成之前和之后添加额外的操作.但是,要使用此注销,只需调用/logoutURL,用户将手动注销.此方法将使会话无效,清除Spring安全上下文和cookie.

另外,对于第二种方法,如果您正在使用RequestMethod.POST,则需要将csrf键作为帖子包含在内.另一种方法是使用隐藏输入csrf键创建一个表单.这是使用jquery自动生成的注销链接的一些示例:

$("#Logout").click(function(){
    $form=$("<form>").attr({"action":"${pageContext.request.contextPath}"+"/logout","method":"post"})
    .append($("<input>").attr({"type":"hidden","name":"${_csrf.parameterName}","value":"${_csrf.token}"}))
    $("#Logout").append($form);
    $form.submit();
});
Run Code Online (Sandbox Code Playgroud)

您只需要创建超链接<a id="Logout">Logout</a>即可使用它.

如果您正在使用RequestMethod.GET,只需在您的链接中包含一个csrf键作为参数,如下所示:

<a href="${pageContext.request.contextPath}/logout?${_csrf.parameterName}=${_csrf.token}">Logout</a>
Run Code Online (Sandbox Code Playgroud)

多数民众赞成,希望得到它的帮助.

  • 您好,FreezY。实际上,您是对的-我混合了两种配置方式。我要做的是仅从与注销相关的SecurityConfig中删除代码,并保留控制器的当前实现。现在它可以正常工作了。谢谢 (2认同)