2015-03-06 14 views
6

Verwenden von pure Spring Java Config Ich habe Probleme mit Spring und CAS, um Single Sign Out durchzuführen. Ich habe Single Sign On, das mit der folgenden Konfiguration arbeitet. Ich verwende eine einfache JSP-Seite, um einen Formular-POST an die URL https://nginx.shane.com/app/logout zu machen, und ich füge den CSRF-Wert in die POST-Daten ein. Alles scheint fehlerfrei zu funktionieren, aber wenn ich auf eine gesicherte Seite gehe, lässt es mich einfach rein, ohne dass ich mich anmelden muss. Irgendwelche Ideen?Einmaliges Abmelden mit Spring Security und CAS

@Configuration 
@EnableWebSecurity 
public class SecurityWebAppConfig extends WebSecurityConfigurerAdapter { 

@Bean 
protected ServiceProperties serviceProperties() { 
    ServiceProperties serviceProperties = new ServiceProperties(); 
    serviceProperties.setService("https://nginx.shane.com/app/j_spring_cas_security_check"); 
    serviceProperties.setSendRenew(false); 
    return serviceProperties; 
} 

@Bean 
public CasAuthenticationProvider casAuthenticationProvider() { 
    CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider(); 
    casAuthenticationProvider.setAuthenticationUserDetailsService(authenticationUserDetailsService()); 
    casAuthenticationProvider.setServiceProperties(serviceProperties()); 
    casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator()); 
    casAuthenticationProvider.setKey("an_id_for_this_auth_provider_only"); 
    return casAuthenticationProvider; 
} 

@Bean 
public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService() { 
    return new TestCasAuthenticationUserDetailsService(); 
} 

@Bean 
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() { 
    return new Cas20ServiceTicketValidator("https://nginx.shane.com/cas"); 
} 

@Bean 
public CasAuthenticationFilter casAuthenticationFilter() throws Exception { 
    CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter(); 
    casAuthenticationFilter.setAuthenticationManager(authenticationManager()); 
    return casAuthenticationFilter; 
} 

@Bean 
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() { 
    CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint(); 
    casAuthenticationEntryPoint.setLoginUrl("https://nginx.shane.com/cas/login"); 
    casAuthenticationEntryPoint.setServiceProperties(serviceProperties()); 

    return casAuthenticationEntryPoint; 
} 

@Bean 
public SingleSignOutFilter singleSignOutFilter() { 
    // This filter handles a Single Logout Request from the CAS Server 
    return new SingleSignOutFilter(); 
} 

@Bean 
public LogoutFilter requestLogoutFilter() { 
    // This filter redirects to the CAS Server to signal Single Logout should be performed 
    SecurityContextLogoutHandler handler = new SecurityContextLogoutHandler(); 
    handler.setClearAuthentication(true); 
    handler.setInvalidateHttpSession(true); 

    LogoutFilter logoutFilter = new LogoutFilter("https://nginx.shane.com/", handler); 
    return logoutFilter; 
} 

@Override 
protected void configure(HttpSecurity http) throws Exception { 
    http.addFilter(casAuthenticationFilter()); 
    http.addFilterBefore(requestLogoutFilter(), LogoutFilter.class); 
    http.addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class); 

    http.exceptionHandling() 
     .authenticationEntryPoint(casAuthenticationEntryPoint()); 

    http.authorizeRequests() 
     .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") 
     .antMatchers("/dba/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_DBA')"); 

    http.logout() 
     .addLogoutHandler(handler) 
     .deleteCookies("remove") 
     .invalidateHttpSession(true) 
     .logoutUrl("/logout") 
     .logoutSuccessUrl("/"); 
} 

@Override 
protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
    auth.authenticationProvider(casAuthenticationProvider()); 
} 

}

ich auch eine WebListener hinzugefügt, um die Sitzung zerstört Ereignis zu behandeln:

@WebListener 
public class SecurityWebListener implements HttpSessionListener { 

private SingleSignOutHttpSessionListener listener = new SingleSignOutHttpSessionListener(); 

@Override 
public void sessionCreated(HttpSessionEvent se) { 
    listener.sessionCreated(se); 
} 

@Override 
public void sessionDestroyed(HttpSessionEvent se) { 
    listener.sessionDestroyed(se); 
} 
} 

Hier ist die Protokollausgabe

[org.springframework.security.web.FilterChainProxy] [/logout at position 6 of 14 in additional filter chain; firing Filter: 'LogoutFilter'] [] 
[org.springframework.security.web.util.matcher.AntPathRequestMatcher] [Checking match of request : '/logout'; against '/logout'] [] 
[org.springframework.security.web.authentication.logout.LogoutFilter] [Logging out user 'org.spr[email protected]836ad34b: Principal: [email protected]: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]fffdaa08: RemoteIpAddress: 127.0.0.1; SessionId: FA880C15EF09C033E1CA0C8E4785905F; Granted Authorities: ROLE_ADMIN Assertion: [email protected] Credentials (Service/Proxy Ticket): ST-23-1UandqRxBcG6HCTx0Pdd-cas01.example.org' and transferring to logout destination] [] 
[org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler] [Invalidating session: FA880C15EF09C033E1CA0C8E4785905F] [] 
[org.jasig.cas.client.session.HashMapBackedSessionMappingStorage] [Attempting to remove Session=[FA880C15EF09C033E1CA0C8E4785905F]] [] 
[org.jasig.cas.client.session.HashMapBackedSessionMappingStorage] [Found mapping for session. Session Removed.] [] 
[org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler] [Using default Url: /] [] 
[org.springframework.security.web.DefaultRedirectStrategy] [Redirecting to '/app/'] [] 

Antwort

3

(Un) Glück, dass ich ähnliches Problem hatte ;) Es tritt auf, wenn CAS versucht, Ihre Anwendung anzumelden, um sich abzumelden. Auf der einen Seite versucht CAS, sessionId zu übergeben, um Abmeldung durchzuführen, auf der anderen Seite erwartet SpringSecurity CSRF-Token zu erhalten, das nicht von CAS gesendet wurde, da es nur GET-Anfrage sendet. CsrfFilter findet das CSRF-Token nicht und unterbricht die Filterkette. Der Benutzer ist sich dessen nicht bewusst, da CAS die Abmeldeanforderung implizit aufruft. Die Anfrage geht direkt vom CAS-Server zum Anwendungsserver, nicht durch Umleiten des Benutzers im Webbrowser.

Um Sie HttpSecurity konfigurieren müssen, damit es funktioniert auszuschließen/nicht LogoutFilter filterProcessesUrl (die j_spring_security_logout in Ihrem Fall, wie Sie den Standard einen verwenden) aufzunehmen.

Angenommen, Sie wollen CSRF überprüfen, wenn sie versuchen neue Admin zu erstellen, für insatnce, müssen Sie es wie folgt konfigurieren:

@Override 
protected void configure(HttpSecurity http) throws Exception { 
    http.addFilter(casAuthenticationFilter()); 
    http.addFilterBefore(requestLogoutFilter(), LogoutFilter.class); 
    http.addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class); 

    http.exceptionHandling() 
     .authenticationEntryPoint(casAuthenticationEntryPoint()); 

    http.authorizeRequests() 
     .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") 
     .antMatchers("/dba/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_DBA')"); 

    http.csrf() 
     .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/admin/create")); 

    http.logout() 
     .addLogoutHandler(handler) 
     .deleteCookies("remove") 
     .invalidateHttpSession(true) 
     .logoutUrl("/logout") 
     .logoutSuccessUrl("/"); 
} 

Nur um anzuzeigen, ich habe hinzugefügt

http.csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/admin/create")); .

Achten Sie darauf, dass Sie nicht alle Muster zuordnen können (/ admin/**), da Sie vermutlich auch einige get-Anfragen aufrufen und der CSRF-Filter erwartet, dass sie das Token dann senden.

Dieses Problem tritt bei Spring Security vor 3.2.x nicht auf, da die CSRF-Unterstützung (Cross Site Request Forgery) dort eingeführt wurde.

Hoffe, dass diese Hilfe :)

+0

Danke, das geholfen hat. –