2011-11-16 15 views
12

Mi objetivo es implementar el protocolo de cierre de sesión único. En primer lugar yo soy la comprensión de cómo funciona el standar y cómo puede encajar en mi escenario: AD FS 2.0 como IdP, para mí es como un "recuadro negro" Construir una solicitud de LogOut de SAML2 firmada

lo que estoy haciendo en este momento es la siguiente :

  1. Enviar un <AuthnRequest> a mi IdP

  2. IdP me pide credenciales, les proporciono y conseguir con éxito de inicio de sesión.

  3. Obtener el valor SessionIndex forman el y construye un <LogoutRequest>

<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_135ad2fd-b275-4428-b5d6-3ac3361c3a7f" Version="2.0" Destination="https://idphost/adfs/ls/" IssueInstant="2008-06-03T12:59:57Z"><saml:Issuer>myhost</saml:Issuer><NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" NameQualifier="https://idphost/adfs/ls/">[email protected]</NameID<samlp:SessionIndex>_0628125f-7f95-42cc-ad8e-fde86ae90bbe</samlp:SessionIndex></samlp:LogoutRequest>

  1. Tome lo anterior <LogoutRequest> y codificarlo en base 64

  2. contructs la siguiente cadena : SAMLRequest=base64encodedRequest&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1

  3. Con la cadena anterior genera la firma

  4. codificar la firma en base 64

  5. Enviar la solicitud: https://"https://idphost/adfs/ls/?SAMLRequest=base64encodedRequest&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=base64EncodedSignature

Pero el IdP me responde: La verificación de la Firma del mensaje SAML fallida.

por firmar estoy usando mi clave privada (2048 bytes), y para verificar que se supone que el IdP está usando mi clave pública (la que envié cuando me registré mi anfitrión)

El código para la firma de la solicitud se parece a:

// Retrieve the private key 
KeyStore keyStore = KeyStore.getInstance("JKS", "SUN"); 
FileInputStream stream; 
stream = new FileInputStream("/path/to/my/keystore.jks"); 
keyStore.load(stream, "storepass".toCharArray()); 
PrivateKey key = (PrivateKey) keyStore.getKey("keyAlias","keyPass".toCharArray()); 

// Create the signature 
Signature signature = Signature.getInstance("SHA1withRSA"); 
signature.initSign(key); 
signature.update("SAMLRequest=jVJda8IwFH2e4H8ofW%2BbVmvboGWCDApusDn2sBdJm1sNtEmXmw7x1y92KDrY2Ov5uueEzJG1TUfXaqd68wIfPaBxDm0jkQ7Mwu21pIqhQCpZC0hNRTfLxzWNfEI7rYyqVONeWf52METQRijpOsVq4W7JoSzjJJnWAEAmwLMMpmRG0jCrYJICIcR13kCjdSxcG%2BA6K9tQSGYGZG9MhzQIGrUT0uPw6VegpV%2FtA8ZrDBq0ZxB7KCQaJo2NICT1yMwjk9cwonFG4%2BTdzceju%2FmpOx3EOu8qYThgGJ3j5sE1fZE%2F2X3FynlQumXm9%2BGhHw6I4F49SCm0TDRLzjWgrXiKee5ZI2oB%2Bj%2Bj8qYX6GvFtdj1cPRryzPJ4Xh%2F2%2Fe736VvRzf2nn24wmoP%2BZbMojSM4tpL6iz2plFVeYyn4NUc0hmDjJQlfCf9cI5HZ%2Fjm4%2BRf&RelayState=null&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1".getBytes()); 

String signatureBase64encodedString = (new BASE64Encoder()).encodeBuffer(signature.sign()); 
+0

Ahora recibo esta excepción: ** Longitud no válida para una matriz de caracteres Base-64 **. Básicamente eliminé el atributo NameQualifier de la solicitud y estoy usando rsa-sha256 en lugar del anterior, paso a paso ... – Gaucho

+0

Último registro de ADFS2: ADFS2: ** La solicitud de SAML no está firmada con el algoritmo de firma esperado. \t Firmado con: http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 \t Esperado: http://www.w3.org/2000/09/xmldsig#rsa-sha1* * – Gaucho

+0

Bien, el problema es el algoritmo. Estoy generando la firma usando ** SHA1withRSA **. Necesitaría usar ** http: //www.w3.org/2000/09/xmldsig#rsa-sha1 ". Usar la [Java XML API] (http://java.sun.com/developer/technicalArticles/ xml/dig_signature_api /) Puedo generar un xml SignedAuthnRequest, pero necesitaría aplicar HTTP-Redirect (SAMLRequest = value & SigAlg = value & Signature = value) ... – Gaucho

Respuesta

9

finalmente conseguí la receta adecuada:

  1. generar el valor SAMLRequest
  2. Codificar el valor SAMLRequest en Base64
  3. URL a codificar el valor SAMLRequest
  4. URL a codificar el valor SigAlg: http://www.w3.org/2000/09/xmldsig#rsa-sha1
  5. Alimentar a la firma algoritmo (SHA1withRSA) con el SAMLRequest = valor & SigAlg = valor
  6. URL a codificar la firma generada

podemos realizar los pasos 2 y 3 con el SAML 2.0 depurador (https://rnd.feide.no/simplesaml/module.php/saml2debug/debug.php).Y para , la codificación URL utiliza el w3schools clásico (http://www.w3schools.com/tags/ref_urlencode.asp)

¡Advertencia! Asegúrese de que el algoritmo para su parte dependiente, en el ADFS2, esté configurado a SHA1.

Saludos,

Luis

PS: ahora tengo que codificar un poco ...

PPS: Usted puede encontrar el código aquí: https://github.com/cerndb/wls-cern-sso/tree/master/saml2slo

+2

¡Gracias por la información sobre el algoritmo hash! Eso me atrapó. – David

+0

Esto funcionó para mí contra un NAM pero solo cuando detuve URLEncoding –

+0

NAM = Novell Access Manager? – Gaucho

2

Hay de donde a bug in the ADFS implementation el mensaje de error que da es al revés. Cuando dice:

La solicitud SAML no está firmada con el algoritmo esperado de la firma. La solicitud SAML está firmada con el algoritmo de firma http://www.w3.org/2001/04/xmldsig-more#rsa-sha256. El algoritmo de firma esperado es http://www.w3.org/2000/09/xmldsig#rsa-sha1

significa realmente que está utilizando SHA1 y esperaba SHA256.

+0

¡Gracias por compartirlo! – Gaucho

+0

Para aclarar, esto significa que tu aplicación está solicitando el cierre de sesión usando sha1 y su AD FS está configurado para SHA256. – Dscoduc

0

Como teníamos que seguir muchos pasos para finalmente lograr la implementación correcta de SLO en Domino 9.0.1, decidí escribir un código que permitiera utilizar cualquier configuración IdP (futura) para ejecutar con nuestros servidores Domino. He implementado la siguiente estrategia:

  • uso tanto de la información disponible a partir de una solicitud SAML Salir de entrada como sea posible
  • Identificar la configuración en el IdP idpcat.nsf para encontrar la información correspondiente sobre el IdP SLO Respuesta a ser enviado al proveedor de servicios IdP (servidor SAML)
  • Defina la respuesta de cierre de sesión de SAML en la configuración de IdP correspondiente en idpcat.nsf para permitir la adaptación dinámica a nuevos requisitos si la configuración de SAML cambia.

Como resultado, el código lee todos los campos de la solicitud SAML Salir entrante en un mapa de parámetros y decodifica e infla la cadena de consulta para extraer los parámetros XML de la solicitud en el mapa de parámetros. Como se pueden configurar diferentes sitios web en el servidor de domino para diferentes proveedores de servicios IdP para permitir la conexión de SSO, identifico la configuración de IdP con el "nombre de host" correspondiente y leo todos sus campos en el mismo Mapa de parámetros. Para definir una Respuesta XML aplicable, decidí escribir todas las definiciones necesarias en la configuración de Comment of the IdP, que permite adaptar configuraciones individuales de IdP para usar el mismo código para diferentes proveedores de IdP, incluso si usan diferentes versiones de SAML. Las definiciones en el campo Comentario de la configuración de IdP en idpcat.nsf son como las siguientes:

Respuesta SLO:/idp/SLO.saml2;

XML

SLO Respuesta: "< "urn: LogoutResponse ID =" @ UUID" versión = "# Versión" IssueInstant = Destino "@ ACTUAL_TIME" = "SLO_Response" InResponseTo = xmlns "# ID": Urna = "# xmlns : urna ">" "< "urn1: Emisor xmlns: urn1 =" XML_Parameter1" ">" HTTP_HSP_LISTENERURI "< "/ urn1: Emisor">" "< "urn: Status">" "<" urn: StatusCode value = "XML_Parameter2"/">" "< "/ urn: Status">" "< "/ urn: LogoutResponse">";

valores XML: #xmlns: urna = Protocolo -> aseveración & #xmlns: urn = protocolo -> estado: éxito;

Parámetros de respuesta: RelayState & SigAlg & Signature;

Tipo de firma: SHA256withRSA;

KeyStore Tipo: PKCS12;

Archivo de KeyStore: D: \ saml_cert.pfx;

KeyStore Contraseña: **********;

Certificado: {xxxxxxxxxx}

las claves de este definiciones se separan de los Valores con ":" y el final de los valores se especifica con ";" (no la nueva línea) Esto permite configurar una parametrización completa de la respuesta SAML según lo requiera el proveedor de servicios IdP en la configuración IdP correspondiente utilizada para la conexión SSO. Las definiciones se especifican de la siguiente manera:

• Respuesta SLO: esta es la dirección relativa a la cual debe enviarse la respuesta SLO en el servidor IdP respectivo.

• SLO Response XML: cadena de texto que define SLO Response estructurado en formato XML (Use "<" y ">" sin "). Las cadenas que identifican los parámetros encontrados en el Mapa de parámetros se intercambian a sus respectivos valores. Para asegurarse de que los parámetros similares se identifican correctamente, los parámetros de Cookie tienen un "$" inicial y los parámetros XML de la consulta de solicitud un "#" inicial. Además, se proporcionan 2 fórmulas, donde "@UUID" calculará un UUID aleatorio con el el formato correcto para el parámetro ID de la respuesta XML y "@ACTUAL_TIME" calculará la marca de tiempo correcta en el formato Instant para el parámetro IssueInstant de la respuesta XML.

• Valores XML: esta cadena de texto identifica parámetros adicionales, donde básicamente se usa un parámetro conocido, pero una parte del valor del parámetro debe intercambiarse para que coincida con el texto requerido. Los parámetros se identifican mediante la cadena "XML_Paramater" seguida de la posición en la cadena que separa cada valor con "&" en el texto XML de Respuesta SLO. El texto para los valores XML se estructura teniendo la identificación del parámetro seguida por "=" y el texto que se reemplazará seguido de "->" y el nuevo texto.

• Parámetros de respuesta: los parámetros de respuesta se separan con "&" y se agregarán a la Respuesta SLO como se define. Si se requiere una firma, los parámetros SigAlg y Firma son necesarios en esta cadena y deben colocarse al final.

• Tipo de firma: si se requiere una firma, aquí se especifica el tipo de algoritmo utilizado para calcular la firma.

• KeyStore Type: este es el tipo de KeyStore utilizado para el certificado.

• Archivo de KeyStore: este es el archivo donde KeyStore se ha guardado, incluida la unidad y la ruta en el servidor de Lotus Notes. Usamos D: \ saml_cert.pfx en el servidor de prueba.

• Contraseña de KeyStore: esta es la contraseña requerida para abrir el archivo KeyStore y los certificados almacenados en el mismo.

• Certificado: este es el alias del certificado que identifica el certificado en el archivo KeyStore. Si un Certificado se almacena en un nuevo Archivo KeyStore para combinar varios Certificados en una ubicación, el Alias ​​siempre se cambia a un nuevo valor, que debe adaptarse aquí.

El código que implementé es un Agente de Java con el nombre "Cerrar sesión" en el archivo domcfg.nsf, pero básicamente podría implementarse en cualquier base de datos disponible para los usuarios de SSO y se ejecuta como el servidor para permitir la protección del IdP configuraciones en idpcat.nsf con la mayor seguridad. En el proveedor de servicios IdP, debe configurar la solicitud de SLO para el servidor de Domino, respectivamente, el sitio web correspondiente, como "https://WEBSITE/domcfg.nsf/Logout?Open&", seguido de la solicitud de SAML. Si el proveedor de servicios IdP solicita la firma, debe almacenar un archivo KeyStore con el certificado, incluida la clave privada requerida para firmar. El archivo KeyStore se puede administrar utilizando la función del complemento MMC (consulte https://msdn.microsoft.com/en-us/library/ms788967(v=vs.110).aspx). Es posible combinar varios certificados en un archivo mediante la función de exportación, pero debe asegurarse de exportar las claves privadas al archivo mediante la configuración correspondiente en el asistente de exportación.

Este es el código para el agente "Salir", que cierra la sesión del usuario del servidor Domino y envía el SAML Salir Respuesta al proveedor de servicios IdP:

import lotus.domino.*; 
import java.io.*; 
import java.util.*; 
import java.text.*; 
import com.ibm.xml.crypto.util.Base64; 
import java.util.zip.*; 
import java.net.URLEncoder; 
import java.security.*; 

public class JavaAgent extends AgentBase { 
    public void NotesMain() { 
     try { 
      Session ASession = getSession(); 
      AgentContext AContext = ASession.getAgentContext(); 
      DateTime date = ASession.createDateTime("Today 06:00"); 
      int timezone = date.getTimeZone(); 

      Database DB = AContext.getCurrentDatabase(); 
      String DBName = DB.getFileName(); 
      DBName = DBName.replace("\\", "/").replace(" ", "+"); 

      //Load PrintWriter to printout values for checking (only to debug) 
      //PrintWriter pwdebug = getAgentOutput(); 
      //pwdebug.flush(); 

      //Load Data from Logout Request 
      Document Doc = AContext.getDocumentContext(); 
      Vector<?> items = Doc.getItems(); 
      Map<String, String> Params = new LinkedHashMap<String, String>(); 
      for (int j=0; j<items.size(); j++) { 
       Item item = (Item)items.elementAt(j); 
       if (!item.getValueString().isEmpty()) Params.put(item.getName(), item.getValueString()); 
      } 
      String ServerName = Params.get("HTTP_HSP_HTTPS_HOST"); 
      int pos = ServerName.indexOf(":"); 
      ServerName = pos > 0 ? ServerName.substring(0, ServerName.indexOf(":")) : ServerName; 
      Params.put("ServerName", ServerName); 
      Doc.recycle(); 
      DB.recycle(); 

      //Load Cookie Variables 
      Params = map(Params, Params.get("HTTP_COOKIE"), "$", "; ", "=", false, false); 
      //Load Query Variables 
      Params = map(Params, Params.get("QUERY_STRING_DECODED"), "", "&", "=", false, false); 
      //Decode and Infalte SAML Request 
      String RequestUnziped = decode_inflate(Params.get("SAMLRequest"), true); 
      //pwdebug.println("Request unziped: " + RequestUnziped); 
      //System.out.println("Request unziped: " + RequestUnziped); 
      String RequestXMLParams = RequestUnziped.substring(19, RequestUnziped.indexOf("\">")); 
      //Load XML Parameters from Request 
      Params = map(Params, RequestXMLParams, "#", "\" ", "=\"", false, false); 
      //for (Map.Entry<String, String> entry : Params.entrySet()) pwdebug.println(entry.getKey() + " value: " + entry.getValue()); 
      //for (Map.Entry<String, String> entry : Params.entrySet()) System.out.println(entry.getKey() + " value: " + entry.getValue()); 
      String Issuer = RequestUnziped.substring(RequestUnziped.indexOf(":Issuer"), RequestUnziped.indexOf("Issuer>")); 
      Issuer = Issuer.substring(Issuer.indexOf(">") + 1, Issuer.indexOf("<")); 
      Params.put("SLO_Issuer", Issuer); 

      //Load Parameters for the Response 
      DbDirectory Dir = ASession.getDbDirectory(null); 
      Database idpcat = Dir.openDatabase("idpcat.nsf"); 
      View idpView = idpcat.getView("($IdPConfigs)"); 
      Document idpDoc = idpView.getDocumentByKey(ServerName, false); 
      items = idpDoc.getItems(); 
      for (int j=0; j<items.size(); j++) { 
       Item item = (Item)items.elementAt(j); 
       if (!item.getValueString().isEmpty()) Params.put(item.getName(), item.getValueString()); 
      } 
      Params = map(Params, idpDoc.getItemValueString("Comments"), "", ";", ": ", false, false); 
      Params.put("SLO_Response", Issuer + Params.get("SLO Response")); 
      Params.put("@UUID", "_" + UUID.randomUUID().toString()); 
      Params.put("@ACTUAL_TIME", actualTime(Params.get("#IssueInstant"), Params.get("#NotOnOrAfter"), timezone)); 
      //for (Map.Entry<String, String> entry : Params.entrySet()) pwdebug.println(entry.getKey() + " value: " + entry.getValue()); 
      //for (Map.Entry<String, String> entry : Params.entrySet()) System.out.println(entry.getKey() + " value: " + entry.getValue()); 
      idpDoc.recycle(); 
      idpView.recycle(); 
      idpcat.recycle(); 
      Dir.recycle(); 

      //Setup XML Response as defined 
      String ResponseString = Params.get("SLO Response XML"); 
      for (Iterator<String> itRq = Params.keySet().iterator(); itRq.hasNext();) { 
       String Key = (String) itRq.next(); 
       ResponseString = ResponseString.replace(Key, Params.get(Key)); 
      } 
      //pwdebug.println("Response String replaced: " + ResponseString); 
      //System.out.println("Response String replaced: " + ResponseString); 
      //Load Values to be exchanged in the defined Response 
      Map<String, String> RsXMLValues = map(new LinkedHashMap<String, String>(), Params.get("XML Values"), "", "&", "=", true, false); 
      //for (Map.Entry<String, String> entry : RsXMLValues.entrySet()) pwdebug.println(entry.getKey() + " value: " + entry.getValue()); 
      //for (Map.Entry<String, String> entry : RsXMLValues.entrySet()) System.out.println(entry.getKey() + " value: " + entry.getValue()); 
      //Exchange defined Strings with Values from the Request 
      int itc = 0; 
      for (Iterator<String> itRXV = RsXMLValues.keySet().iterator(); itRXV.hasNext();) { 
       itc = itc + 1; 
       String Key = (String) itRXV.next(); 
       int lock = Key.indexOf(" -> "); 
       String KeyRq = lock > 0 ? Key.substring(0, lock) : Key; 
       int lockRq = KeyRq.indexOf(" "); 
       KeyRq = lockRq > 0 ? KeyRq.substring(0, lockRq) : KeyRq; 
       String Parameter = Params.get(KeyRq); 
       String Value = RsXMLValues.get(Key); 
       if (!Value.isEmpty()) { 
        int locv = Value.indexOf(" -> "); 
        String ValueS = locv > 0 ? Value.substring(0, locv) : Value; 
        String ValueR = locv > 0 && Value.length() > locv + 4 ? Value.substring(locv + 4) : ValueS; 
        Parameter = Parameter.replace(ValueS, ValueR); 
       } 
       ResponseString = ResponseString.replace(("XML_Parameter" + itc), Parameter); 
      } 
      //pwdebug.println("Final XML Response String: " + ResponseString); 
      //System.out.println("Final XML Response String: " + ResponseString); 
      //Deflate and Encode the XML Response 
      String ResponseZiped = deflate_encode(ResponseString, Deflater.DEFAULT_COMPRESSION, true); 
      //pwdebug.println("Response Ziped: " + ResponseZiped); 
      //System.out.println("Response Ziped: " + ResponseZiped); 
      //Setup Response URLQuery as defined 
      String ResponseEncoded = "SAMLResponse=" + URLEncoder.encode(ResponseZiped, "UTF-8"); 
      //pwdebug.println("Response to Sign: " + ResponseEncoded); 
      //System.out.println("Response to Sign: " + ResponseEncoded); 
      //Load Parameters to be added to the Response 
      Map<String, String> ResponseParams = map(new LinkedHashMap<String, String>(), Params.get("Response Parameters"), "", "&", "=", false, true); 
      //for (Map.Entry<String, String> entry : ResponseParams.entrySet()) pwdebug.println(entry.getKey() + " value: " + entry.getValue()); 
      //for (Map.Entry<String, String> entry : ResponseParams.entrySet()) System.out.println(entry.getKey() + " value: " + entry.getValue()); 
      //Add defined Parameters with Values from the Request 
      for (Iterator<String> itRP = ResponseParams.keySet().iterator(); itRP.hasNext();) { 
       String Key = (String) itRP.next(); 
       if (Key.contains("Signature")) { 
        //pwdebug.println("Response to Sign: " + ResponseEncoded); 
        //System.out.println("Response to Sign: " + ResponseEncoded); 
        Signature signature = Signature.getInstance(Params.get("Signature Type")); 
        //pwdebug.println("Signature: Initiated"); 
        //System.out.println("Signature: Initiated"); 
        KeyStore keyStore = KeyStore.getInstance(Params.get("KeyStore Type")); 
        //pwdebug.println("Key Store: Initiated"); 
        //System.out.println("Key Store: Initiated"); 
        keyStore.load(new FileInputStream(Params.get("KeyStore File")), Params.get("KeyStore Password").toCharArray()); 
        //pwdebug.println("Key Store: Loaded"); 
        //System.out.println("Key Store: Loaded"); 
        PrivateKey key = (PrivateKey) keyStore.getKey (Params.get("Certificate"), Params.get("KeyStore Password").toCharArray()); 
        //pwdebug.println("Key Store: Private Key Loaded"); 
        //System.out.println("Key Store: Private Key Loaded"); 
        signature.initSign(key); 
        //pwdebug.println("Signature: Private Key Initiated"); 
        //System.out.println("Signature: Private Key Initiated"); 
        signature.update(ResponseEncoded.getBytes("UTF-8")); 
        //pwdebug.println("Signature: Signed"); 
        //System.out.println("Signature: Signed"); 
        String ResponseSignature = URLEncoder.encode(Base64.encode(signature.sign()), "UTF-8"); 
        //pwdebug.println("Signature: Signed"); 
        //System.out.println("Signature: Signed"); 
        ResponseEncoded = ResponseEncoded.concat("&").concat(Key).concat("=").concat(ResponseSignature); 
       } 
       else ResponseEncoded = ResponseEncoded.concat("&").concat(Key).concat("=").concat(URLEncoder.encode(Params.get(Key), "UTF-8")); 
      } 
      String ResponseURL = Params.get("SLO_Response").concat("?").concat(ResponseEncoded); 
      //pwdebug.println("Final Response URL: " + ResponseURL); 
      //pwdebug.close(); 
      //System.out.println("Final Response URL: " + ResponseURL); 

      //Send Logout to Server and redirect to Response to defined Destination 
      PrintWriter pwsaml = getAgentOutput(); 
      pwsaml.flush(); 
      pwsaml.println("[" + Params.get("HTTP_HSP_LISTENERURI") + "/" + DBName + "?logout&redirectto=" + URLEncoder.encode(ResponseURL, "UTF-8") + "]"); 
      pwsaml.close(); 

      //Recycle Agent and Session 
      AContext.recycle(); 
      ASession.recycle(); 

     } catch(Exception e) { 
      PrintWriter pwerror = getAgentOutput(); 
      pwerror.flush(); 
      pwerror.println(e); 
      System.out.println(e); 
      pwerror.close(); 
     } 
    } 

    //Load Maps from Strings to identify Paramteres and Values 
    private static Map<String, String> map(Map<String, String> map, String input, String keys, String spliting, String pairing, Boolean keycount, Boolean empty) { 
     Map<String, String> output = map.isEmpty() ? new LinkedHashMap<String, String>() : map; 
     String[] Pairs = input.split(spliting); 
     int kc = 0; 
     for (String Pair : Pairs) { 
      kc = kc + 1; 
      int pos = Pair.indexOf(pairing); 
      String Key = pos > 0 ? Pair.substring(0, pos) : Pair; 
      if (keycount) Key = Key + " " + kc; 
      String Value = pos > 0 && Pair.length() > (pos + pairing.length()) ? Pair.substring(pos + pairing.length()) : ""; 
      if (!output.containsKey(Key) && (empty || !Value.trim().isEmpty())) output.put((keys + Key).trim(), Value.trim()); 
     } 
     return output; 
    } 

    //Decode and Inflate to XML 
    private static String decode_inflate(String input, Boolean infflag) throws IOException, DataFormatException { 
     byte[] inputDecoded = Base64.decode(input.getBytes("UTF-8")); 
     Inflater inflater = new Inflater(infflag); 
     inflater.setInput(inputDecoded); 
     byte[] outputBytes = new byte[1024]; 
     int infLength = inflater.inflate(outputBytes); 
     inflater.end(); 
     String output = new String(outputBytes, 0, infLength, "UTF-8"); 
     return output; 
    } 

    //Deflate and Encode XML 
    private static String deflate_encode(String input, int level , Boolean infflag) throws IOException { 
     byte[] inputBytes = input.getBytes("UTF-8"); 
     Deflater deflater = new Deflater(level, infflag); 
     deflater.setInput(inputBytes); 
     deflater.finish(); 
     byte[] outputBytes = new byte[1024]; 
     int defLength = deflater.deflate(outputBytes); 
     deflater.end(); 
     byte[] outputDeflated = new byte[defLength]; 
     System.arraycopy(outputBytes, 0, outputDeflated, 0, defLength); 
     String output = Base64.encode(outputDeflated); 
     return output; 
    } 

    //Define Date and Time Formats 
    private static SimpleDateFormat DateFormat = new SimpleDateFormat("yyyy-MM-dd"); 
    private static SimpleDateFormat TimeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); 

    //Formated Actual Time 
    private static String actualTime(String minTime, String maxTime, int localZone) throws ParseException { 
     Date actualtime = new Date(); 
     long acttime = actualtime.getTime(); 
     long mintime = resetTime(minTime, localZone); 
     long maxtime = resetTime(maxTime, localZone); 
     acttime = (acttime > mintime) && (acttime < maxtime) ? acttime: mintime + 1000; 
     return formatTime(acttime); 
    } 

    //Reset timemillis from String as defined 
    private static long resetTime(String givenTime, int localZone) throws ParseException { 
     Date date = DateFormat.parse(givenTime.substring(0, givenTime.indexOf("T"))); 
     long days = date.getTime(); 
     Date time = TimeFormat.parse(givenTime.substring(givenTime.indexOf("T") + 1, givenTime.indexOf("Z"))); 
     long hours = time.getTime(); 
     long zonecorr = localZone * 3600000; 
     return days + hours - zonecorr; 
    } 

    //Format timemillis into a String as required 
    private static String formatTime(long totalmilliSeconds) { 
     long date = 86400000 * (totalmilliSeconds/86400000); 
     long time = totalmilliSeconds % 86400000; 
     String dateString = DateFormat.format(date).concat("T"); 
     String timeString = TimeFormat.format(time).concat("Z"); 
     return dateString.concat(timeString); 
    } 

    public static String noCRLF(String input) { 
     String lf = "%0D"; 
     String cr = "%0A"; 
     String find = lf; 
     int pos = input.indexOf(find); 
     StringBuffer output = new StringBuffer(); 
     while (pos != -1) { 
      output.append(input.substring(0, pos)); 
      input = input.substring(pos + 3, input.length()); 
      if (find.equals(lf)) find = cr; 
      else find = lf; 
      pos = input.indexOf(find); 
     } 
     if (output.toString().equals("")) return input; 
     else return output.toString(); 
    } 
} 

Como ya habrán reconocido, varios Las líneas comentadas se pueden usar para depurar el agente, si las definiciones no son correctas y no dan como resultado un cierre de sesión exitoso. Puede cambiar fácilmente esas líneas eliminando el "//" que comienza esas líneas e imprime los parámetros que le gustaría ver en su pantalla o los envía a los registros.

Para iniciar SLO en el servidor de domino, escribí otro agente de Java utilizando el mismo concepto. El agente se llama startSLO y se encuentra en la misma base de datos que el agente de "Cerrar sesión". El uso de este agente se puede implementar fácilmente en cualquiera de sus aplicaciones creando botones que abren la URL relativa "/domcfg.nsf/startSLO?Open". El agente "startSLO" tiene el siguiente código .:

import lotus.domino.*; 
import java.io.*; 

public class JavaAgent extends AgentBase { 
    public void NotesMain() { 
     try { 
      Session ASession = getSession(); 
      AgentContext AContext = ASession.getAgentContext(); 

      Database DB = AContext.getCurrentDatabase(); 
      String DBName = DB.getFileName(); 
      DBName = DBName.replace("\\", "/").replace(" ", "+"); 

      //Load Data from Logout Request 
      Document Doc = AContext.getDocumentContext(); 
      String ServerName = Doc.getItemValueString("HTTP_HSP_HTTPS_HOST"); 
      int pos = ServerName.indexOf(":"); 
      ServerName = pos > 0 ? ServerName.substring(0, ServerName.indexOf(":")) : ServerName; 
      String Query = Doc.getItemValueString("Query_String"); 
      pos = Query.indexOf("?Open&"); 
      Query = pos > 0 ? "?" + Query.substring(Query.indexOf("?Open") + 6) : ""; 
      Doc.recycle(); 
      DB.recycle(); 

      //Load Parameters for the Response 
      DbDirectory Dir = ASession.getDbDirectory(null); 
      Database idpcat = Dir.openDatabase("idpcat.nsf"); 
      View idpView = idpcat.getView("($IdPConfigs)"); 
      Document idpDoc = idpView.getDocumentByKey(ServerName, false); 
      String SAMLSLO = idpDoc.getItemValueString("SAMLSloUrl"); 
      idpDoc.recycle(); 
      idpView.recycle(); 
      idpcat.recycle(); 
      Dir.recycle(); 

      //Send Logout to Server and redirect to Response to defined Destination 
      PrintWriter pwsaml = getAgentOutput(); 
      pwsaml.flush(); 
      pwsaml.println("[" + SAMLSLO + Query + "]"); 
      pwsaml.close(); 

      //Recycle Agent and Session 
      AContext.recycle(); 
      ASession.recycle(); 

     } catch(Exception e) { 
      PrintWriter pwerror = getAgentOutput(); 
      pwerror.flush(); 
      pwerror.println(e); 
      System.out.println(e); 
      pwerror.close(); 
     } 
    } 
} 
Cuestiones relacionadas