Inquiridor
Autenticação personalizada no WCF

Discussão Geral
-
Estou desenvolvendo uma aplicação windows Forms que irá consumir um serviço WCF. Até aí tudo bem, eu consigo criar meu serviço WCF com tudo o que preciso, agora só está faltando a parte da segurança, ou seja, preciso criar uma autenticação dentro de WCF para que o cliente possa se autenticar antes que qualquer outra operação possa ser feita.
Estou iniciando meus estudo sobre o WCF e realmente não consegui ter progressos ainda com a autenticação.
Como é que se cria uma autenticação em um serviço WCF?
Todas as Respostas
-
Boas Marcos,
Veja se isso te ajuda em algo: http://www.israelaece.com/post/WCF-Seguranca-Autenticacao-e-Autorizacao-Customizadas.aspx
http://www.israelaece.com -
Incrível cara! Mais fácil do que eu pensava! Muito abrigado pela dica e pela ajuda ai, funcionou perfeitamente.. Agora só falta substituir o XML e colocar meu Banco como repositório.
Porém me ocorreu mais uma dúvida. A mensagem tranpostada pela rede ja está com tudo isso encriptado? ou tenho que implementar um serviço de criptografia? -
-
-
Boas Marcos,
Por padrão, o algoritmo usado pelo makecert.exe é o MD5, mas você pode customizar: http://www.israelaece.com/post/Criando-certificados-para-teste.aspx
http://www.israelaece.com -
-
Israel, surgiu um novo problrma com minha aplicação. No cliente quando vou utilizar o serviço do WCF, está dando um erro de comunicação.
A maneira que estou chamando o cliente é igual a indicada pro voce em seu artigo:
webservice = New service.Service1Client Using proxy As New Service1Client() proxy.ClientCredentials.UserName.UserName = "MarcosLatchuk" proxy.ClientCredentials.UserName.Password = "123456" a = proxy.RetornarPessoa()
Só que o seguinte erro é lançado quando o proxy.RetornarPessoa() é chamado:
End Using
"Falha na negociação de segurança porque a parte remota não retornou uma resposta pontual. Isso pode ter ocorrido por causa da anulação da conexão de transporte subjacente."
O que pode estar acontecendo? -
Boas Marcos,
Esse erro é genérico.
A configuração do certificado em ambos os lados está correta?
http://www.israelaece.com -
Desculpa... erro meu!
Era justamente a configuração, agora está funcionando, mas ainda não consigo fazer a autenticação. Dentro do serviço WCF ele da o seguinte erro:
"Falha na solicitação de permissão principal."
Isso logo no início do serviço que eu solicito:
<PrincipalPermission(SecurityAction.Demand, Role:="Usuario")> _ Public Function RetornarPessoa() As Pessoa.Pessoa Implements IService1.RetornarPessoa //aqui retorno o objeto Pessoa End Function
eu não implementei a classe XmlAuthorizationManager.vb que está em seu artigo, tem alguma relação com ela esse erro? -
Boas Marcos,
O que está ocorrendo é que o usuário autenticado não possui este papel, o que proibe o acesso ao método. Se você não precisar de autorização, então você pode remover o atributo PrincipalPermission.
É dentro da classe XmlAuthorizationPolicy que eu carrego os papeis do usuário.
http://www.israelaece.com -
-
Olá Israel,
Aproveitando a dúvida do nosso amigo Marcos... É possível consumir um WCF que utiliza ClientCredentials com otras tecnologias como Java, PHP e etc?
Abraço,
Roberto Santos http://blog.robertoosantos.com -
Boas Marcos,
É dessa forma mesmo. Mas onde o erro ocorre? No artigo que você usou como base, eu falo de autenticação e autorização, ou seja, se você só estiver interessado em autenticar o usuário, o atributo PrincipalPermission não é necessário.
Neste outro artigo http://www.israelaece.com/post/WCF-Seguranca.aspx eu tenho um exemplo de autenticação, utilizando os mesmos principios, mas não me preocupando com a autorização.
http://www.israelaece.com -
Boas Roberto,
O WCF é baseado em padrões abertos, que a Microsoft implementou no WCF. Todas as outras tecnologias também tem seu framework de comunicação, que também já devem ter implementados esses padrões.
Esses padrões são baseados em XML. O que o WCF faz é ler e escrever nesses formatos predefinidos, encapsulando toda a complexidade para escrever ou efetuar o parser desses padrões, permitindo a nós, interargirmos de forma indireta com o XML, ou seja, através de classes.
http://www.israelaece.com -
Tenho uma dúvida sobre as tecnologias envolvidas em meu projeto.
Eu tenho que criar um WebService para a comunicação dos sistemas envolvidos, é um WebService simples, mas que precisa ser seguro, com autenticação e com criptografia das mensagens trocadas... bom, seria melhor a utilização do WCF ou um simples ASP.NET WebService Application poderia resolver?
Abraço! -
-
Boas Marcos,
Você pode utilizar os ASP.NET Web Services, mas eles tem uma série de limitações, como por exemplo, se quiser fazer a autenticação do usuário, terá que proteger a comunicação através do protocolo, ou seja, HTTPS. Do contrário, qualquer um que interceptar a requisição, conseguirá visualizar as credenciais.
http://www.israelaece.com -
Boas Marcos,
Sim, esse atributo quer dizer que somente usuários que estão dentro do papel "Admin" podem executar tal método.
É dentro da classe XmlAuthorizationPolicy que eu carrego os papeis do usuário.
http://www.israelaece.com -
Olá Israel,
ainda tenho mais dúvidas...
O atributo PrincipalPermission, quando utuilizado sem valor algum para o parâmetro Role, apenas <PrincipalPermission(SecurityAction.Demand)>_ por exemplo, ainda funciona da mesma forma para a autenticação? Pois na minha aplicação não é interessante ter um controle dependente do papel so usuário, apenas quero um autenticação com o nome do usuário e sua senha.
Outra duvida:
Quando crio uma classe para fazer a autenticação, classe esta que extende a classe UserNamePasswordValidator, a única coisa que garante que será esta a classe utilizada para a autenticação é a configuração do web.config/app.config? Qual é a tag que especifica isso dentro do arquivo?
ps: desculpa a insistência em algumas dúvidas, mas estou iniciando os estudos no WCF, então ainda está tudo meio confuso. -
Boas Marcos,
O atributo PrincipalPermission somente deve ser utilizar se você quiser filtrar o acesso à um determinado membro, através de um papel ao nome de usuário. Pelo que descreveu, acredito que no seu caso você não precise dele.
Sim, é através do arquivo de configuração que você acopla a classe ao runtime do WCF. De uma olhada no atributo customUserNamePasswordValidatorType que mostro neste artigo: http://www.israelaece.com/post/WCF-Seguranca-Autenticacao-e-Autorizacao-Customizadas.aspx
http://www.israelaece.com -
Ok, consegui associar minha classe de autenticação.
Mas qual é o atributo que devo usar no método para que a classe que autenticação seja usada? Pois sem o uso do PrincipalPermission eu não consegui negar o acesso a um usuário não autenticado. -
Boas Marcos,
Isso é feito automaticamente. Você não precisa chamar em lugar nenhum. Quando chegar uma requisição para o teu serviço, o runtime do WCF irá executar automaticamente, obviamente, desde que ela esteja devidamente configurada no arquivo de configuração.
http://www.israelaece.com -
-
Boas Marcos,
Eu falo sobre isso aqui: http://www.israelaece.com/post/Criando-certificados-para-teste.aspx
http://www.israelaece.com -
Então Israel, fiz exatamente como está no post, modifiquei a configuração la no web.config e executei o comando com o makecert, mas quando vou executar o host que baixei do seu post http://www.israelaece.com/post/WCF-Seguranca-Autenticacao-e-Autorizacao-Customizadas.aspx o seguinte erro ocorre:
Não é possível encontrar o certificado X.509 usando os seguintes critérios de pesquisa: StoreName 'TrustedPeople', StoreLocation 'LocalMachine', FindType 'FindBySubjectName', FindValue 'localhost'.
não sei o que pode estar acontecendo, visto que em minhas pesquisas parece que tudo foi feito da maneira correta, tanto na minha aplicação quanto na sua que eu baixei da o erro. Sera que pode ser SO? Estou usando o Windows Vista Professional 32 bit. -
Boas Marcos,
Esse erro ocorre porque ele não encontra o certificado, assim como a mensagem diz. De certeza que está tudo correto? O sistema operacional está OK.
http://www.israelaece.com -
Fiz o seguinte..
- baixei o seu projeto do post http://www.israelaece.com/post/WCF-Seguranca.aspx
- abri um terminal cmd e executei o comando do makecert: C:\>makecert -sr LocalMachine -ss TrustedPeople -sky exchange -pe -a sha1 -n "CN=localhost" C:\localhost.cer
- modifiquei a main do seu projeto onde é setado o certificado, ficou da seguinte forma:static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(Servico), new Uri[] { new Uri("http://localhost:2999/") })) { WSHttpBinding binding = new WSHttpBinding(SecurityMode.Message); ServiceCredentials credenciais = new ServiceCredentials(); credenciais.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom; credenciais.UserNameAuthentication.CustomUserNamePasswordValidator = new Autenticador(); //modificado para o certificado criado credenciais.ServiceCertificate.SetCertificate( StoreLocation.LocalMachine, StoreName.TrustedPeople, X509FindType.FindBySubjectName, "localhost"); host.Description.Behaviors.Add(credenciais); host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); host.AddServiceEndpoint(typeof(IContrato), binding, "srv"); host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); host.Open(); Console.ReadLine(); } }
Está correto? -
Boas Marcos,
Refiz o exemplo aqui, e tudo funcionou, seguindo os seus passos.
Já tentou dar uma revisada na criação do certificado e a configuração do serviço?
http://www.israelaece.com -
Sim, agora funcionou!
O problema era na configuração do cliente. O Binding não estava configurado corretamente, deixei desta maneira e funcionou:
<bindings> <wsHttpBinding> <binding name="WSHttpBinding_IService1" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /> <security mode="Message"> <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" /> <!--Aqui foi alterado o clientCredentialType para UserName--> <message clientCredentialType="UserName" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true" /> </security> </binding> </wsHttpBinding> </bindings>
Obrigado pela ajuda amigo!
Abraço! -
-
Israel, está ocorrendo um problema aqui, eu iniciei uma nova aplicação WCF utilizando o modelo de criação de WCF services do visual studio 2008.
Este modelo ja vem com um webService para que a aplicação rode utilizando o serviço web, diferente de como eu tinha feito antes seguindo seus artigos, onde a aplicação rodava em um console application.
Montei minha aplicação seguindo o mesmo esquema feito no seu artigo http://www.israelaece.com/post/WCF-Seguranca-Autenticacao-e-Autorizacao-Customizadas.aspx, e o seguinte erro está ocorrendo:
- quando é feito o DirectCast para que o xmlPrincipal seja igual ao CurrentePrincipal:
Dim xmlPrincipal As XmlPrincipal = DirectCast(Thread.CurrentPrincipal, XmlPrincipal)
é lançado uma InvalidCastException, exibindo a seguinte mensagem:
Não é possível converter um objeto do tipo 'System.Security.Principal.WindowsPrincipal' no tipo 'ServicoWcf.Seguranca.Principal'.
Não sei qual é o motivo do código Thread.CurrentePrincipal estar retornando um objeto WindosPrincipal, ao invés de um Principal.
O que pode estar ocorrendo? -
Boas Marcos,
Provavelmente, você está com a autenticação integrada ao Windows ligada.
Você precisa especificar UserName no tipo, através do arquivo de configuração.
http://www.israelaece.com -
Então eu estava pelo caminho certo, porque pensei que poderia ser isso, mas no web.config não há o modo UserName..
A tag responsável por isso (não tenho certeza), acho que é a :
<authentication mode="Windows"/>
mas as únicas opções ali disponíveis são:
- None
- Forms
- Windows
- Passport
Olha como está meu web.config:<?xml version="1.0"?> <!-- Note: As an alternative to hand editing this file you can use the web admin tool to configure settings for your application. Use the Website->Asp.Net Configuration option in Visual Studio. A full list of settings and comments can be found in machine.config.comments usually located in \Windows\Microsoft.Net\Framework\v2.x\Config --> <configuration> <configSections> <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/> <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/> <section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/> <section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/> <section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/> </sectionGroup> </sectionGroup> </sectionGroup> </configSections> <appSettings/> <connectionStrings/> <system.web> <!-- Set compilation debug="true" to insert debugging symbols into the compiled page. Because this affects performance, set this value to true only during development. Visual Basic options: Set strict="true" to disallow all data type conversions where data loss can occur. Set explicit="true" to force declaration of all variables. --> <compilation debug="true" strict="false" explicit="true"> <assemblies> <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> </assemblies> </compilation> <pages> <namespaces> <clear/> <add namespace="System"/> <add namespace="System.Collections"/> <add namespace="System.Collections.Specialized"/> <add namespace="System.Configuration"/> <add namespace="System.Text"/> <add namespace="System.Text.RegularExpressions"/> <add namespace="System.Linq"/> <add namespace="System.Web"/> <add namespace="System.Web.Caching"/> <add namespace="System.Web.SessionState"/> <add namespace="System.Web.Security"/> <add namespace="System.Web.Profile"/> <add namespace="System.Web.UI"/> <add namespace="System.Web.UI.WebControls"/> <add namespace="System.Web.UI.WebControls.WebParts"/> <add namespace="System.Web.UI.HtmlControls"/> </namespaces> <controls> <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> </controls> </pages> <!-- The <authentication> section enables configuration of the security authentication mode used by ASP.NET to identify an incoming user. --> <authentication mode="Forms"/> <!-- The <customErrors> section enables configuration of what to do if/when an unhandled error occurs during the execution of a request. Specifically, it enables developers to configure html error pages to be displayed in place of a error stack trace. <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm"> <error statusCode="403" redirect="NoAccess.htm" /> <error statusCode="404" redirect="FileNotFound.htm" /> </customErrors> --> <httpHandlers> <remove verb="*" path="*.asmx"/> <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/> </httpHandlers> <httpModules> <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> </httpModules> </system.web> <system.codedom> <compilers> <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" warningLevel="4" type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <providerOption name="CompilerVersion" value="v3.5"/> <providerOption name="OptionInfer" value="true"/> <providerOption name="WarnAsError" value="false"/> </compiler> </compilers> </system.codedom> <!-- The system.webServer section is required for running ASP.NET AJAX under Internet Information Services 7.0. It is not necessary for previous version of IIS. --> <system.webServer> <validation validateIntegratedModeConfiguration="false"/> <modules> <add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> </modules> <handlers> <remove name="WebServiceHandlerFactory-Integrated"/> <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> </handlers> </system.webServer> <!-- AQUI É A CONFIGURAÇÃO DO SERVIÇO --> <system.serviceModel> <services> <service behaviorConfiguration="ServicoWcf.Service1Behavior" name="ServicoWcf.Servico"> <!--<host> <baseAddresses> <add baseAddress="http://localhost:49588/"/> </baseAddresses> </host>--> <endpoint address="" binding="wsHttpBinding" contract="ServicoWcf.IServico"> <identity> <dns value="localhost"/> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <!--Definição do binding de segurança--> <bindings> <wsHttpBinding> <binding name="BindingConfig"> <security mode="Message"> <message clientCredentialType="UserName"/> </security> </binding> </wsHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="ServicoWcf.Service1Behavior"> <!--Autenticação e Certificado--> <serviceCredentials> <serviceCertificate findValue="e5 62 55 d5 3d a3 af 88 41 50 10 32 c5 e4 c1 eb" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySerialNumber" /> <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="ServicoWcf.Seguranca.Autenticacao, ServicoWcf"/> </serviceCredentials> <!--Autorização personalizada--> <!--<serviceAuthorization principalPermissionMode="Custom" serviceAuthorizationManagerType="Host.XmlSecurity.XmlAuthorizationManager, Host"> <authorizationPolicies> <add policyType="Host.XmlSecurity.XmlAuthorizationPolicy, Host" /> </authorizationPolicies> </serviceAuthorization>--> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
Será que esse modelo do WCF existente no visual studio 2008 pode ser customizado como o feito antes?
-
Boas Marcos,
E você está criando o XmlAuthorizationManager, e lá definindo o IIdentity como eu faço no artigo?
http://www.israelaece.com -
-
-
-
Para que serve esta parte do código da função CheckAccessCore na classe XmlAuthorizationManager??
If operationContext.IncomingMessageHeaders.Action = _ "http://www.photo.com/IService/RecuperarPessoa" Then If Not principal.IsInRole("Administrador") Then Return False End If End If
-
Oi!
Estou tendo o mesmo problema que o Marcos teve. Israel, baixei o projeto do seu site e criei e configurei o certificado conforme os seus 2 artigos. Mas quando executo o host aparece o seguinte erro:
Não é possível encontrar o certificado X.509 usando os seguintes critérios de pesquisa: StoreName 'TrustedPeople', StoreLocation 'LocalMachine', FindType 'FindBySubjectName', FindValue 'localhost'.
Já revisei o código mais que uma vez e não estou encontrando onde eu possa ter errado. Usei o seguinte comando para gerar o certificado:
makecert -sr LocalMachine -ss TrustedPeople -sky exchange -pe -a sha1 -n "CN=localhost" localhost.cer
Abaixo como está o app.config do host referente a configuração do certificado:
<serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName" />
Alguém sabe o que pode ser?
Daniel Henrique Cordeiro http://danielcordeiro.eti.br