Spring Security 无法注销

Posted

技术标签:

【中文标题】Spring Security 无法注销【英文标题】:Spring security cannot logout 【发布时间】:2018-09-02 01:18:20 【问题描述】:

我在 Spring 启动应用程序中自定义实现了 Spring 安全性。所以我有我的依赖项,我有一个名为 SecurityImpl 的类,它为我实现了登录访问。 当我进入浏览器时,我被正确地要求使用警报登录。当我登录时,我可以正确访问我的 Spring 控制器的所有 @RequestMapping。但我始终保持登录状态。即使我从浏览器中删除了 JSESSIONID,当我发出另一个 http 请求时,我也会被允许并创建一个新的 JSESSIONID 并将其发送到我的浏览器。

奇怪的是,即使我第一次使用登录名访问,即使cookie是自动生成的,过期日期也是:1969-12-31T23:59:59.000Z

我试图使会话无效,从服务器中删除 cookie,以各种方式注销,但没有。登录后,我总是被允许。

这里是配置我的 Spring Security 的 SecurityImpl.java 类:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Configuration
@Component
public class SecurityImpl extends WebSecurityConfigurerAdapter implements AuthenticationProvider 

  public static final String ROLE_ADMIN = "ROLE_ADMIN";
  public static final String ROLE_USER = "ROLE_USER";

  @Autowired UtenteDao utenteDao;

  /* authentication provider part */

  @Override
  public Authentication authenticate(Authentication auth) throws AuthenticationException 

    String username = auth.getName();
    String password = auth.getCredentials().toString();
    String ruolo = "";

    Optional<Utente> utenteOptional = utenteDao.findByCodiceFiscaleAndPassword(username, password);

    if(utenteOptional.isPresent())
        ruolo = utenteOptional.get().getRuolo();
    
    if(ROLE_ADMIN.equals(ruolo)) 
        List<GrantedAuthority> grantedAuths = new ArrayList<>();
        grantedAuths.add(new SimpleGrantedAuthority(ROLE_USER));
        grantedAuths.add(new SimpleGrantedAuthority(ROLE_ADMIN));
        return new UsernamePasswordAuthenticationToken(username, password, grantedAuths);
     else if(ROLE_USER.equals(ruolo))
        List<GrantedAuthority> grantedAuths = new ArrayList<>();
        grantedAuths.add(new SimpleGrantedAuthority(ROLE_USER));
        return new UsernamePasswordAuthenticationToken(username, password, grantedAuths);
     else 
        throw new BadCredentialsException("Autenticazione fallita");
    
  

  @Override
  public boolean supports(Class<?> auth) 
    return auth.equals(UsernamePasswordAuthenticationToken.class);
  



  /* websecurity adapter part: erase it if you don't want login alert but default spring login web page */

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception 
    auth.authenticationProvider(this); //this because it is either a WebSecurityAdapter than an AuthenticationProvider
  

  @Override
  protected void configure(HttpSecurity http) throws Exception 
    http.authorizeRequests().anyRequest().authenticated()
            .and()
            .httpBasic()
            .and()
            .logout().clearAuthentication(true).logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/test")
            .deleteCookies("JSESSIONID")
            .invalidateHttpSession(true);
  

  /*  per non filtrare con il login alcuni path  */
  @Override
  public void configure(WebSecurity web) throws Exception 
    web.ignoring().antMatchers("/test");
  


它不起作用:当我转到 /logout 时,我被正确地重定向到 /test,但是当我要求一个禁止的路径时,我被允许无需登录。

然后我在@RestController 中尝试了一些解决方案:

@RequestMapping("/logout")
public String logoutPage (UsernamePasswordAuthenticationToken token) 
    token.eraseCredentials();
    token.setAuthenticated(false);
    SecurityContextHolder.getContext().setAuthentication(null);
    return "<h1>Logout effettuato con successo.</h1>";

然后我尝试了:

@RequestMapping(value = "/logout")
public String loadApp(HttpServletRequest request) 
    HttpSession session= request.getSession(false);
    SecurityContextHolder.clearContext();
    if(session != null) 
        session.invalidate();
    
    return "<h1>Logout effettuato con successo.</h1>";

然后,作为一个绝望的人,我尝试了:

@RequestMapping("/logout")
public String logoutDo(HttpServletRequest request)
    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 "<h1>Logout effettuato con successo.</h1>";

我尝试使用这些方法并暂时从浏览器中删除我的 cookie。我还尝试使用注释@PreAuthorize 对禁止的方法进行预授权,以防它们被允许(当您打开新浏览器时,在首次登录之前,即使没有@PreAuthorize,也不允许使用它们,但是当登录时,IS永远!)

【问题讨论】:

当注销 api 被命中时,您必须从令牌存储中删除或需要过期令牌。 你的意思是在浏览器中?我已经这样做了,你知道吗?它是用这个过期数据重新创建的:1969-12-31T23:59:59.000Z 您正在使用基本身份验证... 基本身份验证和注销将不起作用。浏览器在认证之后,总是会发送一个认证头。所以基本上你注销,每个请求你都会再次登录。 为了不使用基本认证需要修改什么? 【参考方案1】:

问题是没有使用 showForm()。没有它,是的,我将我的凭据插入到呈现给我的 javascript 警报中。但无法注销。

所以代码是这样变化的:

@Override
protected void configure(HttpSecurity http) throws Exception 

    http
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .and()
            .httpBasic()
            .and()
            .logout().clearAuthentication(true).logoutRequestMatcher(new AntPathRequestMatcher("/logout")) 
            .logoutSuccessUrl("/test") 
            .deleteCookies("JSESSIONID")
            .invalidateHttpSession(true);



【讨论】:

以上是关于Spring Security 无法注销的主要内容,如果未能解决你的问题,请参考以下文章

调用 j_spring_security_logout 不起作用

Spring Security - 无法注销[关闭]

带有 LDAP 注销的 Spring Security 无法删除会话

注销在Spring Security OAuth2中无法正常运行

使用 Spring MVC 和 Spring Security 进行 404 注销

Spring Security 注销和最大会话数