Esta página apresenta uma visão geral do Building Block Security Assertion Markup Language (SAML) 2.0 junto com questões comuns de logon único (SSO) e técnicas de resolução de problemas para o provedor de autenticação SAML.

Se, por qualquer motivo, um arquivo XML de metadados de IdP atualizado/novo for enviado para a GUI do Blackboard Learn na página de Configurações de autenticação SAML na seção Configurações do provedor de identidade de um provedor de autenticação SAML, o provedores de autenticação SAML e o SAML B2 também deverão ser alternados para Inativo/Disponível, enquanto o status do provedor de autenticação SAML estiver como ‘Ativado’, para garantir que os metadados IdP em cache sejam eliminados e os metadados IdP atualizados sejam totalmente utilizados.


Principais termos

Os seguintes termos e abreviações são usados em todo este guia:

  • SAML: Security Assertion Markup Language
  • IdP: provedor de identidade
  • SP: provedor de serviço
  • ADFS: Serviços de Federação do Active Directory
  • GUI: interface gráfica do usuário. No contexto do Blackboard Learn, isso significa trabalhar dentro do software.

Editar configurações de SAML

Para ajudar a solucionar problemas de autenticação SAML, o Building Block SAML foi atualizado na versão 3200.2.0 para incluir estas opções e definições de configuração:

  • Definir o limite de idade da sessão SAML
  • Escolher um tipo de algoritmo de assinatura
  • Gerar novamente os certificados
  • Alterar o valor de ResponseSkew

Saiba mais sobre como definir as configurações no Building Block SAML


Erros e exceções

Erros/exceções relacionados a SAML são capturados nos seguintes registros:

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

Esses registros devem sempre ser pesquisados ao investigar um problema de autenticação SAML relatado.


Rastreador SAML

Com iterações de resolução de problemas de autenticação SAML 2.0, poderá ser necessário, em algum ponto, confirmar/visualizar os atributos que estão de fato sendo liberados do IdP e enviados ao Learn durante o processo de autenticação. Se os atributos do IdP NÃO estiverem criptografados na resposta SAML, o Complemento rastreador de SAML do navegador Firefox ou o Decodificador de mensagens SAML do Chrome poderá ser usado para visualizar os atributos.


Atributo não mapeado adequadamente

Se o atributo contendo o userName não estiver adequadamente mapeado conforme especificado no campo Código do usuário remoto na seção Mapear atributos SAML na página Configurações de autenticação SAML na GUI do Blackboard Learn, o seguinte evento será registrado no log bb-services ao tentar fazer logon no Blackboard Learn via autenticação SAML:

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

Uma mensagem similar de Erro ao efetuar logon! exibida no navegador: O Blackboard Learn está indisponível para fazer logon na sua conta no momento usando logon único. Para obter ajuda, entre em contato com seu administrador.

An image of a Sign On Error message displayed in the browser that says Blackboard Learn is currently unable to log into your account using single-sing on. Contact your administrator for assistance.

Uma entrada de Falha de autenticação aparece no registro do bb-services:

2016-06-28 12:48:12 -0400 – BbSAMLExceptionHandleFilter – javax.servlet.ServletException: Falha na autenticação
    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]

Resolução

Você tem duas opções para resolver o problema. Primeiro, selecione a opção Criar contas se elas não existirem no sistema na página Configurações de autenticação SAML na GUI do Blackboard Learn. Como alternativa, você pode tentar visualizar o valor dos atributos liberados pelo IdP via rastreador SAML ou Registro de depuração se os atributos NÃO estiverem criptografados:


    www.w3.org/2001/XMLSchema"
                          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                          xsi:type="xs:anyType"
                          >bbuser_saml2@bbchjones.net

e mapear o Attribute Name que tem o AttributeValue desejado para o Código de usuário remoto na página Configurações de autenticação SAML na GUI do Blackboard Learn.


Fonte de dados compatível não selecionada

Os usuários não poderão fazer logon no Blackboard Learn usando autenticação SAML se a Fonte de dados para os usuários não estiver selecionada na seção Configurações do provedor de serviços > Fontes de dados compatíveis na página Configurações de autenticação SAML na GUI do Blackboard Learn. O evento a seguir será registrado no bb-services quando for feita uma tentativa de logon no Blackboard Learn via autenticação SAML:

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

A mensagem Erro ao efetuar logon! aparecerá no navegador, bem como Falha de autenticação no registro do bb-services:

2016-09-23 12:33:13 -0500 – BbSAMLExceptionHandleFilter – javax.servlet.ServletException: Falha na autenticação
    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]

Resolução

  1. Obter o nome de usuário para o usuário que não consegue fazer logon.
  2. Na GUI do Blackboard Learn, acesse Administração do sistema > Usuários e busque o usuário.
  3. Copie a chave de fonte de dados do usuário.
  4. Navegue até Administração do sistema > Autenticação > "Nome do provedor" > Configurações de SAML > Fontes de dados compatíveis.
  5. Coloque uma marca de seleção ao lado daquela Fonte de dados na coluna Nome e selecione Enviar.

Mensagem de erro "O URL fornecido não está bem formado"

Se o OneLogon for configurado como o IdP para o provedor de autenticação SAML no Blackboard Learn, um erro de O URL fornecido não está bem formado poderá ser exibido na página depois de inserir as credenciais do OneLogon ao tentar fazer logon no Blackboard Learn.

Com o seguinte exibido no bb-services-log:

2016-09-16 09:43:40 -0400 – O URL fornecido não está bem formado

Para referência, o código de erro é 17500f44-7809-4b9f-a272-3bed1d1af131. – java.lang.IllegalArgumentException:O O URL fornecido não está bem formado
    at org.opensaml.util.URLBuilder.(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: sem protocolo: {recipient}
    at java.net.URL.(URL.java:593)
    at java.net.URL.(URL.java:490)
    at java.net.URL.(URL.java:439)
    at org.opensaml.util.URLBuilder.(URLBuilder.java:77)
        ... 203 mais

Resolução

  1. Ligue o rastreador SAML do navegador Firefox e replique o problema de logon.
  2. Verifique o início do evento POST SAML:

            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"
            >
        https://app.onelogin.com/saml/metadata/123456
        [SNIP]

  3. Para a linha 1 com a Resposta, observe que Destino= é definido somente para o destinatário.
  4. Peça para o cliente acessar a seção Configuração do seu IdP do OneLogon.
  5. Verifique se o campo Destinatário está em branco.
  6. Copie o valor do URL do ACS (consumidor), cole-o no campo Destinatário e selecione Salvar.

Cenários de problema de IdP/SP

  1. Se aparecer um erro depois de você ter sido redirecionado à página de logon do IdP, os metadados do IdP poderão ser inválidos.
  2. Se aparecer um erro depois de você fazer logon na página do IdP, os motivos poderão ser:
    1. O mapeamento de atributos entre o SP e o IdP está incorreto, ou o IdP não retornou um código de usuário remoto válido.
    2. A resposta SAML do IdP não foi validada pelo SP. Isso pode ter sido causado por:
      • O IdP assina a resposta SAML com um certificado que não é emitido por uma autoridade de certificação válida, e o repositório de chaves do SP não contém esse certificado.
      • O relógio do sistema do SP está incorreto.

Serviços de Federação do Active Directory (ADFS)

Os nomes de atributo diferenciam maiúsculas e minúsculas na seção Mapear atributos SAML na página Configurações de autenticação SAML na GUI do Blackboard Learn. Assim, se o Código de usuário remoto tiver sAMAccountName para o Nome do atributo na página de configurações e o SAML POST real do IdP tiver isso para o Nome do atributo em AttributeStatement:


   
        Test-User
   

O usuário não poderá fazer logon. O valor de nome do atributo do Código de usuário remoto na página Configurações de autenticação SAML precisaria ser alterado de sAMAccountName para SamAccountName.

Aviso de "Recurso não encontrado" ou "Erro ao efetuar logon!"

Essa seção contém alguns dos problemas comuns que podem impedir que um usuário faça logon no Learn via autenticação SAML com o ADFS quando a mensagem O recurso especificado não foi encontrado ou você não tem permissão para acessá-lo ou Erro ao efetuar logon! é exibida na GUI do Blackboard Learn.

Problema nº 1

Depois de inserir as credenciais de logon na página de logon do ADFS, poderá ser exibido um erro após o redirecionamento para a GUI do Blackboard Learn: O recurso especificado não foi encontrado ou você não tem permissão para acessá-lo.

A specified resource was not found error message

Com uma mensagem correspondente no registro stdout-stderr:

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'

O problema ocorre porque o método noHandlerFound() é usado no código DispatcherServlet.java e não é capaz de localizar/mapear a solicitação de SSO HTTP.

/**
 * No handler found -> 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);
 }
}

Resolução

Isso geralmente ocorre porque o código da entidade para o SP configurado na GUI do Blackboard Learn está incorreta. Isso pode ser resolvido acessando Administração do sistema > Autenticação > Configurações de autenticação SAML > Configurações do provedor de serviços e atualizando o Código da entidade. Para o ADFS, a configuração padrão para o Código da entidade seria https://[Learn Server Hostname]/auth-saml/saml/SSO.

Se uma escola mudar o URL do https://school.blackboard.com padrão para https://their.school.edu, o código da entidade na GUI do Blackboard Learn na página Configurações de autenticação SAML deveria ser atualizada para https://their.school.edu/auth-saml/saml/SSO.

Problema nº 2

Depois de inserir as credenciais de logon na página de logon do ADFS, poderá ser exibido um erro após o redirecionamento para a GUI do Blackboard Learn: O recurso especificado não foi encontrado ou você não tem permissão para acessá-lo.

Com esta mensagem correspondente no registro stdout-stderr:

INFO  | jvm 1  | 2016/06/22 06:08:33 | – Nenhum mapeamento encontrado para a solicitação HTTP com URI [/auth-saml/saml/SSO] no DispatcherServlet com nome 'saml'

E esta mensagem no registro catalina:

ERROR 2016-06-27 10:47:03,664 connector-6: userId=_2_1, sessionId=62536416FB80462298C92064A7022E50 org.opensaml.xml.encryption.Decrypter – Erro ao descriptografar o elemento de dados criptografados
org.apache.xml.security.encryption.XMLEncryptionException: Tamanho da chave ilegal
A exceção original foi java.security.InvalidKeyException: Tamanho da chave ilegal
    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: Tamanho da chave ilegal
    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 mais

E esta mensagem exibida no registro bb-services:

2016-06-27 10:47:03 -0400 – unsuccessfulAuthentication – org.springframework.security.authentication.AuthenticationServiceException: Erro ao validar a mensagem SAML
    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: A resposta não tem nenhuma afirmação válida que passe na validação do assunto
    at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:229)
    at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87)
        ... 229 mais

O problema ocorre porque, por padrão, o ADFS criptografa os atributos que ele envia usando AES-256 e o tempo de execução Java usado pelo Blackboard Learn não tem suporte de fábrica para AES-256.

Resolução

Uma opção de resolução universal é abrir um PowerShell no servidor ADFS e definir a parte confiável criada para o Blackboard Learn para enviar os atributos como não criptografados. Uma vez que a comunicação como um todo é por SSL, isso não reduzirá a segurança da autenticação. Também torna a depuração de quaisquer problemas mais fácil, uma vez que os atributos podem ser visualizados usando ferramentas de depuração, como o complemento de rastreador SAML do navegador Firefox e uma reinicialização do sistema Blackboard Learn não é necessária. Para definir a parte confiável criada para o Blackboard Learn enviar os atributos não criptografados, abra um PowerShell e execute o comando a seguir, substituindo TargetName pelo nome da Confiança da terceira parte confiável que está no Console de gerenciamento do ADFS em Relacionamentos de confiança > Confianças de parte confiável.

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

Depois dessa alteração, será preciso reiniciar o serviço do ADFS com o comando: Restart-Service ADFSSRV

Problema nº 3

Depois de inserir as credenciais de logon na página de logon do ADFS, poderá ser exibido um erro após o redirecionamento para a GUI do Blackboard Learn: O recurso especificado não foi encontrado, ou você não tem permissão para acessá-lo, ou Erro ao efetuar logon!

Com qualquer uma das opções, estes eventos relacionados SAML com correspondências similares aparecem no registro stdout-stderr:

INFO   | jvm 1    | 2016/09/06 20:33:04 | – /saml/logon?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 | – Não existe nenhum HttpSession no momento
INFO   | jvm 1    | 2016/09/06 20:33:04 | – Nenhum SecurityContext estava disponível de HttpSession: zero. Um novo será criado.
INFO   | jvm 1    | 2016/09/06 20:33:04 | – /saml/logon?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/logon?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/logon?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 | – Confira a correspondência da solicitação: '/saml/logon'; against '/saml/logon/**'
INFO   | jvm 1    | 2016/09/06 20:33:04 | – /saml/logon?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 | – Solicitação de URI http://www.w3.org/2000/09/xmldsig#rsa-sha1
INFO   | jvm 1    | 2016/09/06 20:33:04 | – Solicitação de URI http://www.w3.org/2000/09/xmldsig#rsa-sha1
INFO   | jvm 1    | 2016/09/06 20:33:04 | – SecurityContext está vazio ou o conteúdo é anônimo – o contexto não será armazenado em HttpSession.
INFO   | jvm 1    | 2016/09/06 20:33:04 | – SecurityContextHolder agora limpo, assim como o processamento da solicitação foi concluído
INFO   | jvm 1    | 2016/09/06 20:33:07 | – /saml/SSO na posição 1 de 10 na cadeia de filtro adicional; filtro de disparo: 'SecurityContextPersistenceFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | – HttpSession retornou objeto nulo para SPRING_SECURITY_CONTEXT
INFO   | jvm 1    | 2016/09/06 20:33:07 | – Nenhum SecurityContext estava disponível em HttpSession: org.apache.catalina.session.StandardSessionFacade@6708a718. Um novo será criado.
INFO   | jvm 1    | 2016/09/06 20:33:07 | – /saml/SSO na posição 2 de 10 na cadeia de filtro adicional; filtro de disparo: 'WebAsyncManagerIntegrationFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | – /saml/SSO na posição 3 de 10 na cadeia de filtro adicional; filtro de disparo: 'HeaderWriterFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | – /saml/SSO na posição 4 de 10 na cadeia de filtro adicional; filtro de disparo: 'FilterChainProxy'
INFO   | jvm 1    | 2016/09/06 20:33:07 | – Confira a correspondência da solicitação: '/saml/sso'; against '/saml/logon/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | – Confira a correspondência da solicitação: '/saml/sso'; against '/saml/logout/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | – Confira a correspondência da solicitação: '/saml/sso'; against '/saml/bbsamllogout/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | – Confira a correspondência da solicitação: '/saml/sso'; against '/saml/sso/**'
INFO   | jvm 1    | 2016/09/06 20:33:07 | – /saml/SSO na posição 1 de 1 na cadeia de filtro adicional; filtro de disparo: 'SAMLProcessingFilter'
INFO   | jvm 1    | 2016/09/06 20:33:07 | – Tentativa de autenticação usando org.springframework.security.saml.SAMLAuthenticationProvider
INFO   | jvm 1    | 2016/09/06 20:33:07 | – Encaminhamento para /
INFO   | jvm 1    | 2016/09/06 20:33:07 | – DispatcherServlet com nome 'saml' processando a solicitação POST para [/auth-saml/saml/SSO]
INFO   | jvm 1    | 2016/09/06 20:33:07 | – Nenhum mapeamento encontrado para a solicitação HTTP com URI [/auth-saml/saml/SSO] em DispatcherServlet com nome 'saml'
INFO   | jvm 1    | 2016/09/06 20:33:07 | – SecurityContext está vazio ou o conteúdo é anônimo – o contexto não será armazenado em HttpSession.
INFO   | jvm 1    | 2016/09/06 20:33:07 | – Solicitação concluída com sucesso
INFO   | jvm 1    | 2016/09/06 20:33:07 | – Ignorar chamada em
INFO   | jvm 1    | 2016/09/06 20:33:07 | – SecurityContextHolder agora removido, conforme processamento de solicitação concluído

Ou estas exceções SAML similares no registro bb-services:

2016-11-29 09:04:24 -0500 – unsuccessfulAuthentication – org.springframework.security.authentication.AuthenticationServiceException: Erro ao validar a mensagem SAML
    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)
Causado por: org.opensaml.common.SAMLException: O tempo de emissão de resposta é muito antigo ou com data futura, inclinação 60, hora 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 mais

O problema ocorre quando o servidor ADFS e o servidor de aplicativo Blackboard Learn têm um deslocamento de tempo perto do padrão de 60 segundos ou superior a ele.

Resolução

Existem duas opções para resolver o problema:

  1. Sincronizar manualmente os relógios dos servidores de aplicativo do Blackboard Learn e do servidor ADFS. Para o Blackboard Learn, a hora e o fuso horário atuais do servidor podem ser visualizados em um navegador da web adicionando /webapps/portal/healthCheck ao final de um URL do Blackboard Learn.

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

    Nome de host: ip-10-145-49-11.ec2.internal
    Status: Ativo – Conectividade de banco de dados estabelecida
    Em execução desde: Sáb, 3 de dez de 2016 – 05:39:11 PM
    Hora da solicitação: Qui, 8 de dez de 2016 – 05:12:43 PM EST

    Uma instituição pode usar o URL acima para comparar o relógio e o fuso horário do sistema do Blackboard Learn com aqueles do servidor ADFS e então ajustar esses itens conforme necessário no servidor ADFS para eles estejam sincronizados ao site do Blackboard Learn.

Problema nº 4

Depois de inserir as credenciais de logon na página de logon do ADFS, poderá ser exibido um erro após o redirecionamento para a GUI do Blackboard Learn: O recurso especificado não foi encontrado, ou você não tem permissão para acessá-lo, ou Erro ao efetuar logon!

Com as seguintes exceções no registro bb-services:

2016-11-01 12:47:19 -0500 – unsuccessfulAuthentication – org.springframework.security.authentication.AuthenticationServiceException: Erro ao validar a mensagem SAML
    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)
Causado por: org.opensaml.common.SAMLException: A resposta tem um código de status inválido urn:oasis:names:tc:SAML:2.0:status:Responder, a mensagem de status é nula
    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 mais
 2016-11-01 12:47:19 -0500 – BbSAMLExceptionHandleFilter – javax.servlet.ServletException: Autenticação malsucedida
         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]

Resolução
  1. Navegue até o Painel do administrador.
  2. Em Building Blocks, selecione Building Blocks.
  3. Selecione Ferramentas instaladas.
  4. Localize Provedor de autenticação – SAML na lista. Abra o menu e selecione Configurações.
  5. Em Configurações do algoritmo de assinatura, escolha SHA-256 na lista. Depois de selecionar o Tipo de algoritmo de assinatura, reinicie o Building Block SAML para aplicar as novas configurações.
  6. Clique em Enviar para salvar as alterações.

Problema nº 5

Depois de inserir as credenciais de logon na página de logon do ADFS, poderá ser exibido um erro após o redirecionamento para a GUI do Blackboard Learn: O recurso especificado não foi encontrado, ou você não tem permissão para acessá-lo, ou Erro ao efetuar logon!

Com as seguintes exceções no registro bb-services:

2017-01-04 22:52:58 -0700 – unsuccessfulAuthentication – org.springframework.security.authentication.AuthenticationServiceException: Erro ao validar a mensagem SAML
    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)
Causado por: org.opensaml.common.SAMLException: O elemento NameID deve estar presente como parte do Assunto na mensagem de Resposta, habilite-o na configuração IDP
    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 mais

Conforme definido na exceção SAML acima, o elemento NameID está ausente do Assunto na mensagem Resposta. O problema costuma ocorrer quando a NameID não configurada como um Tipo de declaração de saída em uma Regra de declarações para a Confiança da parte confiável no IdP do ADFS da instituição ou a Regra de declarações para a NameID não está na ordem adequada para a Confiança da parte confiável no IdP do ADFS da instituição, o que, por sua vez, faz o elemento NameID estar ausente no Assunto na mensagem de Resposta.

Exemplo: o elemento NameID está ausente


   
                                         NotOnOrAfter="2017-01-03T05:57:58.234Z"
                                 Recipient="https://yourschool.blackboard.com/auth-saml/saml/SSO"
                                 />
   

Exemplo: o elemento NameID está presente


    testadfs
   
                                         NotOnOrAfter="2017-01-05T04:33:12.715Z"
                                 Recipient="https://yourschool.blackboard.com/auth-saml/saml/SSO"
                                 />
   

Você pode usar o complemento rastreador de SAML do Firefox para visualizar o Assunto na mensagem de Resposta.

Resolução

Existem três métodos para resolver esse problema.

  1. Confirme se as etapas do Guia de configuração SAML B2 para ADFS foram adequadamente seguidas e faça as alterações necessárias para transformar uma declaração de chegada para a Confiança da terceira parte confiável para o IdP do ADFS:
    1. Selecione Editar regra de declarações.
    2. Selecione Adicionar regra.
    3. Na página Selecionar modelo de regra, selecione Transformar uma declaração de entrada para o modelo de regra de declaração e, em seguida, selecione Avançar.
    4. Na página Configurar regra, no campo Nome de regra de declaração, digite Transformar e-mail em código de nome.
    5. O tipo de declaração de entrada deve ser SamAccountName (deve corresponder ao Tipo de declaração de saída criado inicialmente na regra Transformar nome de usuário em NameID).
    6. O Tipo de declaração de saída é o código do nome.
    7. O formato de código de nome de saída é E-mail.
    8. Confirme que Passar por todos os valores de declaração está selecionado e selecione Concluir.
    9. Selecione OK para salvar a regra e OK novamente para concluir os mapeamentos de atributo.
  2. Para a ordem de Regras de declarações usadas para o IdP do ADFS, garanta que nenhuma regra opcional ocorra antes da regra com o elemento NameID.
  3. Se estiver usando um atributo personalizado, garanta que o elemento NameID esteja na Confiança da terceira parte confiável já que o Learn ainda espera que seu IdP do ADFS libere um valor de NameID.

Problema nº 6

Quando conectado ao Blackboard Learn usando autenticação SAML, o usuário tenta fazer logout clicando no botão Sair no lado esquerdo da página e então clica no botão Encerrar sessão de SSO, um Erro ao efetuar logon! é exibido imediatamente.

Erro de sign-on!
O Blackboard Learn está indisponível para fazer logon na sua conta no momento usando single sign-on. Para obter ajuda, entre em contato com seu administrador.
Para referência, o código de erro é [error ID].

Com a seguinte exceção no registro bb-services:

2017-05-08 15:10:46 -0400 – Código de erro BbSAMLExceptionHandleFilter: f3299757-8d4e-4fab-98cf-49cd99f4891e – javax.servlet.ServletException: Falha na validação de segurança da mensagem SAML recebida
    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)
Causado por: org.opensaml.ws.security.SecurityPolicyException: Falha na validação da assinatura simples de solicitação para emissor de contexto
    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 mais

O erro ocorre devido à configuração Tipo de serviço de logout único na página Configurações de SAML.

Resolução

A configuração precisa ser feita no Blackboard Learn e no servidor ADFS.

Para ADFS como o IdP, selecione a configuração Publicar somente e remova o ponto de extremidade Redirecionar para a Confiança da terceira parte confiável da instância do Learn no servidor ADFS.

  1. No Learn, navegue até Administração > Autenticação > (Nome do provedor) > Configurações de SAML > Tipo de serviço de logout único.
  2. Selecione Publicar e desmarque a caixa de seleção Redirecionar.
  3. No ADFS Server, vá para a Confiança da terceira parte confiável da sua instância do Learn.
  4. Selecione Propriedades > Pontos de extremidade. Dois pontos de extremidade de logout SAML são listados.
  5. Remova o ponto de extremidade Redirecionar. Selecione Remover ponto de extremidade para removê-lo, então selecione Aplicar e OK.

Depois de fazer as alterações acima no Learn e no ADFS Server, o botão de logout Encerrar sessão de SSO operará para desconectar o usuário adequadamente.

Problema nº 7

Depois de inserir as credenciais de logon na página de logon do ADFS, uma mensagem de Erro ao efetuar logon! é exibida quando redirecionado para o Learn.

Com a seguinte exceção SAML no registro bb-services:

2017-05-26 07:39:30 -0400 – unsuccessfulAuthentication – org.springframework.security.authentication.AuthenticationServiceException: Erro ao validar a mensagem SAML
        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)
Causado por: org.opensaml.common.SAMLException: A resposta tem um código de status inválido urn:oasis:names:tc:SAML:2.0:status:Responder, a mensagem de status é nula
        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 mais

Resolução

A partir do Blackboard Learn 3200.0.0, agora existe uma opção de gerar novamente o certificado de criptografia SAML navegando até Administração do sistema > Building blocks > Provedor de autenticação – SAML > Configurações > Gerar o certificado novamente. O problema Erro ao efetuar logon! poderá ocorrer se o botão Gerar o certificado novamente for selecionado depois que os metadados do SP já tiverem sido carregados para a Confiança da terceira parte confiável para o site do Learn no servidor ADFS. Para resolver o problema:

  1. Navegue até Administração do sistema > Autenticação > [Nome do provedor SAML] > Configurações de SAML.
  2. Selecione Gerar ao lado de Metadados do provedor de serviços para salvar o novo arquivo de metadados.
  3. Acesse seu servidor ADFS e carregue os novos metadados do SP para a Confiança da terceira parte confiável para seu site do Learn.

Se você gerar um novo certificado nas configurações de B2, será necessário alternar o SAML B2 para Inativo e, então, voltar para Ativo para forçar a alteração. Depois, você pode retornar às configurações do provedor e gerar os novos metadados para importar para o IDP. Se você não alternar as configurações, o certificado antigo ainda poderá ser incluído ao gerar novos metadados. O IDP não será atualizado e, na próxima vez que o Learn o reiniciar, ele apresentará o novo certificado. A autenticação SAML será interrompida por causa dessa incompatibilidade.

Metadados de federação

Com o Active Directory Federation Services (ADFS), uma vez que os metadados para uma federação do ADFS geralmente localizados em https://[ADFS Server Hostname]/FederationMetadata/2007-06/FederationMetadata.xml incluem um elemento incompatível com SAML 2.0, os metadados precisam ser editados para excluir o elemento incompatível antes que ele seja carregado para a seção Configurações do provedor de identidade na página Configurações de autenticação SAML na GUI do Blackboard Learn. Se os metadados com o elemento incompatível forem carregados, ocorrerá um erro ao selecionar o link de logon SAML na página de logon do Blackboard Learn: Metadados para entidade [entity] e função {} não foram encontrados. Para referência, o código de erro é [error ID].

E o rastreamento de pilha Java correspondente para o código de erro no registro bb-services tem o seguinte:

2016-06-21 11:42:51 -0700 – Metadados para entidade https:///adfs/ls/ e função {urn:oasis:names:tc:SAML:2.0:metadata}SPSSODescritor não encontrado

Para referência, o código de erro é c99511ae-1162-4941-b823-3dda19fea157. – org.opensaml.saml2.metadata.provider.MetadataProviderException: Metadados para entidade https://ulvsso.laverne.edu/adfs/ls/ e função {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]

Resolução

Uma vez que o local padrão de metadados para uma federação ADFS é https://[ADFS server hostname]/FederationMetadata/2007-06/FederationMetadata.xml:

  1. Baixe esse arquivo e abra-o em um editor de texto. Exclua cuidadosamente a seção com início <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> ... </X509Data></KeyInfo> e término </ds:Signature>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">


      ://www.w3.org/2001/10/xml-exc-c14n#"/>
      ://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
     
       
        ://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
        ://www.w3.org/2001/10/xml-exc-c14n#"/>
       
       ://www.w3.org/2001/04/xmlenc#sha256"/>
       z1H1[SNIP]jaYM=
     
     
      FVj[SNIP]edrfNKWvsvk5A==
     
      ://www.w3.org/2000/09/xmldsig#">
       
       
        FDdd[SNIP]qTNKdk5F/vf1AocDaX
       
       
     

  2. Carregue o arquivo XML de metadados atualizado na GUI do Blackboard Learn na página Configurações de autenticação SAML na seção Configurações do provedor de identidade.
  3. Alterne o provedor de autenticação SAML e SAML B2 para Inativo/Disponível, enquanto o provedor de autenticação SAML estiver no status "Ativado".

Se uma instituição estiver testando a autenticação SAML em um site do Blackboard Learn e tiver diversos provedores de autenticação SAML que compartilham o mesmo arquivo XML de metadados do IdP do ADFS no site do Blackboard Learn, mesmo que os outros provedores de autenticação SAML estejam definidos como Inativos, eles também precisarão ter o arquivo XML de metadados atualizado carregado na GUI do Blackboard Learn na página de Configurações de autenticação SAML na seção Configurações do provedor de identidade. O SAML B2, então, deve ser alternado para Inativo/Disponível, enquanto o status do provedor de autenticação SAML estiver “Ativado”, para garantir que o arquivo XML de metadados atualizado seja reconhecido em todo o sistema.

Método incorreto de pesquisa de usuário

Depois de inserir as credenciais de logon na página de logon do ADFS, o usuário é redirecionado para a GUI do Blackboard Learn, mas não é conectado ao Blackboard Learn.

O ÚNICO evento relacionado a autenticação SAML no registro bb-services é:

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

Resolução

  1. Faça logon no Blackboard Learn como administrador usando a autenticação interna do Blackboard Learn padrão.
  2. Navegue até Administração do sistema > "Nome do provedor de autenticação SAML" > Editar.
  3. Altere o Método de pesquisa do usuário de Código de identificação do lote para Nome de usuário.

Botão de logout Encerrar sessão de SSO extra

O ADFS tenta adicionar um botão de logout Encerrar sessão de SSO extra na página Encerrar todas as sessões? que é exibida depois de selecionar primeiro o botão de logout no alto à direita na GUI do Blackboard Learn.

Isso é feito adicionando um SingleLogoutService extra ao arquivo de Metadados do IdP:

://your.server.name/adfs/ls/"/>
://your.server.name/adfs/ls/"/>

Uma vez que essa é uma configuração de IdP SAML B2 opcional e a assinatura que está sendo fornecida no Ponto de extremidade de redirecionamento não está correta, ocorrerá um erro ao selecionar o botão Encerrar sessão de SSO na página Encerrar todas as sessões?: A mensagem SAML de entrada falhou na validação de segurança. A validação da assinatura simples da solicitação falhou para o emissor do contexto. Para referência, o código de erro é [error ID].

O rastreamento de pilha Java correspondente para o código de erro no registro bb-services tem:

2016-10-17 16:57:44 -0400 – Falha na validação de segurança da mensagem SAML recebida Falha na validação de assinatura simples de solicitação para emissor de contexto

Para referência, o código de erro é 930c7767-8710-475e-8415-2077152280e0. – org.opensaml.ws.security.SecurityPolicyException: Falha na validação de assinatura simples de solicitação para emissor de contexto
    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]

Resolução

  1. Acesse o ADFS Server e vá para a Confiança da terceira parte confiável para a instância do Blackboard Learn.
  2. Selecione a guia Propriedades > Pontos de extremidade.
  3. Na guia Pontos de extremidade, haverá dois Pontos de extremidade de logout SAML.
  4. Remova o ponto de extremidade Redirecionar.
  5. Selecione Remover ponto de extremidade para removê-lo, então selecione Aplicar e OK.

Depois de remover o ponto de extremidade Redirecionar, o botão Encerrar sessão de SSO funcionará desconectando o usuário adequadamente.

Como visualizar registros de aplicativo com o visualizador de eventos

Ao solucionar problemas de autenticação SAML do ADFS, pode ser necessário que uma instituição também verifique os registros de aplicativo do ADFS em Visualização de eventos no servidor ADFS para obter mais informações. Isso é particularmente necessário quando a resposta de SAML do ADFS Server tem um status de Solicitação negada, conforme visto a seguir:


   
       
   

A resposta SAML pode ser visualizada usando o complemento de rastreador SAML do navegador Firefox.

O status Solicitação negada em uma resposta geralmente indica que ocorreu um problema quando o IdP (ADFS) tentou entender a resposta e processar o resultado que o SP (Blackboard Learn) forneceu.

Para visualizar os registros de aplicativo ADFS com o Visualizador de eventos:

  1. Abra o Visualizador de eventos no ADFS Server.
  2. No menu Visualizar, selecione Mostrar registros analíticos e de depuração.
  3. Na árvore do console, navegue até Registros de aplicativo e serviço > Rastreamento do Active Directory FS > Depurar.

Azure Active Directory

O Azure AD é o serviço de gerenciamento de identidade e diretório baseado em nuvem da Microsoft (MS).

Enviar primeira parte do e-mail

Se uma instituição estiver usando o Azure AD como IdP e quiser utilizar apenas a primeira parte do nome de usuário de e-mail do Azure AD como nome de usuário do Blackboard Learn, ela poderá configurar o IdP do Azure AD para usar a função especial ExtractMailPrefix() para remover o sufixo de domínio do e-mail ou do nome principal de usuário, fazendo apenas a primeira parte do nome de usuário ser passada (por exemplo, "joesmith" em vez de joesmith@example.com).

Se o código de usuário remoto do Blackboard Learn for urn:oid:1.3.6.1.4.1.5923.1.1.1.6, a configuração de Atributo para o IdP do Azure seria parecida com a seguinte:

Nome do atributo:        urn:oid:1.3.6.1.4.1.5923.1.1.1.6
Valor do atributo:    ExtractMailPrefix()
E-mail:            user.userprincipalname

Assim, com o nome de usuário de e-mail joesmith@example.com de exemplo, ele seria passado como isto na asserção SAML do IdP do Azure para o Blackboard Learn:


    joesmith

Informações adicionais sobre o uso da função ExtractMailPrefix() estão disponíveis na página de documentação do MS Azure.

IdP do Azure AD atualizando o certificado

Depois de inserir as credenciais de logon na página de logon do MS Azure AD, um Erro ao efetuar logon! poderá ser exibido depois do redirecionamento à GUI do Blackboard Learn.

Com a seguinte exceção no registro bb-services:

2016-10-13 12:03:23 +0800 – unsuccessfulAuthentication – org.springframework.security.authentication.AuthenticationServiceException: Erro ao validar a mensagem SAML
 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)
Causado por: org.opensaml.common.SAMLException: A resposta não tem nenhuma afirmação válida que passe na validação do assunto
 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 mais
Causado por: org.opensaml.xml.validation.ValidationException: A assinatura não é confiável ou inválida
 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 mais

Isso é causado pela atualização do certificado pelo IdP do MS Azure AD, mas o XML dos metadados usados pelo SP do Blackboard Learn não é ajustado para refletir o novo certificado.

Resolução

  • O novo arquivo XML de metadados com o novo certificado precisará ser atualizado na página Configurações de SAML na GUI do Blackboard Learn para o provedor de autenticação.
  • O SAML B2 e o provedor de autenticação então precisarão ser alternados para Inativo/Disponível, enquanto o status do provedor de autenticação SAML estiver “Ativado”, para que os metadados sejam atualizados com o novo certificado aplicado.
  • Se um site do Blackboard Learn tiver vários provedores de autenticação que compartilhem o mesmo certificado subjacente para o mesmo código de entidade do IdP subjacente, TODOS esses provedores de autenticação precisarão ser atualizados.

A Microsoft indicou que atualizará os certificados a cada seis semanas a partir de agora, e essas atualizações não serão anunciadas.


Logon único iniciado pelo IdP

Se um usuário fizer logon no portal do usuário inicialmente e então selecionar o aplicativo para o site do Blackboard Learn, uma nova guia do navegador será aberta para exibir a mensagem: O recurso especificado não foi encontrado ou você não tem permissão para acessá-lo.

Com os eventos relacionados SAML correspondentes no stdout-stderr.log:

INFO   | jvm 1    | 2016/08/16 10:49:22 | – /saml/SSO na posição 1 de 10 na cadeia de filtro adicional; filtro de disparo: 'SecurityContextPersistenceFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | – HttpSession retornou objeto nulo para SPRING_SECURITY_CONTEXT
INFO   | jvm 1    | 2016/08/16 10:49:22 | – Nenhum SecurityContext estava disponível na HttpSession: org.apache.catalina.session.StandardSessionFacade@58c53845. Um novo será criado.
INFO   | jvm 1    | 2016/08/16 10:49:22 | – /saml/SSO na posição 2 de 10 na cadeia de filtro adicional; filtro de disparo: 'WebAsyncManagerIntegrationFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | – /saml/SSO na posição 3 de 10 na cadeia de filtro adicional; filtro de disparo: 'HeaderWriterFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | – /saml/SSO na posição 4 de 10 na cadeia de filtro adicional; filtro de disparo: 'FilterChainProxy'
INFO   | jvm 1    | 2016/08/16 10:49:22 | – Confira a correspondência de solicitação: '/saml/sso'; against '/saml/logon/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | – Confira a correspondência de solicitação: '/saml/sso'; against '/saml/logout/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | – Confira a correspondência de solicitação: '/saml/sso'; against '/saml/bbsamllogout/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | – Confira a correspondência de solicitação: '/saml/sso'; against '/saml/sso/**'
INFO   | jvm 1    | 2016/08/16 10:49:22 | – /saml/SSO na posição 1 de 1 na cadeia de filtro adicional; filtro de disparo: 'SAMLProcessingFilter'
INFO   | jvm 1    | 2016/08/16 10:49:22 | – Encaminhamento para /
INFO   | jvm 1    | 2016/08/16 10:49:22 | – DispatcherServlet com nome 'saml' processando solicitação POST para [/auth-saml/saml/SSO]
INFO   | jvm 1    | 2016/08/16 10:49:22 | – Nenhum mapeamento encontrado para a solicitação HTTP com URI [/auth-saml/saml/SSO] no DispatcherServlet com nome 'saml'
INFO   | jvm 1    | 2016/08/16 10:49:22 | – SecurityContext está vazio ou o conteúdo é anônimo – o contexto não será armazenado em HttpSession.
INFO   | jvm 1    | 2016/08/16 10:49:22 | – Solicitação concluída com sucesso
INFO   | jvm 1    | 2016/08/16 10:49:22 | – Ignorar chamada em
INFO   | jvm 1    | 2016/08/16 10:49:22 | – SecurityContextHolder agora removido, conforme processamento de solicitação concluído

A seção Configurações do provedor de serviços da página Configurações de autenticação SAML mudou e a opção Habilitar SSO automático deve ser marcada para permitir que um usuário acesse o Blackboard Learn usando seu próprio portal. Se estiver habilitada, o URL do ACS também será alterado para incluir um alias.


Erro de documento incorreto

Depois de inserir as credenciais de logon na página de logon do provedor de autenticação SAML, um Erro ao efetuar logon! poderá ser exibido depois do redirecionamento à GUI do Blackboard Learn.

Com a seguinte DOMException e WRONG_DOCUMENT_ERR no registro bb-services:

2016-11-18 12:27:31 -0600 – WRONG_DOCUMENT_ERR: Um nó é usado em um documento diferente daquele que o criou.

Para referência, o código de erro é 86ebb81d-d3a3-4da5-95ab-1c94505f4281. – org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: Um nó é usado em um documento diferente daquele que o criou.
    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]

A razão pela qual o problema ocorre é que outro B2/Project alterou o valor da propriedade do sistema javax.xml.parsers.DocumentBuilderFactory de org.apache.xerces.jaxp.DocumentBuilderFactoryImpl para com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl.

Resolução temporária

Até que uma correção seja liberada, as opções de resolução temporárias são:

  1. Reinicie os serviços Bb em cada nó.
  2. Desative a criptografia de resposta SAML no lado do IdP. Uma vez que a comunicação como um todo é por SSL, isso não reduzirá a segurança da autenticação.

Nenhuma opção para adicionar SAML à ordem do provedor

Ao configurar a autenticação SAML, uma instituição pode perceber que não existe a opção de adicionar um provedor de autenticação SAML na seção Ordem do provedor na GUI do Blackboard Learn ao navegar até Administração do sistema > Building blocks: Autenticação > Ordem do provedor.

O motivo pelo qual não há uma opção de adicionar um provedor de autenticação SAML à Ordem do provedor é que provedores do tipo de redirecionamento como CAS e SAML entregam a autenticação à origem remota. Esses não estão listados na Ordem do provedor porque são considerados a origem autoritativa para autenticação e tratam das próprias falhas de autenticação.


Testar conexão SAML

Começando com a versão Q4 2016 do Blackboard Learn, agora há uma opção de testar a conexão de um provedor SAML na seção Autenticação na GUI do Blackboard Learn. O teste de conexão verificará os seguintes itens:

  • Analisar metadados de IdP
  • Conectar-se ao IdP
  • Receber a resposta SAML
  • Analisar a resposta SAML
  • Correspondência de código de usuário remoto
  • Logon no Blackboard Learn

Para testar a conexão para o provedor de autenticação SAML:

  1. Faça logon no Blackboard Learn como administrador.
  2. Navegue até Administração do sistema > Building blocks: Autenticação > "Nome do provedor SAML" > Testar conexão.
  3. Insira as credenciais de logon do IdP, se solicitado.

O recurso Testar conexão pode ser usado em lugar de habilitar manualmente o registro de depuração SAML no Blackboard Learn por vários motivos.

O valor do código da entidade do provedor de identidade exibido na página de saída de Testar conexão é obtido do elemento Emissor no SAML POST do IdP para o Blackboard Learn depois que o usuário tiver sido autenticado:

http://bbpdcsi-adfs1.bbpdcsi.local/a...services/trust

Os valores de Atributo SAML exibidos na página de saída Testar conexão na seção Resposta SAML são extraídos dos elementos Assunto e AttributeStatement no SAML POST do IdP para o Blackboard Learn depois que o usuário foi autenticado:


    luke.skywalker
    [SNIP]


   
        luke.skywalker
   
   
        Luke
   
   
        Skywalker
   


Crie um provedor de autenticação SAML e um IdP para teste

Siga as etapas abaixo para criar um provedor de identidade (IdP) usando a solução de autenticação SSO gratuita da Centrify.

Esse IdP então pode ser configurado como o provedor de autenticação SAML em um provedor de serviço (SP) do Blackboard Learn:

Provedor de serviço do Blackboard Learn

  1. Faça logon na GUI do Blackboard Learn como administrador e navegue até Administração do sistema > Autenticação.
  2. Selecione Criar provedor > SAML.
  3. Insira as seguintes configurações:
    • Nome > SAML ou o desejado.
    • Provedor de autenticação > Inativo (por enquanto).
    • Método de pesquisa do usuário > Nome de usuário
    • Restringir por nome de host > Use este provedor para qualquer nome de host
    • Text do link > Login Centrify SAML
  4. Selecione Salvar e configurar.
  5. No campo Código da entidade, defina isso para qualquer valor que quiser (porém, se alterá-lo, você deverá fornecer os metadados do provedor de serviços atualizados para o provedor de identidade). Simplesmente copie/cole o URL do ACS.
  6. Em Metadados do provedor de serviços, selecione Gerar e salve o arquivo na área de trabalho.
  7. Em Fonte de dados, recomendamos criar uma nova origem de dados para esse CENTRIFY nomeado, caso contrário, use SYSTEM ou o que você escolher
  8. Ao lado de Habilitar provisionamento JIT, marque essa caixa para que uma conta seja criada automaticamente ao tentar fazer logon usando esse provedor de autenticação SAML se o usuário não existir. Se o provisionamento JIT não estiver selecionado, o usuário no Blackboard Learn precisará ser criado manualmente.
  9. Na lista Fontes de dados compatíveis, selecione as fontes de dados com as quais esse provedor de autenticação deve ser compatível.
  10. Selecione Provedor de identidade de ponto para o Tipo de provedor de identidade.
  11. Ignore os Metadados do provedor de identidade por enquanto; você carregará o arquivo depois que ele tiver sido criado na seção do IdP Centrify.
  12. Para a seção Mapear atributos SAML, use NameID para o Código do usuário remoto.
  13. Selecione Enviar.

Provedor de identidade Centrify

  1. Acesse o site da Centrify e selecione Iniciar agora.
  2. Insira suas informações para inscrever-se e selecione Iniciar agora.
  3. Você receberá um e-mail de boas-vindas com suas credenciais de administrador. Use-as para fazer logon no https://cloud.centrify.com.
  4. Selecione Ignorar na janela Bem-vindo ao Centrify Identify Service.
  5. Na guia Aplicativos na parte superior da página, selecione o botão Adicionar aplicativos da web.
  6. Na guia Personalizado, role para baixo e selecione o botão Adicionar para SAML. Selecione Sim.
  7. Selecione Fechar na parte inferior da janela Adicionar aplicativos da web.
  8. Vá para a guia Aplicativos. Na seção Configurações do aplicativo, selecione o botão Carregar metadados do SP e carregue o arquivo criado na Etapa 6 da seção do SP do Blackboard Learn.
  9. O URL de serviço do consumidor de asserção deve ser preenchido automaticamente depois do carregamento dos metadados do SP.
  10. Desmarque Criptografar asserção. Isso permite que os atributos que estão sendo liberados do IdP e enviados ao Blackboard Learn sejam visualizados usando o complemento rastreador SAML do navegador Firefox ou o Decodificador de mensagens SAML do Chrome. Uma vez que a comunicação como um todo é por SSL, isso não reduzirá a segurança da autenticação.
  11. Role para baixo e selecione Baixar metadados SAML do provedor de identidade. Salve o arquivo em seu computador.
  12. Selecione Salvar e vá para a próxima seção.
  13. Insira um nome para a seção Descrição. Selecione Salvar e vá para a próxima seção.
  14. Na seção Acesso do usuário, selecione Todos e Administrador do sistema. Selecione Salvar.
  15. Não faça nenhuma seleção na seção Política.
  16. Para a seção Mapeamento da conta, confirme que userprincipalname foi inserido para o nome do campo Serviço de diretório.
  17. Para a seção Avançado, adicione a seguinte linha à parte inferior do script usado para gerar uma asserção SAML para o aplicativo:

    O script completo será:

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

    Isso permitirá ao IdP Centrify liberar a AttributeStatement com o código do usuário no POST SAML.

    Exemplo:


                       NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
                   >
            luke.skywalker@blackboard.com.47
       

    Saiba mais sobre como especificar elementos de asserção no script SAML Centrify

  18. Selecione Salvar.
  19. Não será preciso fazer qualquer alteração às seções restantes (Gateway de aplicativo, Changelog e Fluxo de trabalho).
  20. Na guia Aplicativos, confirme que o aplicativo SAML tenha sido implantado de modo automático.
  21. Na guia Usuários, selecione Adicionar usuários, insira as informações da conta e selecione Criar usuário.
  22. Faça logon novamente na GUI do Blackboard Learn como administrador, navegue até Administração do sistema > Autenticação > Nome do provedor de autenticação SAML > Configurações de SAML > Configurações do provedor de identidade, carregue o arquivo de metadados do IdP salvo na sua área de trabalho na etapa 13 e selecione Enviar.

O usuário do IdP Centrify que foi criado agora pode fazer logon no Blackboard Learn via SAML selecionando o provedor de autenticação na página de logon e logout do Blackboard Learn usando o botão de logout Encerrar sessão de SSO extra na página Encerrar todas as sessões? exibida depois de selecionar o botão de logout no alto à direita do Blackboard Learn.


Altere o texto na página de logout Encerrar sessão de SSO

Uma instituição pode consultar se é possível alterar o texto na página de logout da Encerrar sessão de SSO. É possível alterar o texto na página de logout Encerrar sessão de SSO editando o pacote de idiomas:

  1. Abra o arquivo do pacote de idiomas.
  2. Navegue até auth-provider-saml/src/main/webapp/WEB-INF/bundles/bb-manifest-en_US.properties.
  3. Atualize as chaves de mensagem:

    saml.single.logout.warning.conent.description // a primeira linha
    saml.single.logout.warning.conent.recommend // segunda linha
    saml.single.logout.warning.endsso.title // terceira linha
    saml.single.logout.warning.endsso.button // o botão
    saml.single.logout.warning.backtolearn // o botão de cancelamento


Redirecione os usuários à página de logon do IdP

A página de logon padrão do Blackboard Learn apresenta campos de nome de usuário e senha para o provedor de autenticação do Learn interno. Ao habilitar a autenticação SAML, um pequeno link "Fazer logon usando…" para SAML aparecerá na parte inferior dessa página, assim, talvez você queira redirecionar os usuários para o servidor de autenticação do IdP automaticamente quando eles acessarem a página de logon do Learn.

Uma opção para fazer isso é navegar até Administração do sistema > Autenticação e definir a autenticação do Learn interno padrão como Inativa, o que significa que uma página de logon não será mais exibida e o usuário será imediatamente redirecionado para o logon de SAML. O problema com essa opção é que ela substitui o URL de logon padrão e impede qualquer usuário não SAML de fazer logon.

Para evitar esse problema e atingir quase o mesmo resultado, use uma página de logon personalizada. Os usuários são redirecionados à página de logon do IdP do provedor de autenticação SAML do provedor, mas o link de logon padrão também deve poder ser utilizado.

  1. Verifique se a autenticação do Learn interno está ativa
  2. Na página de logon padrão, copie o local do redirecionamento do provedor, por exemplo, faça sign-in usando... SAML. Clique com o botão direito do mouse e selecione Copiar local do link.
  3. Navegue até Administração do sistema > Comunidades > Configurações temáticas e temas > Personalizar página de logon.
  4. Selecione Baixar ao lado da Página de logon padrão para baixar o arquivo JSP de logon padrão.
  5. Abra o arquivo de JSP com um editor de texto. Adicione o seguinte HTML de amostra ao arquivo JSP de logon e substitua o texto do URL pelo URL que foi copiado na Etapa 2.




    Blackboard Learn – Redirect


    Redirecionando... Acessar a página de logon se você não for redirecionado automaticamente.

  6. Navegue para Personalizar página de logon no Learn mais uma vez. Selecione Usar página personalizada e então carregue o arquivo JSP de logon atualizado.
  7. Depois de fazer as alterações, selecione Visualização em Personalizar página de logon para confirmar que o redirecionamento está funcionando adequadamente.

Os usuários que forem para o URL principal agora serão redirecionados para a página de logon para o provedor de autenticação SAML. Os administradores ainda podem fazer logon usando a autenticação interna do Learn por meio da página de logon padrão: /webapps/login/?action=default_login ou/webapps/login/login.jsp).

Saiba mais sobre como personalizar a página de logon na experiência Ultra