从我的应用程序注销时如何从 Saml IDP 注销
How to logout from Saml IDP when I logout from my application
我是 SAML 安全和 KEYCLOAK 的新手。
我在 KEYCLOAK 中有一个使用 SAML 协议的客户端。我的应用程序配置为此 SAML 客户端。当我登录我的应用程序时,SAML 身份验证成功,我可以登录我的应用程序。用户会话也在 keycloak 中创建。在执行注销操作时,用户只是从我的应用程序中注销,而不是从 SAML.The 用户中注销。
如何执行注销以便同时清除 saml 会话。我发现spring saml支持“/saml/logout”来清除session。但是这个 url 需要从浏览器中显式调用,然后必须再次从我的应用程序中执行注销。有没有办法一次执行这两个调用。
下面是我的 samlSecurityconfig java 代码:
@Bean
public FilterChainProxy samlFilter() throws Exception {
List<SecurityFilterChain> chains = new ArrayList<>();
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"),
samlEntryPoint()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"),
samlLogoutFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
metadataDisplayFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
samlWebSSOProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
samlWebSSOHoKProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
samlLogoutProcessingFilter()));
return new FilterChainProxy(chains);
}
// Handler for successful logout
@Bean
public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
SimpleUrlLogoutSuccessHandler successLogoutHandler = new CustomSimpleUrlLogoutSuccessHandler();
successLogoutHandler.setDefaultTargetUrl(config().getSp().getEntityBaseURL());
return successLogoutHandler;
}
// Logout handler terminating local session
@Bean
public SecurityContextLogoutHandler logoutHandler() {
SecurityContextLogoutHandler logoutHandler =
new SecurityContextLogoutHandler();
logoutHandler.setInvalidateHttpSession(true);
logoutHandler.setClearAuthentication(true);
return logoutHandler;
}
// Filter processing incoming logout messages
// First argument determines URL user will be redirected to after successful
// global logout
@Bean
public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() {
return new SAMLLogoutProcessingFilter(successLogoutHandler(),
logoutHandler());
}
// Overrides default logout processing filter with the one processing SAML
// messages
@Bean
public SAMLLogoutFilter samlLogoutFilter() {
return new SAMLLogoutFilter(successLogoutHandler(),
new LogoutHandler[]{logoutHandler()},
new LogoutHandler[]{logoutHandler()});
}
@Bean
public FilterChainProxy samlFilter() throws Exception {
List<SecurityFilterChain> chains = new ArrayList<>();
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"),
samlEntryPoint()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"),
samlLogoutFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
metadataDisplayFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
samlWebSSOProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
samlWebSSOHoKProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
samlLogoutProcessingFilter()));
return new FilterChainProxy(chains);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.authenticationEntryPoint(samlEntryPoint());
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringAntMatchers("/saml/**");
http
.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
.addFilterAfter(samlFilter(), BasicAuthenticationFilter.class);
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/saml/logout"))
.deleteCookies("JSESSIONID")
.logoutSuccessUrl(config().getSp().getEntityBaseURL() + LOGIN);
http
.headers()
.frameOptions()
.disable();
http
.sessionManagement()
.sessionAuthenticationErrorUrl(config().getSp().getEntityBaseURL() + LOGIN)
.invalidSessionUrl(config().getSp().getEntityBaseURL() + LOGIN);
http
.sessionManagement()
.maximumSessions(1)
.expiredUrl(config().getSp().getEntityBaseURL() + LOGIN);
http
.sessionManagement()
.sessionFixation()
.newSession();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
permitEndpoints(http);
}
public class CustomSimpleUrlLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
if(config().getSp().getEntityBaseURL().equalsIgnoreCase(this.getDefaultTargetUrl())) {
URLBuilder builder = new URLBuilder(request.getRequestURL().toString());
builder.setPath("/");
builder.setFragment("/login");
builder.setPort(CmsUtil.getWebServerPort());
this.setDefaultTargetUrl(builder.buildURL());
}
super.onLogoutSuccess(request, response, authentication);
}
}
请帮忙
为了支持全局注销 IDP 服务器应该公开注销 URL(如果元数据包含 SingleLogoutService,最简单的方法就是这样做)
您可以从此处获取更多信息 https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html
本地注销可以参考spring saml: How is LOGOUT handled? Is it mandatory to have logout endpoint in IDP metadata xml?
对于全局注销,您只需在上面接受的答案中的查询参数中删除 local=true 即,只需发送 /saml/logout
更多注销相关信息可以参考https://docs.spring.io/spring-security-saml/docs/1.0.x-SNAPSHOT/reference/htmlsingle/#configuration-logout
我是 SAML 安全和 KEYCLOAK 的新手。 我在 KEYCLOAK 中有一个使用 SAML 协议的客户端。我的应用程序配置为此 SAML 客户端。当我登录我的应用程序时,SAML 身份验证成功,我可以登录我的应用程序。用户会话也在 keycloak 中创建。在执行注销操作时,用户只是从我的应用程序中注销,而不是从 SAML.The 用户中注销。
如何执行注销以便同时清除 saml 会话。我发现spring saml支持“/saml/logout”来清除session。但是这个 url 需要从浏览器中显式调用,然后必须再次从我的应用程序中执行注销。有没有办法一次执行这两个调用。
下面是我的 samlSecurityconfig java 代码:
@Bean
public FilterChainProxy samlFilter() throws Exception {
List<SecurityFilterChain> chains = new ArrayList<>();
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"),
samlEntryPoint()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"),
samlLogoutFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
metadataDisplayFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
samlWebSSOProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
samlWebSSOHoKProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
samlLogoutProcessingFilter()));
return new FilterChainProxy(chains);
}
// Handler for successful logout
@Bean
public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
SimpleUrlLogoutSuccessHandler successLogoutHandler = new CustomSimpleUrlLogoutSuccessHandler();
successLogoutHandler.setDefaultTargetUrl(config().getSp().getEntityBaseURL());
return successLogoutHandler;
}
// Logout handler terminating local session
@Bean
public SecurityContextLogoutHandler logoutHandler() {
SecurityContextLogoutHandler logoutHandler =
new SecurityContextLogoutHandler();
logoutHandler.setInvalidateHttpSession(true);
logoutHandler.setClearAuthentication(true);
return logoutHandler;
}
// Filter processing incoming logout messages
// First argument determines URL user will be redirected to after successful
// global logout
@Bean
public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() {
return new SAMLLogoutProcessingFilter(successLogoutHandler(),
logoutHandler());
}
// Overrides default logout processing filter with the one processing SAML
// messages
@Bean
public SAMLLogoutFilter samlLogoutFilter() {
return new SAMLLogoutFilter(successLogoutHandler(),
new LogoutHandler[]{logoutHandler()},
new LogoutHandler[]{logoutHandler()});
}
@Bean
public FilterChainProxy samlFilter() throws Exception {
List<SecurityFilterChain> chains = new ArrayList<>();
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"),
samlEntryPoint()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"),
samlLogoutFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
metadataDisplayFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
samlWebSSOProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
samlWebSSOHoKProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
samlLogoutProcessingFilter()));
return new FilterChainProxy(chains);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.authenticationEntryPoint(samlEntryPoint());
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringAntMatchers("/saml/**");
http
.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
.addFilterAfter(samlFilter(), BasicAuthenticationFilter.class);
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/saml/logout"))
.deleteCookies("JSESSIONID")
.logoutSuccessUrl(config().getSp().getEntityBaseURL() + LOGIN);
http
.headers()
.frameOptions()
.disable();
http
.sessionManagement()
.sessionAuthenticationErrorUrl(config().getSp().getEntityBaseURL() + LOGIN)
.invalidSessionUrl(config().getSp().getEntityBaseURL() + LOGIN);
http
.sessionManagement()
.maximumSessions(1)
.expiredUrl(config().getSp().getEntityBaseURL() + LOGIN);
http
.sessionManagement()
.sessionFixation()
.newSession();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
permitEndpoints(http);
}
public class CustomSimpleUrlLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
if(config().getSp().getEntityBaseURL().equalsIgnoreCase(this.getDefaultTargetUrl())) {
URLBuilder builder = new URLBuilder(request.getRequestURL().toString());
builder.setPath("/");
builder.setFragment("/login");
builder.setPort(CmsUtil.getWebServerPort());
this.setDefaultTargetUrl(builder.buildURL());
}
super.onLogoutSuccess(request, response, authentication);
}
}
请帮忙
为了支持全局注销 IDP 服务器应该公开注销 URL(如果元数据包含 SingleLogoutService,最简单的方法就是这样做) 您可以从此处获取更多信息 https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html
本地注销可以参考spring saml: How is LOGOUT handled? Is it mandatory to have logout endpoint in IDP metadata xml?
对于全局注销,您只需在上面接受的答案中的查询参数中删除 local=true 即,只需发送
更多注销相关信息可以参考https://docs.spring.io/spring-security-saml/docs/1.0.x-SNAPSHOT/reference/htmlsingle/#configuration-logout