Deze pagina biedt een algemeen overzicht van het Building Block Security Assertion Markup Language (SAML) 2.0. Daarnaast bespreken we veelvoorkomende problemen met eenmalige aanmelding (SSO) en technieken voor het oplossen van problemen met de SAML-verificatieprovider.

Als om wat voor reden dan ook een bijgewerkt of nieuw XML-bestand met IdP-metagegevens wordt geüpload voor een SAML-verificatieprovider (naar de pagina Instellingen voor SAML-verificatie in de sectie Instellingen identiteitsprovider van Blackboard Learn), moeten de SAML B2- en die SAML-verificatieprovider ook worden geschakeld tussen Inactief/Beschikbaar, terwijl de SAML-verificatieprovider de status ‘Actief’ heeft, om ervoor te zorgen dat IdP-metagegevens in de cache worden verwijderd en de bijgewerkte IdP-metagegevens volledig worden benut.


Belangrijke terminologie

In dit artikel worden de volgende termen en afkortingen gebruikt:

  • SAML: Security Assertion Markup Language
  • IdP: Identiteitsprovider
  • SP: Serviceprovider
  • ADFS: Active Directory Federation Services
  • GUI: Grafische gebruikersinterface. In de context van Blackboard Learn betekent dit werken binnen de software.

Configuratie-instellingen voor SAML bewerken

Om makkelijker problemen met SAML-verificatie op te lossen, is het Building Block SAML in release 3200.2.0 uitgebreid met deze configuratie-instellingen en -opties:

  • Vervallimiet voor SAML-sessies definiëren
  • Type Signature Algorithm kiezen
  • Certificaten opnieuw genereren
  • De ResponseSkew-waarde aanpassen

Meer informatie over het configureren van instellingen in het Building Block SAML


Fouten en uitzonderingen

Fouten en uitzonderingen die met SAML te maken hebben, worden vastgelegd in de volgende logboeken:

  • /usr/local/blackboard/logs/bb-services-log.txt
  • /usr/local/blackboard/logs/tomcat/stdout-stderr-<datum>.log
  • /usr/local/blackboard/logs/tomcat/catalina-log.txt

Deze logboeken moeten altijd worden geraadpleegd als er een probleem is gemeld over SAML-verificatie.


SAML Tracer

Als iteraties nodig zijn om verificatieproblemen met SAML 2.0 op te lossen, kan het op een bepaald punt nodig zijn om de kenmerken te controleren of weergeven die daadwerkelijk worden vrijgegeven door de IdP en die tijdens het verificatieproces naar Learn worden verzonden. Als de kenmerken van de IdP NIET zijn gecodeerd in het antwoord van SAML, kun je de kenmerken bekijken met de Firefox-invoegtoepassing SAML tracer of met SAML Message Decoder voor Chrome.


Kenmerk niet goed toegewezen

Als het kenmerk met de userName niet op de juiste manier is toegewezen, namelijk zoals opgegeven in het veld Externe gebruikers-ID in de sectie SAML-kenmerken in kaart brengen op de pagina Instellingen voor SAML-verificatie in de GUI van Blackboard Learn, wordt de volgende gebeurtenis vastgelegd in het logboek bb-services wanneer er wordt geprobeerd om via SAML-verificatie aan te melden bij Blackboard Learn:

2016-06-28 12:48:12 -0400 - userName is null or empty

Er dan een vergelijkbare Aanmeldingsfout! weergegeven in de browser: Blackboard Learn kan zich op dit moment niet aanmelden bij je account met behulp van single sign-on. Neem contact op met je beheerder voor hulp.

Er wordt een vermelding Authentication Failure toegevoegd aan het logboek bb-services:

2016-06-28 12:48:12 -0400 - BbSAMLExceptionHandleFilter - javax.servlet.ServletException: Authentication Failure
    at blackboard.auth.provider.saml.customization.handler.BbAuthenticationSuccessHandler.checkAuthenticationResult(BbAuthenticationSuccessHandler.java:81)
    at blackboard.auth.provider.saml.customization.handler.BbAuthenticationSuccessHandler.onAuthenticationSuccess(BbAuthenticationSuccessHandler.java:57)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:331)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:245)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
[SNIP]

Oplossing

Je hebt twee opties om dit probleem op te lossen. De eerste optie is het selecteren van Maak accounts als ze niet in het systeem bestaan op de pagina Instellingen voor SAML-verificatie in de GUI van Blackboard Learn. De tweede optie is om de waarde te bekijken van de kenmerken die door de IdP worden vrijgegeven. Dit kan via SAML tracer of Debug loggen als de kenmerken NIET zijn gecodeerd:

<saml2:Attribute Name="urn:oid:0.9.2342.19200300.100.1.3">
    <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                          xsi:type="xs:anyType"
                          >bbuser_saml2@bbchjones.net</saml2:AttributeValue>
</saml2:Attribute>

Daarna moet je de Attribute Name met de gewenste AttributeValue toewijzen aan Externe gebruikers-ID op de pagina Instellingen voor SAML-verificatie in de GUI van Blackboard Learn.


Geen compatibele gegevensbron geselecteerd

Gebruikers kunnen zich niet via SAML-verificatie aanmelden bij Blackboard Learn als de Gegevensbron voor de gebruikers niet is geselecteerd in de sectie Instellingen serviceprovider > Compatibele gegevensbronnen van de pagina Instellingen voor SAML-verificatie in de GUI van Blackboard Learn. De volgende vermelding wordt vastgelegd in het logboek bb-services als een gebruiker probeert om zich via SAML-verificatie aan te melden bij Blackboard Learn:

2016-09-23 12:33:13 -0500 - userName is null or empty

Er wordt een Aanmeldingsfout! weergegeven in de browser, plus de vermelding Authentication Failure in het logboek bb-services:

2016-09-23 12:33:13 -0500 - BbSAMLExceptionHandleFilter - javax.servlet.ServletException: Authentication Failure
    at blackboard.auth.provider.saml.customization.handler.BbAuthenticationSuccessHandler.checkAuthenticationResult(BbAuthenticationSuccessHandler.java:82)
    at blackboard.auth.provider.saml.customization.handler.BbAuthenticationSuccessHandler.onAuthenticationSuccess(BbAuthenticationSuccessHandler.java:58)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.successfulAuthentication(AbstractAuthenticationProcessingFilter.java:331)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:245)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter (SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor3399.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
        [SNIP]

Oplossing

  1. Vraag de gebruikersnaam van een gebruiker die zich niet kan aanmelden.
  2. Ga in de GUI van Blackboard Learn naar Systeembeheer > Gebruikers en zoek de gebruiker.
  3. Kopieer de waarde voor Gegevensbronsleutel van de gebruiker.
  4. Ga naar Systeembeheer > Verificatie > "naam van provider" > Instellingen voor SAML-verificatie > Compatibele gegevensbronnen.
  5. Zet een vinkje naast die gegevensbron in de kolom Naam en selecteer Verzenden.

Foutbericht dat notatie van opgegeven URL onjuist is

Als OneLogin is geconfigureerd als de IdP voor de SAML-verificatieprovider in Blackboard Learn, kan er op de pagina een foutbericht worden weergegeven dat de notatie van de opgegeven URL onjuist is na het invoeren van de referenties voor OneLogin om aan te melden bij Blackboard Learn.

In het logboek bb-services wordt dan deze vermelding opgenomen:

2016-09-16 09:43:40 -0400 - Given URL is not well formed<P><span class="captionText">For reference, the Error ID is 17500f44-7809-4b9f-a272-3bed1d1af131.</span&gt; - java.lang.IllegalArgumentException: Given URL is not well formed
    at org.opensaml.util.URLBuilder.<init>(URLBuilder.java:120)
    at org.opensaml.util.SimpleURLCanonicalizer.canonicalize(SimpleURLCanonicalizer.java:87)
    at org.opensaml.common.binding.decoding.BasicURLComparator.compare(BasicURLComparator.java:57)
    at org.opensaml.common.binding.decoding.BaseSAMLMessageDecoder.compareEndpointURIs(BaseSAMLMessageDecoder.java:173)
    at org.opensaml.common.binding.decoding.BaseSAMLMessageDecoder.checkEndpointURI(BaseSAMLMessageDecoder.java:213)
    at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:72)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:172)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:80)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    [SNIP]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.MalformedURLException: no protocol: {recipient}
    at java.net.URL.<init>(URL.java:593)
    at java.net.URL.<init>(URL.java:490)
    at java.net.URL.<init>(URL.java:439)
    at org.opensaml.util.URLBuilder.<init>(URLBuilder.java:77)
        ... 203 more

Oplossing

  1. Schakel de Firefox-invoegtoepassing SAML tracer in en repliceer het probleem.
  2. Kijk naar het begin van de gebeurtenis SAML POST:

    <samlp:Response Destination="{recipient}"
            ID="R8afbfbfee7292613f98ad4ec4115de7c6b385be6"
            InResponseTo="a3g2424154bb0gjh3737ii66dadbff4"
            IssueInstant="2016-09-16T18:49:09Z"
            Version="2.0"
            xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
            xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
            >
        <saml:Issuer>https://app.onelogin.com/saml/metadata/123456</saml:Issuer>
        [SNIP]

  3. Op regel 1 met het antwoord (Response) zie je dat de bestemming (Destination=) alleen is ingesteld op recipient (ontvanger).
  4. Laat de client naar de sectie Configuration van OneLogin gaan.
  5. Controleer of het veld Recipient leeg is.
  6. Kopieer de waarde van ACS (Consumer) URL, plak deze in het veld Recipient en selecteer Save.

Probleemscenario's voor IdP/SP

  1. Als er een fout wordt weergegeven voordat je wordt omgeleid naar de aanmeldingspagina van de IdP, zijn de metagegevens van de IdP mogelijk ongeldig.
  2. Als er een fout optreedt nadat je je hebt aangemeld bij de IdP, zijn dit de mogelijke oorzaken:
    1. Onjuiste koppeling van kenmerken tussen de SP en de IdP, of de IdP heeft geen geldige externe gebruikers-ID geretourneerd.
    2. De SAML-respons van de IdP is niet gevalideerd door de SP. Dit kan worden veroorzaakt door:
      • De IdP ondertekent de SAML-respons met een certificaat dat niet is uitgegeven door een erkende certificeringsinstantie, en het certificaat is niet aanwezig in de keystore van de SP.
      • De systeemklok van de SP staat niet goed.

Active Directory Federation Services (ADFS)

Namen van kenmerken zijn hoofdlettergevoelig in de sectie SAML-kenmerken in kaart brengen van de pagina Instellingen voor SAML-verificatie in de GUI van Blackboard Learn. Dus als voor de Externe gebruikers-ID sAMAccountName is ingevoerd voor Kenmerknaam op de pagina Instellingen en de feitelijke SAML POST van de IdP het onderstaande bevat voor Attribute Name in de AttributeStatement:

<AttributeStatement>
    <Attribute Name="SamAccountName>
        <AttributeValue>Test-User</AttributeValue>
    </Attribute>
</AttributeStatement>

kan de gebruiker zich niet aanmelden. De waarde voor de kenmerknaam voor Externe gebruikers-ID op de pagina Instellingen voor SAML-verificatie moet dan worden gewijzigd van sAMAccountName in SamAccountName.

Waarschuwing dat resource niet is gevonden of aanmeldingsfout!

In deze sectie bespreken we enkele veelvoorkomende problemen die tot gevolg kunnen hebben dat een gebruiker zich niet kan aanmelden bij Learn via SAML-verificatie met ADFS wanneer het bericht De opgegeven bron is niet gevonden of je hebt geen toegang tot de bron. of Aanmeldingsfout! wordt weergegeven in de GUI van Blackboard Learn.

Probleem 1

Na het invoeren van aanmeldingsreferenties op de aanmeldingspagina van ADFS, kan er een fout worden weergegeven nadat de omleiding naar de GUI van Blackboard Learn is gestart: De opgegeven bron is niet gevonden of je hebt geen toegang tot de bron.

In het logboek stdout-stderr wordt dan deze melding vastgelegd:

INFO | jvm 1 | 2016/06/22 06:08:33 | - No mapping found for HTTP request with URI [/auth-saml/saml/SSO] in DispatcherServlet with name 'saml'

Het probleem treedt op omdat de methode noHandlerFound() wordt gebruikt in de code DispatcherServlet.java en het HTTP SSO-verzoek niet kan worden gevonden/toegewezen.

/**
 * No handler found -&gt; set appropriate HTTP response status.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception if preparing the response failed
 */
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
 if (pageNotFoundLogger.isWarnEnabled()) {
  pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) +
    "] in DispatcherServlet with name '" + getServletName() + "'");
 }
 if (this.throwExceptionIfNoHandlerFound) {
  throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
    new ServletServerHttpRequest(request).getHeaders());
 }
 else {
  response.sendError(HttpServletResponse.SC_NOT_FOUND);
 }
}

Oplossing

Dit probleem treedt meestal op omdat de entiteits-ID voor de SP die is geconfigureerd in de GUI van Blackboard Learn onjuist is. Dit kan worden opgelost door naar Systeembeheer > Verificatie > Instellingen voor SAML-verificatie > Instellingen serviceprovider te gaan en de waarde voor Entity ID aan te passen. Voor ADFS is de standaardconfiguratie voor Entity ID https://[Learn Server Hostname]/auth-saml/saml/SSO.

Als een school de standaard-URL https://school.blackboard.com wijzigt in https://their.school.edu, moet de entiteits-ID in de GUI van Blackboard Learn op de pagina Instellingen voor SAML-verificatie worden gewijzigd in https://their.school.edu/auth-saml/saml/SSO.

Probleem 2

Na het invoeren van aanmeldingsreferenties op de aanmeldingspagina van ADFS, kan er een fout worden weergegeven nadat de omleiding naar de GUI van Blackboard Learn is gestart: De opgegeven bron is niet gevonden of je hebt geen toegang tot de bron.

In het logboek stdout-stderr wordt dan deze melding vastgelegd:

INFO  | jvm 1  | 2016/06/22 06:08:33 | - No mapping found for HTTP request with URI [/auth-saml/saml/SSO] in DispatcherServlet with name 'saml'

En deze melding in het catalina-logboek:

ERROR 2016-06-27 10:47:03,664 connector-6: userId=_2_1, sessionId=62536416FB80462298C92064A7022E50 org.opensaml.xml.encryption.Decrypter - Error decrypting the encrypted data element
org.apache.xml.security.encryption.XMLEncryptionException: Illegal key size
Original Exception was java.security.InvalidKeyException: Illegal key size
    at org.apache.xml.security.encryption.XMLCipher.decryptToByteArray(XMLCipher.java:1822)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:596)
    at org.opensaml.xml.encryption.Decrypter.decryptUsingResolvedEncryptedKey(Decrypter.java:795)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:535)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToList(Decrypter.java:453)
    at org.opensaml.xml.encryption.Decrypter.decryptData(Decrypter.java:414)
    at org.opensaml.saml2.encryption.Decrypter.decryptData(Decrypter.java:141)
    at org.opensaml.saml2.encryption.Decrypter.decrypt(Decrypter.java:69)
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:199)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:82)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
        [SNIP]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.security.InvalidKeyException: Illegal key size
    at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
    at javax.crypto.Cipher.init(Cipher.java:1393)
    at javax.crypto.Cipher.init(Cipher.java:1327)
    at org.apache.xml.security.encryption.XMLCipher.decryptToByteArray(XMLCipher.java:1820)
        ... 205 more

En deze melding in het logboek bb-services:

2016-06-27 10:47:03 -0400 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor3422.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:277)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:274)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:309)
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:249)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:237)
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:55)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:191)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:187)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:186)
    at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at sun.reflect.GeneratedMethodAccessor3421.invoke(Unknown Source)
        [SNIP]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: Response doesn't have any valid assertion which would pass subject validation
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:229)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 229 more

Het probleem doet zich voor omdat ADFS kenmerken altijd gecodeerd met AES-256 verstuurd en de Java-runtime die wordt gebruikt door Blackboard Learn, geen ingebouwde ondersteuning heeft voor AES-256.

Oplossing

Een universele oplossing is om een PowerShell-sessie te openen op de ADFS-server en in te stellen dat de relying party die wordt gemaakt voor Blackboard Learn, de kenmerken ongecodeerd verzendt. Aangezien de communicatie volledig verloopt via SSL, heeft dit geen gevolgen voor de beveiliging van de verificatie. Hierdoor wordt ook de foutopsporing van eventuele problemen gemakkelijker, aangezien de kenmerken kunnen worden bekeken met tools voor foutopsporing zoals de Firefox-invoegtoepassing SAML tracer en het Blackboard Learn-systeem niet opnieuw hoeft te worden gestart. Om de relying party die wordt gemaakt voor Blackboard Learn kenmerken ongecodeerd te laten verzenden, open je een PowerShell-sessie en voer je de volgende opdrachten uit, waarbij je TargetName vervangt door de naam van de Relying Party Trust die in de ADFS-beheerconsole wordt vermeld onder Trust Relationships > Relying Party Trusts.

set-ADFSRelyingPartyTrust –TargetName "yourlearnserver.blackboard.com" –EncryptClaims $False

Na deze wijziging moet de ADFS-service opnieuw worden gestart met de opdracht: Restart-Service ADFSSRV

Probleem 3

Na het invoeren van aanmeldingsreferenties op de aanmeldingspagina van ADFS, kan er een fout worden weergegeven nadat de omleiding naar de GUI van Blackboard Learn is gestart: De opgegeven bron is niet gevonden of je hebt geen toegang tot de bron or Aanmeldingsfout!

In beide gevallen worden deze vergelijkbare SAML-gebeurtenissen vastgelegd in het logboek stdout-stderr:

INFO   | jvm 1    | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - No HttpSession currently exists
INFO   | jvm 1    | 2016/09/06 20:33:04 | - No SecurityContext was available from the HttpSession: null. A new one will be created.
INFO   | jvm 1    | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 4 of 10 in additional filter chain; firing Filter: 'FilterChainProxy'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Checking match of request : '/saml/login'; against '/saml/login/**'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - /saml/login?apId=_107_1&redirectUrl=https%3A%2F%2Fbb.fraser.misd.net%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab at position 1 of 1 in additional filter chain; firing Filter: 'SAMLEntryPoint'
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Request for URI http://www.w3.org/2000/09/xmldsig#rsa-sha1
INFO   | jvm 1    | 2016/09/06 20:33:04 | - Request for URI http://www.w3.org/2000/09/xmldsig#rsa-sha1
INFO   | jvm 1    | 2016/09/06 20:33:04 | - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO   | jvm 1    | 2016/09/06 20:33:04 | - SecurityContextHolder now cleared, as request processing completed
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - HttpSession returned null object for SPRING_SECURITY_CONTEXT
INFO   | jvm 1    | 2016/09/06 20:33:07 | - No SecurityContext was available from the HttpSession: [email protected] A new one will be created.
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO at position 4 of 10 in additional filter chain; firing Filter: 'FilterChainProxy'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/login/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/logout/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/bbsamllogout/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Checking match of request : '/saml/sso'; against '/saml/sso/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - /saml/SSO at position 1 of 1 in additional filter chain; firing Filter: 'SAMLProcessingFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Authentication attempt using org.springframework.security.saml.SAMLAuthenticationProvider
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Forwarding to /
INFO   | jvm 1    | 2016/09/06 20:33:07 | - DispatcherServlet with name 'saml' processing POST request for [/auth-saml/saml/SSO]
INFO   | jvm 1    | 2016/09/06 20:33:07 | - No mapping found for HTTP request with URI [/auth-saml/saml/SSO] in DispatcherServlet with name 'saml'
INFO   | jvm 1    | 2016/09/06 20:33:07 | - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Successfully completed request
INFO   | jvm 1    | 2016/09/06 20:33:07 | - Skip invoking on
INFO   | jvm 1    | 2016/09/06 20:33:07 | - SecurityContextHolder now cleared, as request processing completed

Of deze vergelijkbare SAML-uitzonderingen in het logboek bb-services:

2016-11-29 09:04:24 -0500 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor853.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
    at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at sun.reflect.GeneratedMethodAccessor853.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        [SNIP]
    at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:677)
    at blackboard.tomcat.valves.LoggingRemoteIpValve.invoke(LoggingRemoteIpValve.java:44)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1110)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:785)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1425)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: Response issue time is either too old or with date in the future, skew 60, time 2016-11-29T14:03:16.634Z
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:126)
    at blackboard.auth.provider.saml.customization.consumer.BbSAMLWebSSOProfileConsumerImpl.processAuthenticationResponse(BbSAMLWebSSOProfileConsumerImpl.java:40)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 230 more

Het probleem doet zich voor wanneer er tussen de ADFS-server en de toepassingsserver van Blackboard Learn een tijdsverschil is van 60 of meer seconden.

Oplossing

Je hebt twee opties om dit probleem op te lossen:

  1. De klokken van de Blackboard Learn-toepassingsservers en de ADFS-server handmatig synchroniseren. Voor Blackboard Learn kun je de huidige tijd en de tijdzone van de server bekijken in een webbrowser door /webapps/portal/healthCheck toe te voegen aan het einde van een URL voor Blackboard Learn.

    Voorbeeld: https://mhtest1.blackboard.com//webapps/portal/healthCheck

    Hostname: ip-10-145-49-11.ec2.internal
    Status: Active - Database connectivity established
    Running since: Sat, Dec 3, 2016 - 05:39:11 PM EST
    Time of request: Thu, Dec 8, 2016 - 05:12:43 PM EST

    Een instelling kan de bovenstaande URL gebruiken om de tijdzone en tijd van het Blackboard Learn-systeem te vergelijken met die van de ADFS-server en de onderdelen vervolgens indien nodig aanpassen op de ADFS-server, zodat ze synchroon lopen met de Blackboard Learn-site.

  2. Het verschil kan worden gecorrigeerd in securityContext.xml om een tijdelijke oplossing voor het probleem te bieden:
    1. Maak een back-up van het bestand:

      /usr/local/blackboard/content/vi/BBLEARN/plugins/bb-auth-provider-saml/webapp/WEB-INF/config/saml/securityContext.xml

    2. Wijzig:

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

      in:

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

Probleem 4

Na het invoeren van aanmeldingsreferenties op de aanmeldingspagina van ADFS, kan er een fout worden weergegeven nadat de omleiding naar de GUI van Blackboard Learn is gestart: De opgegeven bron is niet gevonden of je hebt geen toegang tot de bron of Aanmeldingsfout!

In het logboek bb-services worden de volgende uitzonderingen opgenomen:

2016-11-01 12:47:19 -0500 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor929.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
    at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 [SNIP]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: Response has invalid status code urn:oasis:names:tc:SAML:2.0:status:Responder, status message is null
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:113)
    at blackboard.auth.provider.saml.customization.consumer.BbSAMLWebSSOProfileConsumerImpl.processAuthenticationResponse(BbSAMLWebSSOProfileConsumerImpl.java:40)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 230 more
 2016-11-01 12:47:19 -0500 - BbSAMLExceptionHandleFilter - javax.servlet.ServletException: Unsuccessful Authentication
         at blackboard.auth.provider.saml.customization.filter.BbSAMLProcessingFilter.unsuccessfulAuthentication(BbSAMLProcessingFilter.java:31)
         at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:235)
         at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
         at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
         at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
         at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
         at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
         at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
         at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
         at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
         at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
         at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
         at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
         at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
         at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
         at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
         at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
         at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
         at sun.reflect.GeneratedMethodAccessor929.invoke(Unknown Source)
         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
         at java.lang.reflect.Method.invoke(Method.java:498)
         at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
         at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
         at java.security.AccessController.doPrivileged(Native Method)
         at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
         at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
         at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
         at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
         at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
         at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
         at java.security.AccessController.doPrivileged(Native Method)
         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
         at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
 [SNIP]

Oplossing
  1. Ga naar het configuratiescherm voor systeembeheer.
  2. Selecteer Building Blocks onder Building Blocks.
  3. Selecteer Geïnstalleerde tools.
  4. Zoek Verificatieprovider - SAML in de lijst. Open het menu en selecteer Instellingen.
  5. Kies onder Signature Algorithm-instellingen de optie SHA-256 in de lijst. Nadat je een waarde hebt gekozen voor Type Signature Algorithm, start je het Building Block SAML opnieuw om de nieuwe instellingen toe te passen.
  6. Selecteer Verzenden om de wijzigingen op te slaan.

Probleem 5

Na het invoeren van aanmeldingsreferenties op de aanmeldingspagina van ADFS, kan er een fout worden weergegeven nadat de omleiding naar de GUI van Blackboard Learn is gestart: De opgegeven bron is niet gevonden of je hebt geen toegang tot de bron or Aanmeldingsfout!

In het logboek bb-services worden de volgende uitzonderingen opgenomen:

2017-01-04 22:52:58 -0700 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor935.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
    at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    [SNIP]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: NameID element must be present as part of the Subject in the Response message, please enable it in the IDP configuration
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:252)
    at blackboard.auth.provider.saml.customization.consumer.BbSAMLWebSSOProfileConsumerImpl.processAuthenticationResponse(BbSAMLWebSSOProfileConsumerImpl.java:40)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 214 more

Zoals wordt vermeld in de bovenstaande SAML-uitzondering, ontbreekt het NameID-element uit het Subject in het Response-bericht. Het probleem treedt meestal op wanneer de NameID niet is ingesteld als een Outgoing Claim Type in een Claims Rule voor de Relying Party Trust van de IdP voor ADFS van de instelling of als de Claims Rule voor de NameID niet in de juiste volgorde staat voor Relying Party Trust van de IdP voor ADFS van de instelling, wat weer het ontbrekende NameID-element in het Subject in het Response-bericht tot gevolg heeft.

Voorbeeld: NameID-element ontbreekt

<Subject>
    <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <SubjectConfirmationData InResponseTo="a22ai8iig0f75ae22hd28748b12da50"
                                 NotOnOrAfter="2017-01-03T05:57:58.234Z"
                                 Recipient="https://yourschool.blackboard.com/auth-saml/saml/SSO"
                                 />
    </SubjectConfirmation>
</Subject>

Voorbeeld: NameID-element is aanwezig

<Subject>
    <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">testadfs</NameID>
    <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <SubjectConfirmationData InResponseTo="a5903d39if463ea87ieiab5135j9ji"
                                 NotOnOrAfter="2017-01-05T04:33:12.715Z"
                                 Recipient="https://yourschool.blackboard.com/auth-saml/saml/SSO"
                                 />
    </SubjectConfirmation>
</Subject>

Je kunt de Firefox-invoegtoepassing SAML tracer gebruiken om het Subject in het Response-bericht te bekijken.

Oplossing

Er zijn drie methoden om dit probleem op te lossen.

  1. Controleer of de stappen uit de SAML B2-configuratiehandleiding Guide voor ADFS goed zijn uitgevoerd en breng zo nodig wijzigingen aan om een binnenkomende claim voor de Relying Party Trust te transformeren voor hun ADFS IdP:
    1. Selecteer Edit Claims Rule.
    2. Selecteer Add Rule.
    3. Selecteer op de pagina Select Rule Template de optie Transform an Incoming Claim voor de Claim rule template en selecteer daarna Next.
    4. Ga op de pagina Configure Rule naar het veld Claim rule name en typ Transform Email to Name ID.
    5. Stel Incoming claim type in op SamAccountName (moet overeenkomen met de waarde voor Outgoing Claim Type die eerder is gemaakt in de regel Transform Username to NameID).
    6. De waarde voor Outgoing claim type is Name ID.
    7. De waarde voor Outgoing name ID format is Email.
    8. Controleer of Pass through all claim values is geselecteerd selecteer Finish.
    9. Selecteer OK om de regel op te slaan en selecteer nogmaals OK om de toewijzing van de kenmerken te voltooien.
  2. Zorg ervoor dat in de volgorde van de Claims Rules die worden gebruikt voor hun ADFS IdP, er geen optionele regels voorafgaan aan de regel met het NameID-element.
  3. Bij gebruik van een aangepast kenmerk moet het NameID-element zich in de Relying Party Trust bevinden, aangezien Learn nog steeds verwacht dat hun ADFS IdP een NameID-waarde vrijgeeft.

Probleem 6

Wanneer een gebruiker via SAML-verificatie is aangemeld bij Blackboard Learn en probeert zich af te melden door eerst te klikken op de knop Afmelden aan de linkerkant van de pagina en vervolgens op SSO-sessie beëindigen, wordt er direct een Aanmeldingsfout! weergegeven.

Sign On Error!
Blackboard Learn is currently unable to log into your account using single sign-on. Contact your administrator for assistance.
For reference, the Error ID is [error ID].

In het logboek bb-services wordt de volgende uitzondering opgenomen:

2017-05-08 15:10:46 -0400 - BbSAMLExceptionHandleFilter Error Id: f3299757-8d4e-4fab-98cf-49cd99f4891e - javax.servlet.ServletException: Incoming SAML message failed security validation
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.processLogout(SAMLLogoutProcessingFilter.java:145)
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.doFilter(SAMLLogoutProcessingFilter.java:104)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    [SNIP]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.ws.security.SecurityPolicyException: Validation of request simple signature failed for context issuer
    at org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule.doEvaluate(BaseSAMLSimpleSignatureSecurityPolicyRule.java:139)
    at org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule.evaluate(BaseSAMLSimpleSignatureSecurityPolicyRule.java:103)
    at org.opensaml.ws.security.provider.BasicSecurityPolicy.evaluate(BasicSecurityPolicy.java:51)
    at org.opensaml.ws.message.decoder.BaseMessageDecoder.processSecurityPolicy(BaseMessageDecoder.java:132)
    at org.opensaml.ws.message.decoder.BaseMessageDecoder.decode(BaseMessageDecoder.java:83)
    at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:70)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:172)
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.processLogout(SAMLLogoutProcessingFilter.java:131)
    ... 244 more

De fout treedt op vanwege de instelling Servicetype eenmalig afmelden op de pagina Instellingen voor SAML-verificatie.

Oplossing

De instelling moet worden geconfigureerd in Blackboard Learn en op de ADFS-server.

Als ADFS de IdP is, selecteer je alleen de instelling Bericht en verwijder je het Redirect-eindpunt voor de Relying Party Trust van het Learn-exemplaar op de ADFS-server.

  1. Ga in Learn naar Systeembeheer > Verificatie > "naam van provider" > Instellingen voor SAML-verificatie > Servicetype eenmalig afmelden.
  2. Selecteer Bericht en schakel het selectievakje Omleiden uit.
  3. Ga op de ADFS-server naar de Relying Party Trust voor het Learn-exemplaar.
  4. Selecteer Properties > Endpoints. Er worden twee eindpunten voor SAML-afmelding vermeld.
  5. Verwijder het eindpunt Redirect. Selecteer Remove Endpoint om het te verwijderen, en kies vervolgens Apply en OK.

Nadat je de bovenstaande wijzigingen hebt doorgevoerd in Learn en op de ADFS-server, werkt de knop SSO-sessie beëindigen om een gebruiker af te melden.

Probleem 7

Na het invoeren van de aanmeldingsreferenties op de aanmeldingspagina van ADFS, wordt er een Aanmeldingsfout! weergegeven bij het omleiden naar Learn.

In het logboek bb-services wordt de volgende SAML-uitzondering opgenomen:

2017-05-26 07:39:30 -0400 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
        at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
        at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
        at blackboard.auth.provider.saml.customization.filter.BbSAMLProcessingFilter.attemptAuthentication(BbSAMLProcessingFilter.java:46)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
        at sun.reflect.GeneratedMethodAccessor380.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
        at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
        at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
        at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
        at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
        at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
        at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
        at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:37)
    [SNIP]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: Response has invalid status code urn:oasis:names:tc:SAML:2.0:status:Responder, status message is null
        at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:113)
        at blackboard.auth.provider.saml.customization.consumer.BbSAMLWebSSOProfileConsumerImpl.processAuthenticationResponse(BbSAMLWebSSOProfileConsumerImpl.java:56)
        at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 247 more

Oplossing

Vanaf Blackboard Learn 3200.0.0 is er een nieuwe optie voor het opnieuw genereren van het SAML-coderingscertificaat. Ga hiervoor naar Systeembeheer > Building Blocks > Verificatieprovider - SAML > Instellingen > Certificaat opnieuw genereren. Het probleem Aanmeldingsfout! kan zich voordoen als de knop Certificaat opnieuw genereren wordt geselecteerd nadat de SP-metagegevens al zijn geüpload naar de Relying Party Trust voor de Learn-site op de ADFS-server. Je kunt dit probleem als volgt oplossen:

  1. Ga naar Systeembeheer > Verificatie > "naam van SAML-provider" > Instellingen voor SAML-verificatie.
  2. Selecteer Genereren naast Metadata serviceprovider om het nieuwe bestand met metagegevens op te slaan.
  3. Ga naar de ADFS-server en upload de nieuwe SP-metagegevens naar de Relying Party Trust voor de Learn-site.

Als je een nieuw certificaat genereert onder de B2-instellingen, moet je de SAML B2 op Inactief zetten en vervolgens teruggaan naar Geactiveerd om de wijziging door te voeren. Daarna ga je terug naar de instellingen van de provider en genereer je de nieuwe metagegevens om te importeren in de IDP. Als je deze instellingen niet inschakelt, kan het oude certificaat nog steeds worden meegenomen als je nieuwe metagegevens genereert. De IDP wordt dus niet bijgewerkt en de volgende keer dat Learn opnieuw wordt gestart, wordt het nieuwe certificaat weergegeven. De SAML-verificatie wordt daardoor verbroken omdat de IDP en het certificaat niet overeenkomen.

Metagegevens voor federatie

Aangezien de metagegevens voor een ADFS-federatie (die zich meestal bevinden in https://[ADFS Server Hostname]/FederationMetadata/2007-06/FederationMetadata.xml) een element bevatten dat niet compatibel is met SAML 2.0, moeten de metagegevens in het geval van Active Directory Federation Services (ADFS) worden bewerkt om het niet-compatibele element te verwijderen voordat het wordt geüpload naar de sectie Instellingen identiteitsprovider van de pagina Instellingen voor SAML-verificatie in de GUI van Blackboard Learn. Als de metagegevens met het niet-compatibele element worden geüpload, treedt er een fout op bij het selecteren van de SAML-aanmeldingskoppeling op de aanmeldingspagina van Blackboard Learn: Metadata for entity [entity] and role {} wasn't found. For reference, the Error ID is [error ID].

En dit is de bijbehorende Java-stacktrace voor de fout-ID in het logboek bb-services:

2016-06-21 11:42:51 -0700 - Metadata for entity https://<Learn Server Hostname>/adfs/ls/ and role {urn:oasis:names:tc:SAML:2.0:metadata}SPSSODescriptor wasn&#39;t found<P><span class="captionText">For reference, the Error ID is c99511ae-1162-4941-b823-3dda19fea157.</span&gt; - org.opensaml.saml2.metadata.provider.MetadataProviderException: Metadata for entity https://ulvsso.laverne.edu/adfs/ls/ and role {urn:oasis:names:tc:SAML:2.0:metadata}SPSSODescriptor wasn't found
    at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalEntity(SAMLContextProviderImpl.java:319)
    at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalContext(SAMLContextProviderImpl.java:216)
    at org.springframework.security.saml.context.SAMLContextProviderImpl.getLocalAndPeerEntity(SAMLContextProviderImpl.java:126)
    at org.springframework.security.saml.SAMLEntryPoint.commence(SAMLEntryPoint.java:146)
    at org.springframework.security.saml.SAMLEntryPoint.doFilter(SAMLEntryPoint.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor1652.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
        [SNIP]

Oplossing

Aangezien de standaardlocatie van metagegevens voor een ADFS-federatie https://[ADFS server hostname]/FederationMetadata/2007-06/FederationMetadata.xml is:

  1. Download dit bestand en open het in een teksteditor. Verwijder de sectie die begint met <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"&gt; ... </X509Data></KeyInfo> en eindigt met </ds:Signature>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">

    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
      <ds:Reference URI="#_43879f32-9a91-4862-bc87-e98b85b51158">
       <ds:Transforms>
        <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
        <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
       </ds:Transforms>
       <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
       <ds:DigestValue>z1H1[SNIP]jaYM=</ds:DigestValue>
      </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue&gt; FVj[SNIP]edrfNKWvsvk5A==
      </ds:SignatureValue>
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
       <X509Data>
        <X509Certificate>
        FDdd[SNIP]qTNKdk5F/vf1AocDaX
        </X509Certificate>
       </X509Data>
      </KeyInfo>
    </ds:Signature>

  2. Upload het bijgewerkte XML-bestand met metagegevens naar de GUI van Blackboard Learn, op de pagina Instellingen voor SAML-verificatie in de sectie Instellingen identiteitsprovider.
  3. Schakelen tussen de SAML-verificatieprovider en SAML B2 inactief/beschikbaar, terwijl de verificatieprovider van de SAML de status 'Actief' heeft.

Als een instelling SAML-verificatie test op een Blackboard Learn-site en verschillende SAML-verificatieproviders gebruikt die hetzelfde onderliggende XML-bestand met metagegevens voor de ADFS IdP delen op de Blackboard Learn-site, moet het bijgewerkte XML-bestand met metagegevens ook worden geüpload in de GUI van Blackboard Learn GUI, naar de pagina Instellingen voor SAML-verificatie in de sectie Instellingen identiteitsprovider. Dit is zelfs nodig als de andere SAML-verificatieproviders op Inactief zijn gezet. De SAML B2 moet vervolgens tussen Inactief/Beschikbaar worden geschakeld, terwijl de SAML-verificatieprovider de status ‘Actief’ heeft, om er zeker van te zijn dat het bijgewerkte XML-bestand met metagegevens in het hele systeem wordt herkend.

Onjuiste methode voor opzoeken van gebruikers

Na het invoeren van de aanmeldingsreferenties op de aanmeldingspagina van ADFS, wordt de gebruiker omgeleid naar de GUI van Blackboard Learn, maar hij of zij wordt niet aangemeld bij Blackboard Learn.

Dit is de ENIGE gebeurtenis die voor SAML-verificatie wordt vastgelegd in het logboek bb-services:

2016-10-18 13:03:28 -0600 - userName is null or empty

Oplossing

  1. Meld je als beheerder aan bij Blackboard Learn met behulp van de standaardverificatie Intern van Blackboard Learn.
  2. Ga naar Systeembeheer > "naam van SAML-verificatieprovider" > Bewerken.
  3. Wijzig de waarde voor Zoekmethode gebruikers van Batch-UID in Gebruikersnaam.

Extra afmeldknop SSO-sessie beëindigen

ADFS probeert om een extra afmeldknop SSO-sessie beëindigen toe te voegen aan de pagina Alle sessies beëindigen? die wordt weergegeven na het voor de eerste maal kiezen van de afmeldknop in de rechterbovenhoek van de GUI van Blackboard Learn.

Hiervoor wordt een extra SingleLogoutService toegevoegd aan het bestand met IdP-metagegevens:

<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://your.server.name/adfs/ls/"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://your.server.name/adfs/ls/"/>

Aangezien dat een optionele SAML B2 IdP-configuratie is en de handtekening die wordt aangeboden in het eindpunt voor omleiden onjuist is, treedt er een fout bij het selecteren van de extra knop SSO-sessie beëindigen op de pagina Alle sessies beëindigen?: Incoming SAML message failed security validation. Validation of request simple signature failed for context issuer. For reference, the error Id is [fout-ID].

Dit is de bijbehorende Java-stacktrace voor de fout-ID in het logboek bb-services:

2016-10-17 16:57:44 -0400 - Incoming SAML message failed security validation Validation of request simple signature failed for context issuer<P><span class="captionText">For reference, the Error ID is 930c7767-8710-475e-8415-2077152280e0.</span&gt; - org.opensaml.ws.security.SecurityPolicyException: Validation of request simple signature failed for context issuer
    at org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule.doEvaluate(BaseSAMLSimpleSignatureSecurityPolicyRule.java:139)
    at org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule.evaluate(BaseSAMLSimpleSignatureSecurityPolicyRule.java:103)
    at org.opensaml.ws.security.provider.BasicSecurityPolicy.evaluate(BasicSecurityPolicy.java:51)
    at org.opensaml.ws.message.decoder.BaseMessageDecoder.processSecurityPolicy(BaseMessageDecoder.java:132)
    at org.opensaml.ws.message.decoder.BaseMessageDecoder.decode(BaseMessageDecoder.java:83)
    at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:70)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105)
    at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:172)
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.processLogout(SAMLLogoutProcessingFilter.java:131)
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.doFilter(SAMLLogoutProcessingFilter.java:104)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor1652.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
        [SNIP]

Oplossing

  1. Ga op de ADFS-server naar de Relying Party Trust voor het exemplaar van Blackboard Learn.
  2. Selecteer het tabblad Properties > Endpoints.
  3. Op het tabblad Endpoints staan twee SAML Logout Endpoints.
  4. Verwijder het eindpunt Redirect.
  5. Selecteer Remove Endpoint om het te verwijderen, en kies vervolgens Apply en OK.

Na het verwijderen van het eindpunt Redirect, werkt de knop SSO-sessie beëindigen zonder problemen bij het afmelden van de gebruiker.

Toepassingslogboeken inzien met Logboeken

Als je een probleem met ADFS SAML-verificatie wilt oplossen, kan het ook nodig zijn dat een instelling de toepassingslogboeken van ADFS bekijkt in Logboeken op hun ADFS-server om een beter beeld te krijgen. Dit is met name handig wanneer het SAML-antwoord van de ADFS-server een RequestDenied-status heeft zoals hieronder wordt aangegeven:

<samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder">
        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:RequestDenied" />
    </samlp:StatusCode>
</samlp:Status>

Het SAML-antwoord kan worden bekeken met de Firefox-invoegtoepassing SAML tracer.

De status RequestDenied in een antwoord geeft meestal aan dat er een probleem is opgetreden bij de interpretatie van het antwoord door de IdP (ADFS) en de hierna volgende verwerking van het door de SP (Blackboard Learn) geleverde resultaat.

Ga als volgt te werk om de toepassingslogboeken van ADFS met Logboeken te bekijken:

  1. Open Logboeken op de ADFS-server.
  2. Selecteer Logboeken voor analyseren en foutopsporing weergeven in het menu Beeld.
  3. Navigeer in de consolestructuur naar Logboeken Toepassingen en Services > AD FS Tracing > Debug.

Azure Active Directory

Azure AD is de cloudgebaseerde service van Microsoft (MS) voor adreslijst- en identiteitsbeheer.

Eerste deel van e-mailadres versturen

Als een instelling Azure AD gebruikt als de IdP en alleen het eerste deel van de Azure AD-gebruikersnaam voor e-mail wil gebruiken voor de Blackboard Learn-gebruikersnaam, kunnen ze configureren dat de IdP van Azure AD de speciale functie ExtractMailPrefix() gebruikt om het domeinachtervoegsel te verwijderen uit het e-mailadres of de principal-naam van de gebruiker, waardoor alleen het eerste deel van de gebruikersnaam wordt doorgegeven (bijvoorbeeld "joesmith" in plaats van joesmith@voorbeeld.com).

Als de externe gebruikers-ID voor Blackboard Learn urn:oid:1.3.6.1.4.1.5923.1.1.1.6 is, zou de instelling Attribute voor de Azure IdP er zo uitzien:

Attribute Name:        urn:oid:1.3.6.1.4.1.5923.1.1.1.6
Attribute Value:    ExtractMailPrefix()
Mail:            user.userprincipalname

Op basis van de gebruikersnaam voor e-mail uit het voorbeeld, joesmith@voorbeeld.com, zou de volgende tekenreeks worden doorgegeven in de SAML-bevestiging van de Azure IdP naar Blackboard Learn:

<Attribute Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6">
    <AttributeValue>joesmith</AttributeValue>

Aanvullende informatie over de functie ExtractMailPrefix() kun je lezen in dit artikel uit de MS Azure-documentatie.

Certificaat bijgewerkt door IdP van Azure AD

Na het invoeren van aanmeldingsreferenties op de aanmeldingspagina van MS Azure AD, kan er een Aanmeldingsfout! worden weergegeven nadat de omleiding naar de GUI van Blackboard Learn is gestart:

In het logboek bb-services wordt de volgende uitzondering opgenomen:

2016-10-13 12:03:23 +0800 - unsuccessfulAuthentication - org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
 at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100)
 at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
 at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
 at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
 at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
 at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
 at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
 at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
 at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
 at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
 at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
 at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
 at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
 at sun.reflect.GeneratedMethodAccessor854.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:498)
 at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
 at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
 at java.security.AccessController.doPrivileged(Native Method)
 at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
 at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
 at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
 at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:46)
 at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:148)
 at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:144)
 at java.security.AccessController.doPrivileged(Native Method)
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:143)
 at blackboard.auth.provider.saml.customization.filter.BbSAMLExceptionHandleFilter.doFilterInternal(BbSAMLExceptionHandleFilter.java:30)
 [SNIP]
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
 at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.common.SAMLException: Response doesn't have any valid assertion which would pass subject validation
 at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:229)
 at blackboard.auth.provider.saml.customization.consumer.BbSAMLWebSSOProfileConsumerImpl.processAuthenticationResponse(BbSAMLWebSSOProfileConsumerImpl.java:40)
 at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
 ... 230 more
Caused by: org.opensaml.xml.validation.ValidationException: Signature is not trusted or invalid
 at org.springframework.security.saml.websso.AbstractProfileBase.verifySignature(AbstractProfileBase.java:272)
 at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.verifyAssertionSignature(WebSSOProfileConsumerImpl.java:419)
 at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.verifyAssertion(WebSSOProfileConsumerImpl.java:292)
 at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:214)
 ... 232 more

Dit wordt veroorzaakt doordat de IdP van MS Azure AD het certificaat bijwerkt, maar de XML met metagegevens die wordt gebruikt door de SP van Blackboard Learn niet is aangepast op basis van het nieuwe certificaat.

Oplossing

  • Het nieuwe XML-bestand met de metagegevens met het nieuwe certificaat moet worden bijgewerkt op de pagina Instellingen voor SAML-verificatie in de GUI van Blackboard Learn voor de verificatieprovider.
  • De SAML B2- en de verificatieprovider moeten vervolgens worden geschakeld tussen Inactief/Beschikbaar, terwijl de SAML-verificatieprovider de status ‘Actief’ heeft, om ervoor te zorgen dat de bijgewerkte metagegevens met het nieuwe certificaat worden toegepast..
  • Als een Blackboard Learn-site verschillende verificatieproviders gebruikt die hetzelfde onderliggende certificaat delen voor dezelfde onderliggende entiteits-ID van de IdP, moeten al die verificatieproviders worden bijgewerkt.

Microsoft heeft aangegeven dat certificaten vanaf nu elke zes weken worden bijgewerkt, en dat deze updates niet worden aangekondigd.


Door IdP gestarte eenmalige aanmelding

Als een gebruiker zich eerst aanmeldt bij de gebruikersportal en vervolgens de app voor de Blackboard Learn-site selecteert, wordt er een nieuw tabblad geopend in de browser met dit bericht: De opgegeven bron is niet gevonden of je hebt geen toegang tot de bron.

Deze overeenkomende gebeurtenissen voor SAML worden vastgelegd in het logboek stdout-stderr:

INFO   | jvm 1    | 2016/08/16 10:49:22 | - /saml/SSO at position 1 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - HttpSession returned null object for SPRING_SECURITY_CONTEXT
INFO   | jvm 1    | 2016/08/16 10:49:22 | - No SecurityContext was available from the HttpSession: [email protected] A new one will be created.
INFO   | jvm 1    | 2016/08/16 10:49:22 | - /saml/SSO at position 2 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - /saml/SSO at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - /saml/SSO at position 4 of 10 in additional filter chain; firing Filter: 'FilterChainProxy'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/login/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/logout/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/bbsamllogout/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Checking match of request : '/saml/sso'; against '/saml/sso/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - /saml/SSO at position 1 of 1 in additional filter chain; firing Filter: 'SAMLProcessingFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Forwarding to /
INFO   | jvm 1    | 2016/08/16 10:49:22 | - DispatcherServlet with name 'saml' processing POST request for [/auth-saml/saml/SSO]
INFO   | jvm 1    | 2016/08/16 10:49:22 | - No mapping found for HTTP request with URI [/auth-saml/saml/SSO] in DispatcherServlet with name 'saml'
INFO   | jvm 1    | 2016/08/16 10:49:22 | - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Successfully completed request
INFO   | jvm 1    | 2016/08/16 10:49:22 | - Skip invoking on
INFO   | jvm 1    | 2016/08/16 10:49:22 | - SecurityContextHolder now cleared, as request processing completed

De sectie Instellingen serviceprovider van de pagina Instellingen voor SAML-verificatie is gewijzigd en de optie Automatische SSO inschakelen moet worden aangevinkt om gebruikers in staat te stellen vanuit hun portal toegang te krijgen tot Blackboard Learn. Als de optie is ingeschakeld, wordt de ACS URL ook gewijzigd en uitgebreid met een alias.


Fout door onjuist document

Na het invoeren van aanmeldingsreferenties op de aanmeldingspagina van de SAML-verificatieprovider, kan er een Aanmeldingsfout! worden weergegeven nadat de omleiding naar de GUI van Blackboard Learn is gestart:

In het logboek bb-services worden de volgende gebeurtenissen opgenomen: DOMException en WRONG_DOCUMENT_ERR:

2016-11-18 12:27:31 -0600 - WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.<P><span class="captionText">For reference, the Error ID is 86ebb81d-d3a3-4da5-95ab-1c94505f4281.</span&gt; - org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.
    at org.apache.xerces.dom.ParentNode.internalInsertBefore(Unknown Source)
    at org.apache.xerces.dom.ParentNode.insertBefore(Unknown Source)
    at org.apache.xerces.dom.NodeImpl.appendChild(Unknown Source)
    at org.opensaml.xml.encryption.Decrypter.parseInputStream(Decrypter.java:832)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:610)
    at org.opensaml.xml.encryption.Decrypter.decryptUsingResolvedEncryptedKey(Decrypter.java:795)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:535)
    at org.opensaml.xml.encryption.Decrypter.decryptDataToList(Decrypter.java:453)
    at org.opensaml.xml.encryption.Decrypter.decryptData(Decrypter.java:414)
    at org.opensaml.saml2.encryption.Decrypter.decryptData(Decrypter.java:141)
    at org.opensaml.saml2.encryption.Decrypter.decrypt(Decrypter.java:69)
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:199)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:82)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:87)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at sun.reflect.GeneratedMethodAccessor1209.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:277)
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:274)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:309)
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:249)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:237)
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:55)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:191)
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:187)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:186)
    at blackboard.platform.servlet.DevNonceFilter.doFilter(DevNonceFilter.java:68)
    [SNIP]

De oorzaak van dit probleem is dat een ander B2/Project de waarde van de systeemeigenschap javax.xml.parsers.DocumentBuilderFactory heeft gewijzigd van org.apache.xerces.jaxp.DocumentBuilderFactoryImpl in com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl.

Tijdelijke oplossing

Totdat er een correctie beschikbaar komt, zijn dit de tijdelijke oplossingen:

  1. Bb-services opnieuw starten op elk knooppunt.
  2. Codering van SAML-antwoorden aan de kant van de IdP uitschakelen. Aangezien de communicatie volledig verloopt via SSL, heeft dit geen gevolgen voor de beveiliging van de verificatie.

Geen optie om SAML toe te voegen aan providervolgorde

Bij het configureren van SAML-verificatie is het mogelijk dat er geen optie beschikbaar is voor het toevoegen van een SAML-verificatieprovider in de sectie Volgorde van providers in de GUI van Blackboard Learn via Systeembeheer > Building Blocks: Verificatie > Volgorde van providers.

De reden waarom er geen optie is om een SAML-verificatieprovider toe te voegen aan Volgorde van providers is dat providers die op basis van omleiding werken, zoals CAS en SAML, verificatie overdragen aan de externe verificatiebron. Deze worden niet vermeld bij Volgorde van providers aangezien ze worden beschouwd als de gezaghebbende bron voor verificatie en ze hun eigen verificatiefouten afhandelen.


SAML-verbinding testen

Vanaf de release van Blackboard Learn van het vierde kwartaal (Q4) van 2016 is er een nieuwe optie voor het testen van de verbinding voor een SAML-provider, te vinden in de sectie Verificatie in de GUI van Blackboard Learn. Tijdens de verbindingstest worden de volgende items gecontroleerd:

  • IdP-metagegevens parseren
  • Verbinden met IdP
  • SAML-antwoord ontvangen
  • SAML-antwoord parseren
  • Overeenkomst externe gebruikers-ID
  • Aanmelden bij Blackboard Learn

Ga als volgt te werk om de verbinding voor een SAML-verificatieprovider te testen:

  1. Meld je als beheerder aan bij Blackboard Learn.
  2. Ga naar Systeembeheer > Building Blocks: Verificatie > "naam van SAML-provider" > Verbinding testen.
  3. Voer de aanmeldingsreferenties voor de IdP in als daarom wordt gevraagd.

De functie Verbinding testen kan om verschillende redenen worden gebruikt in plaats van het handmatig inschakelen van logboekregistratie voor SAML-foutopsporing in Blackboard Learn.

De waarde die voor ID van identiteitsproviderentiteit wordt weergegeven op de uitvoerpagina van de functie Verbinding testen wordt opgehaald uit het element Issuer in de SAML POST van de IdP aan Blackboard Learn nadat de gebruiker is geverifieerd.

<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://bbpdcsi-adfs1.bbpdcsi.local/a...services/trust</Issuer>

De waarden voor SAML-kenmerk die worden weergegeven op de uitvoerpagina van de functie Verbinding testten in de sectie SAML-antwoord worden opgehaald uit de elementen Subject en AttributeStatement in de SAML POST van de IdP aan Blackboard Learn nadat de gebruiker is geverifieerd:

<Subject>
    <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">luke.skywalker</NameID>
    [SNIP]
</Subject>
<AttributeStatement>
    <Attribute Name="SamAccountName">
        <AttributeValue>luke.skywalker</AttributeValue>
    </Attribute>
    <Attribute Name="urn:oid:2.5.4.42">
        <AttributeValue>Luke</AttributeValue>
    </Attribute>
    <Attribute Name="urn:oid:2.5.4.4">
        <AttributeValue>Skywalker</AttributeValue>
    </Attribute>
</AttributeStatement>


Een SAML-verificatieprovider en IdP maken voor testdoeleinden

Gebruik de onderstaande stappen om een IdP (identiteitsprovider) te maken met de gratis oplossing voor SSO-verificatie van Centrify.

Die IdP kan vervolgens worden geconfigureerd als de SAML-verificatieprovider in een SP (serviceprovider) voor Blackboard Learn:

Blackboard Learn-serviceprovider

  1. Meld je als beheerder aan bij de GUI van Blackboard Learn en ga naar Systeembeheer > Verificatie.
  2. Selecteer Provider maken > SAML.
  3. Geef waarden op voor de volgende instellingen:
    • Naam > SAML of een andere naam.
    • Verificatieprovider > Inactief (voorlopig).
    • Zoekmethode gebruikers > Gebruikersnaam
    • Beperken op hostnaam > Deze provider gebruiken voor alle hostnamen
    • Koppelingstekst > SAML Centrify-aanmelding
  4. Selecteer Opslaan en configureren.
  5. In het veld Entity ID kun je elke gewenste waarde invoeren (als je de ID later wijzigt, moet je de bijgewerkte metagegevens voor de serviceprovider wel doorgeven aan de identiteitsprovider). Dit kan door eenvoudigweg door de ACS URL te kopiëren en plakken.
  6. Selecteer Genereren onder Metadata serviceprovider en sla het bestand op je computer op.
  7. Het wordt aangeraden om onder Gegevensbron een nieuwe gegevensbron te maken voor deze serviceprovider met de naam CENTRIFY, of anders SYSTEEM of een andere naam.
  8. Vink het vakje JIT-inrichting inschakelen aan, zodat er automatisch een account wordt gemaakt als er wordt geprobeerd om aan te melden via deze SAML-verificatieprovider en de gebruiker niet bestaat. Als deze optie niet is ingeschakeld, moet de gebruiker handmatig worden gemaakt in Blackboard Learn.
  9. Selecteer in de lijst Compatibele gegevensbronnen de gegevensbronnen waarmee deze verificatieprovider compatibel moet zijn.
  10. Selecteer Identiteitsprovider aanwijzen voor Type van de identiteitsprovider.
  11. Sla Metadata identiteitsprovider nu even over. Je gaat het bestand uploaden nadat dit is gemaakt in de volgende sectie.
  12. Geef in de sectie SAML-kenmerken in kaart brengen NameID op voor Externe gebruikers-ID.
  13. Selecteer Verzenden.

Identiteitsprovider van Centrify

  1. Ga naar https://www.centrify.com/express/identity-service en selecteer Start Now in de kolom Express onder aan de pagina.
  2. Voer de aanmeldingsgegevens in en selecteer Start Now.
  3. Je krijgt een welkomstmail met je beheerdersreferenties. Gebruik deze om je aan te melde bij https://cloud.centrify.com.
  4. Selecteer Skip in het venster Welcome to Centrify Identify Service.
  5. Ga naar het tabblad Apps boven aan de pagina en selecteer de knop Add Web Apps.
  6. Scrol omlaag op het tabblad Custom en selecteer de knop Add voor SAML. Selecteer Ja.
  7. Selecteer Close onder aan het venster Add Web Apps.
  8. Ga naar het tabblad Apps. Selecteer in de sectie Application Settings de knop Upload SP Metadata en upload het bestand dat is gemaakt in stap 6 uit de vorige sectie.
  9. Het veld Assertion Consumer Service URL moet automatisch worden ingevuld nadat de SP-metagegevens zijn geüpload.
  10. Schakel Encrypt Assertion uit. Hierdoor is het mogelijk om de kenmerken die worden vrijgegeven door de IdP en die worden verstuurd naar Blackboard Learn, te bekijken met de Firefox-invoegtoepassing SAML tracer of SAML Message Decoder voor Chrome. Aangezien de communicatie volledig verloopt via SSL, heeft dit geen gevolgen voor de beveiliging van de verificatie.
  11. Scrol omlaag en selecteer Download Identity Provider SAML Metadata. Bewaar het bestand op je computer.
  12. Selecteer Save en ga naar de volgende sectie.
  13. Voer een waarde in voor Description. Selecteer Save en ga naar de volgende sectie.
  14. Selecteer Everybody en System Administrator in de sectie User Acces. Selecteer Opslaan.
  15. Laat de sectie Policy ongewijzigd.
  16. Controleer in de sectie Account Mapping of userprincipalname is ingevoerd voor de veldnaam Directory Service.
  17. Voeg in de sectie Advanced de volgende regel toe aan het einde van het script dat wordt gebruikt om een SAML-bevestiging te genereren voor de toepassing:

    Het volledige script ziet er dan zo uit:

    setIssuer(Issuer);
    setSubjectName(UserIdentifier);
    setAudience('https://YourLearnServer.blackboard.c...saml/saml/SSO');
    setRecipient(ServiceUrl);
    setHttpDestination(ServiceUrl);
    setSignatureType('Assertion');
    setNameFormat('emailaddress');
    setAttribute("NameID", LoginUser.Get("userprincipalname"));

    Hierdoor kan de Centrify-IdP een AttributeStatement vrijgeven met de User ID in de SAML POST.

    Voorbeeld:

    <AttributeStatement>
        <Attribute Name="NameID"
                   NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
                   >
            <AttributeValue>luke.skywalker@blackboard.com.47</AttributeValue>
        </Attribute>
    </AttributeStatement>

    Meer informatie over het opgeven van bevestigingselementen in het Centrify SAML-script

  18. Selecteer Opslaan.
  19. De overige secties kunnen ongewijzigd blijven (App Gateway, Changelog en Workflow).
  20. Controleer op het tabblad Apps of de SAML-app automatisch is geïmplementeerd.
  21. Selecteer Add Users op het tabblad Users, voer de accountgegevens voor een gebruiker in en selecteer Create User.
  22. Meld je weer als beheerder aan bij de GUI van Blackboard Learn, ga naar Systeembeheer > Verificatie > naam van SAML-verificatieprovider > Instellingen voor SAML-verificatie > Instellingen serviceprovider, upload het bestand met IdP-metagegevens dat je in stap 12 hebt opgeslagen op je computer en selecteer Verzenden.

De nieuwe Centrify IdP-gebruiker kan zich nu aanmelden bij Blackboard Learn via SAML door die verificatieprovider te selecteren op de aanmeldingspagina. Afmelden bij Blackboard Learn kan via de extra afmeldknop SSO-sessie beëindigen op de pagina Alle sessies beëindigen? die wordt weergegeven na het kiezen van de afmeldknop in de rechterbovenhoek van Blackboard Learn.


Tekst wijzigen op afmeldingspagina SSO-sessie beëindigen

Een instelling kan desgewenst de tekst op de afmeldingspagina SSO-sessie beëindigen aanpassen. Dit kan door het taalpakket te bewerken:

  1. Open het bestand met het taalpakket.
  2. Ga naar auth-provider-saml/src/main/webapp/WEB-INF/bundles/bb-manifest-en_US.properties.
  3. Pas deze berichtsleutels aan:

    saml.single.logout.warning.conent.description // the first line
    saml.single.logout.warning.conent.recommend // second line
    saml.single.logout.warning.endsso.title // third line
    saml.single.logout.warning.endsso.button // the button
    saml.single.logout.warning.backtolearn // the cancel button


Gebruikers omleiden naar de IdP-aanmeldingspagina

De standaardaanmeldingspagina van Blackboard Learn bevat velden voor een gebruikersnaam en wachtwoord voor de interne verificatieprovider van Learn. Wanneer je SAML-verificatie inschakelt, wordt er onder aan de pagina een kleine koppeling "Aanmelden met behulp van..." toegevoegd voor SAML. Maar je hebt ook de mogelijkheid om gebruikers automatisch om te leiden naar de verificatieserver van de verificatie wanneer ze de aanmeldingspagina van Learn openen.

Dit kan bijvoorbeeld door naar Systeembeheer > Verificatie te gaan en de interne verificatieprovider van Learn op Inactief te zetten. Er wordt dan geen aanmeldingspagina meer weergegeven en de gebruiker wordt direct omgeleid naar de SAML-aanmelding. Het probleem met deze optie is dat de standaard-URL voor aanmelding wordt genegeerd, waardoor alleen SAML-gebruikers zich kunnen aanmelden.

Om dit probleem te voorkomen en bijna hetzelfde resultaat te bieden, kun je een aangepaste aanmeldingspagina gebruiken. Gebruikers worden omgeleid naar de aanmeldingspagina van de IdP van de SAML-verificatieprovider, maar de standaardkoppeling voor aanmelden is ook beschikbaar.

  1. Zorg ervoor dat de interne verificatie van Learn actief is
  2. Kopieer op de standaardaanmeldingspagina de locatie van de provideromleiding, bijvoorbeeld Aanmelden met behulp van... SAML. Klik met de rechtermuisknop op de koppeling en selecteer Koppelingslocatie kopiëren.
  3. Ga naar Systeembeheer > Communities > Opmaken en thema's > Aanmeldingspagina aanpassen.
  4. Selecteer Downloaden naast Standaardaanmeldingspagina om het standaard JSP-bestand voor aanmelden te downloaden.
  5. Open het JSP-bestand met een teksteditor. Voeg de volgende HTML-voorbeeldcode toe aan het JSP-aanmeldingsbestand en vervang de URL-tekst door de URL die je in stap 2 hebt gekopieerd.

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <html>
    <head>
    <title>Blackboard Learn - Redirect</title>
    <meta http-equiv="REFRESH" content="0;url=https://URL_Goes_Here"></HEAD>
    <BODY style="font-family: arial,sans-serif;font-size: small; color: grey; padding: 1em; ">
    Redirecting... <a style="color:grey" href="https://URL_Goes_Here">Go to login page</a&gt; if you are not automatically redirected.
    </BODY>
    </HTML>

  6. Ga opnieuw naar Aanmeldingspagina aanpassen in Learn. Selecteer Aangepaste pagina gebruiken en upload het bijgewerkte JSP-aanmeldingsbestand.
  7. Selecteer na het aanbrengen van de wijzigingen Voorbeeld op de pagina Aanmeldingspagina aanpassen om te controleren of de omleiding goed werkt.

Gebruikers die naar de hoofd-URL gaan, worden nu omgeleid naar de aanmeldingspagina voor de SAML-verificatieprovider. Beheerders kunnen zich nog steeds aanmelden met behulp van de interne verificatieprovider van Learn via de standaardaanmeldingspagina: /webapps/login/?action=default_login of/webapps/login/login.jsp).

Meer informatie over het aanpassen van de aanmeldingspagina in de Ultra-ervaring