2017-08-21 377 views
0

我正在部署的服務器是這個 https://github.com/OpenConext/OpenConext-oidc,我延長它的 註銷功能,(Logout等)。 http:/www.example.com:8080/server app/saml/logout傳遞URL參數通過Spring Security定製LogoutSuccessHandler

我想在這樣的網址添加參數:

現在我有一個請求,註銷使用完成。 http:/www.example.com:8080/server app/saml/logout?value=www.youtube.com,所以我可以重定向到用戶的不同頁面。


爲此我創建了一個自定義的CustomLogoutSuccessHandler

public class CustomLogoutSuccessHandler implements LogoutSuccessHandler { 

    @Override 
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { 
     if (authentication != null && authentication.getDetails() != null) { 
      try { 
       request.getSession().invalidate(); 
       System.out.println("User Successfully Logout"); 
       //you can add more codes here when the user successfully logs out, 
       //such as updating the database for last active. 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 

     //Set the Server Status 
     httpServletResponse.setStatus(HttpServletResponse.SC_OK); 

     //redirect to login 
     String queryString = request.getParameter("value"); 

     //Check if no parameters have been passed 
     if (queryString == null) { 
      httpServletResponse.sendRedirect("http://www.youtube.com"); 
     } else { 
      httpServletResponse.sendRedirect("http://www." + queryString + ".com"); 
     } 
    } 

} 

的問題是,request.getParameter("value");總是返回null!爲什麼會發生?我趕不上網址或什麼?

我總是取回的URL`HTTP:/www.example.com:8080 /服務器應用程序/ SAML/SingleLogout


在用戶的context.xml(註銷定義像這樣)(它在這裏工作是沒有問題的)。

... 

<!-- Filters for processing of SAML messages --> 
    <bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy"> 
     <security:filter-chain-map request-matcher="ant"> 
      <security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/> 
      <security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/> 
      <security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/> 
      <security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/> 
      <security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/> 
      <security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/> 
     </security:filter-chain-map> 
    </bean> 

!-- Handler for successful logout --> 
    <bean id="successLogoutHandler" class="oidc.security.CustomLogoutSuccessHandler"></bean> 

... 

回答

0

1)創建的自定義SAMLLogoutFilter(CustomSAMLLogoutFilter)和我傳遞原始請求的URL到CustomLogoutHandler

/* 
* To change this license header, choose License Headers in Project Properties. 
* To change this template file, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package oidc.security; 

import java.io.IOException; 
import javax.servlet.FilterChain; 
import javax.servlet.ServletException; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import org.springframework.security.saml.SAMLLogoutFilter; 
import org.springframework.security.web.authentication.logout.LogoutHandler; 
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; 
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; 

/** 
* 
* @author GOXR3PLUS 
*/ 
public class CustomSAMLLogoutFilter extends SAMLLogoutFilter { 

    private LogoutSuccessHandler logoutSuccessHandler; 

    public CustomSAMLLogoutFilter(String successUrl, LogoutHandler[] localHandler, LogoutHandler[] globalHandlers) { 
     super(successUrl, localHandler, globalHandlers); 
    } 

    public CustomSAMLLogoutFilter(LogoutSuccessHandler logoutSuccessHandler, LogoutHandler[] localHandler, LogoutHandler[] globalHandlers) { 
     super(logoutSuccessHandler, localHandler, globalHandlers); 
     this.logoutSuccessHandler = logoutSuccessHandler; 
    } 

    @Override 
    public void processLogout(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { 
     super.processLogout(request, response, chain); 

     // System.out.println("Hello from [ CustomSAMLLogoutFilter ] "); 
     //Lets print some information here 
     System.out.println("FULL HttpServletRequest URL is : " + getFullURL(request)); 

     //Downcast and pass it as parameter 
     ((CustomLogoutSuccessHandler) logoutSuccessHandler).setOriginalURL(getFullURL(request)); 
     // System.out.println("Chao chao from [ CustomSAMLLogoutFilter ] \n"); 
    } 

    /** 
    * Returns the full URL of the HTTPServletRequest 
    */ 
    public static String getFullURL(HttpServletRequest request) { 
     StringBuffer requestURL = request.getRequestURL(); 
     String queryString = request.getQueryString(); 

     if (queryString == null) { 
      return requestURL.toString(); 
     } else { 
      return requestURL.append('?').append(queryString).toString(); 
     } 
    } 

} 

2)所述CustomLogoutHandler的的代碼:

/* 
* To change this license header, choose License Headers in Project Properties. 
* To change this template file, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package oidc.security; 

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

import javax.servlet.ServletException; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import java.io.IOException; 
import java.net.URL; 
import java.util.List; 
import java.util.regex.Pattern; 
import org.springframework.util.MultiValueMap; 
import org.springframework.web.util.UriComponentsBuilder; 

public class CustomLogoutSuccessHandler implements LogoutSuccessHandler { 

    /** 
    * This variable is used in order to keep track of the original URL the user 
    * passed before he logged out 
    */ 
    private String originalURL; 

    @Override 
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { 
     if (authentication != null && authentication.getDetails() != null) { 
      try { 
       request.getSession().invalidate(); 
       System.out.println("User Successfully Logout"); 
       //you can add more codes here when the user successfully logs out, 
       //such as updating the database for last active. 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 

     //Set the Server Status 
     response.setStatus(HttpServletResponse.SC_OK); 

     //----------- Choose where to redirect------------------ 
     System.out.println("Hello from [ CustomLogoutSuccessHandler ] \n"); 
     System.out.println("Original URL is : " + originalURL); 

     //Check if any parameters have been passed 
     String redirectURL = null; 
     if (originalURL != null) { 

      try { 
       //Get all the parameters from the url 
       MultiValueMap<String, String> parameters 
         = UriComponentsBuilder.fromUriString(originalURL).build().getQueryParams(); 

       //--Get the parameter value 
       List<String> value = parameters.get("redirect"); 
       if (value.size() != 0) { //if it exists 
        redirectURL = value.get(0); 
       } 
      } catch (Exception ex) { 
       ex.printStackTrace(); 
      } 
     } 

     // Decide where to redirect 
     System.out.println(" Redirect : " + redirectURL); 
     if (redirectURL == null) { 
      response.sendRedirect("/oidc/"); 
     } else { 
      response.sendRedirect(redirectURL); 
     } 
    } 

    public void setOriginalURL(String originalURL) { 
     this.originalURL = originalURL; 
    } 

} 

3)在用戶的context.xml我只有兩件事

<!-- Handler for successful logout --> <bean id="successLogoutHandler" class="oidc.security.CustomLogoutSuccessHandler"></bean>

<!-- Override default logout processing filter with the one processing SAML messages --> <bean id="samlLogoutFilter" class="oidc.security.CustomSAMLLogoutFilter"> <constructor-arg index="0" ref="successLogoutHandler"/> <constructor-arg index="1" ref="logoutHandler"/> <constructor-arg index="2" ref="logoutHandler"/> </bean>

改變爲了讓他們看到n EW定製的Java文件java/..../security


最後如果你想在這裏看到所有用戶的context.xml是:

<?xml version="1.0" encoding="UTF-8"?> 
<!-- 
    Copyright 2015 The MITRE Corporation 
    and the MIT Kerberos and Internet Trust Consortium 

    Licensed under the Apache License, Version 2.0 (the "License"); 
    you may not use this file except in compliance with the License. 
    You may obtain a copy of the License at 

    http://www.apache.org/licenses/LICENSE-2.0 

    Unless required by applicable law or agreed to in writing, software 
    distributed under the License is distributed on an "AS IS" BASIS, 
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    See the License for the specific language governing permissions and 
    limitations under the License. 
--> 
<beans xmlns="http://www.springframework.org/schema/beans" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:security="http://www.springframework.org/schema/security" 
     xmlns:context="http://www.springframework.org/schema/context" 
     xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd 
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd" 

     profile="!local"> 


    <context:property-placeholder location="classpath:application.oidc.properties"/> 

    <!-- Enable auto-wiring --> 
    <context:annotation-config/> 

    <!-- Scan for auto-wiring classes in spring saml packages --> 
    <context:component-scan base-package="org.springframework.security.saml"/> 

    <!-- Unsecured pages --> 
    <security:http security="none" pattern="/translate-sp-entity-id" create-session="never"/> 

    <!-- Secured pages with SAML as entry point --> 
    <security:http entry-point-ref="samlEntryPoint" use-expressions="true" create-session="never"> 
     <security:intercept-url pattern="/authorize" access="hasRole('ROLE_USER')" /> 
     <security:intercept-url pattern="/**" access="permitAll" /> 
     <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/> 
     <security:custom-filter before="PRE_AUTH_FILTER" ref="clientIdFilter"/> 
     <security:custom-filter ref="authRequestFilter" after="SECURITY_CONTEXT_FILTER" /> 
     <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/> 
     <!--<security:anonymous />--> 
     <security:headers> 
      <security:frame-options policy="DENY" /> 
     </security:headers> 
     <security:csrf request-matcher-ref="apiCsrfProtectionMatcher"/> 
    </security:http> 

    <!-- Logger for SAML messages and events --> 
    <bean id="clientIdFilter" class="oidc.security.ClientIdFilter"/> 

    <!-- Filters for processing of SAML messages --> 
    <bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy"> 
     <security:filter-chain-map request-matcher="ant"> 
      <security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/> 
      <security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/> 
      <security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/> 
      <security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/> 
      <security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/> 
      <security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/> 
     </security:filter-chain-map> 
    </bean> 



    <!-- Handler deciding where to redirect user after successful login --> 
    <bean id="successRedirectHandler" 
      class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> 
     <property name="defaultTargetUrl" value="/"/> 
    </bean> 

    <!-- 
    Use the following for interpreting RelayState coming from unsolicited response as redirect URL: 
    <bean id="successRedirectHandler" class="org.springframework.security.saml.SAMLRelayStateSuccessHandler"> 
     <property name="defaultTargetUrl" value="/" /> 
    </bean> 
    --> 

    <!-- Handler deciding where to redirect user after failed login --> 
    <bean id="failureRedirectHandler" 
      class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> 
     <property name="useForward" value="true"/> 
     <property name="defaultFailureUrl" value="/error.jsp"/> 
    </bean> 

    <!-- Handler for successful logout --> 
    <bean id="successLogoutHandler" class="oidc.security.CustomLogoutSuccessHandler"></bean> 

    <!-- Override default logout processing filter with the one processing SAML messages --> 
    <bean id="samlLogoutFilter" class="oidc.security.CustomSAMLLogoutFilter"> 
     <constructor-arg index="0" ref="successLogoutHandler"/> 
     <constructor-arg index="1" ref="logoutHandler"/> 
     <constructor-arg index="2" ref="logoutHandler"/> 
    </bean> 

    <!-- Filter processing incoming logout messages --> 
    <!-- First argument determines URL user will be redirected to after successful global logout --> 
    <bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter"> 
     <constructor-arg index="0" ref="successLogoutHandler"/> 
     <constructor-arg index="1" ref="logoutHandler"/> 
    </bean> 


    <security:authentication-manager alias="authenticationManager"> 
     <!-- Register authentication manager for SAML provider --> 
     <security:authentication-provider ref="samlAuthenticationProvider"/> 
    </security:authentication-manager> 

    <!-- Logger for SAML messages and events --> 
    <bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/> 

    <bean id="keyStoreLocator" class="oidc.saml.KeyStoreLocator"/> 

    <!-- Central storage of cryptographic keys --> 
    <bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager"> 
     <constructor-arg> 
      <bean factory-bean="keyStoreLocator" 
        factory-method="createKeyStore"> 
       <constructor-arg value="${idp.entity.id}"/> 
       <constructor-arg value="${idp.public.certificate}"/> 
       <constructor-arg value="${sp.entity.id}"/> 
       <constructor-arg value="${sp.public.certificate}"/> 
       <constructor-arg value="${sp.private.key}"/> 
       <constructor-arg value="${sp.passphrase}"/> 
      </bean> 
     </constructor-arg> 
     <constructor-arg> 
      <map> 
       <entry key="${sp.entity.id}" value="${sp.passphrase}"/> 
      </map> 
     </constructor-arg> 
     <constructor-arg type="java.lang.String" value="${sp.entity.id}"/> 
    </bean> 

    <!-- Entry point to initialize authentication, default values taken from properties file --> 
    <bean id="samlEntryPoint" class="oidc.saml.ProxySAMLEntryPoint"/> 

    <!-- Filter automatically generates default SP metadata --> 
    <bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter"> 
     <constructor-arg> 
      <bean class="org.springframework.security.saml.metadata.MetadataGenerator"> 
       <property name="entityId" value="${sp.entity.id}"/> 
       <property name="entityBaseURL" value="${sp.entity.base.url}"/> 
       <property name="extendedMetadata"> 
        <bean class="org.springframework.security.saml.metadata.ExtendedMetadata"> 
         <property name="signMetadata" value="true"/> 
         <property name="idpDiscoveryEnabled" value="false"/> 
        </bean> 
       </property> 
      </bean> 
     </constructor-arg> 
    </bean> 

    <!-- The filter is waiting for connections on URL suffixed with filterSuffix and presents SP metadata there --> 
    <bean id="metadataDisplayFilter" class="org.springframework.security.saml.metadata.MetadataDisplayFilter"/> 

    <bean id="metadataManager" class="org.springframework.security.saml.metadata.CachingMetadataManager"> 
     <constructor-arg> 
      <list> 
       <bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate"> 
        <constructor-arg> 
         <bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider"> 
          <constructor-arg> 
           <value type="java.lang.String">${idp.metadata.url}</value> 
          </constructor-arg> 
          <constructor-arg> 
           <value type="int">30000</value> 
          </constructor-arg> 
          <property name="parserPool" ref="parserPool"/> 
          <property name="requireValidMetadata" value="false"/> 
         </bean> 
        </constructor-arg> 
       </bean> 
      </list> 
     </constructor-arg> 
    </bean> 

    <!-- IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here --> 
    <bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate"> 
     <constructor-arg ref="metadataManager"/> 
    </bean> 

    <bean id="defaultSAMLUserDetailsService" class="oidc.saml.DefaultSAMLUserDetailsService"> 
     <constructor-arg value="${sp.entity.id}"/> 
    </bean> 

    <!-- SAML Authentication Provider responsible for validating of received SAML messages --> 
    <bean id="samlAuthenticationProvider" class="oidc.saml.FederatedSAMLAuthenticationProvider"> 
     <property name="userDetails" ref="defaultSAMLUserDetailsService"/> 
    </bean> 

    <!-- Provider of default SAML Context --> 
    <bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/> 

    <!-- Processing filter for WebSSO profile messages --> 
    <bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter"> 
     <property name="authenticationManager" ref="authenticationManager"/> 
     <property name="authenticationSuccessHandler" ref="successRedirectHandler"/> 
     <property name="authenticationFailureHandler" ref="failureRedirectHandler"/> 
    </bean> 

    <!-- Processing filter for WebSSO Holder-of-Key profile --> 
    <bean id="samlWebSSOHoKProcessingFilter" class="org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter"> 
     <property name="authenticationManager" ref="authenticationManager"/> 
     <property name="authenticationSuccessHandler" ref="successRedirectHandler"/> 
     <property name="authenticationFailureHandler" ref="failureRedirectHandler"/> 
    </bean> 

    <!-- Logout handler terminating local session --> 
    <bean id="logoutHandler" 
      class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"> 
     <property name="invalidateHttpSession" value="false"/> 
    </bean> 

    <!-- Class loading incoming SAML messages from httpRequest stream --> 
    <bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl"> 
     <constructor-arg> 
      <list> 
       <ref bean="redirectBinding"/> 
       <ref bean="postBinding"/> 
       <ref bean="artifactBinding"/> 
       <ref bean="soapBinding"/> 
       <ref bean="paosBinding"/> 
      </list> 
     </constructor-arg> 
    </bean> 

    <!-- SAML 2.0 WebSSO Assertion Consumer --> 
    <bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"> 
     <property name="maxAuthenticationAge" value="43200"/> 
    </bean> 

    <!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer --> 
    <bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/> 

    <!-- SAML 2.0 Web SSO profile --> 
    <bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/> 

    <!-- SAML 2.0 Holder-of-Key Web SSO profile --> 
    <bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/> 

    <!-- SAML 2.0 ECP profile --> 
    <bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/> 

    <!-- SAML 2.0 Logout Profile --> 
    <bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/> 

    <!-- Bindings, encoders and decoders used for creating and parsing messages --> 
    <bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding"> 
     <constructor-arg ref="parserPool"/> 
     <constructor-arg ref="velocityEngine"/> 
    </bean> 

    <bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding"> 
     <constructor-arg ref="parserPool"/> 
    </bean> 

    <bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding"> 
     <constructor-arg ref="parserPool"/> 
     <constructor-arg ref="velocityEngine"/> 
     <constructor-arg> 
      <bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl"> 
       <constructor-arg> 
        <bean class="org.apache.commons.httpclient.HttpClient"> 
         <constructor-arg> 
          <bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/> 
         </constructor-arg> 
        </bean> 
       </constructor-arg> 
       <property name="processor"> 
        <bean class="org.springframework.security.saml.processor.SAMLProcessorImpl"> 
         <constructor-arg ref="soapBinding"/> 
        </bean> 
       </property> 
      </bean> 
     </constructor-arg> 
    </bean> 

    <bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding"> 
     <constructor-arg ref="parserPool"/> 
    </bean> 

    <bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding"> 
     <constructor-arg ref="parserPool"/> 
    </bean> 

    <!-- Initialization of OpenSAML library--> 
    <bean class="org.springframework.security.saml.SAMLBootstrap"/> 

    <!-- Initialization of the velocity engine --> 
    <bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/> 

    <!-- XML parser pool needed for OpenSAML parsing --> 
    <bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize"> 
     <property name="builderFeatures"> 
      <map> 
       <entry key="http://apache.org/xml/features/dom/defer-node-expansion" value="false"/> 
      </map> 
     </property> 
    </bean> 

    <bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/> 
</beans>