Criando um Web Service seguro com SoapHeader

Olá pessoa, como meu primeiro artigo vou iniciar uma breve explicação de como criar um WebService seguro utilizando o SoapHeader e como acessar os seus métodos utilizando o ObjectDataSource com GridView. Apesar de o artigo estar com exemplos em C-Sharp, você poderá tranquilamente aplicá-lo em VB.NET, basta um pouco de vontade.

Não irei entrar em detalhes de como criar uma conexão passo a passo, vou levar em consideração que você já sabe como fazer e esta a procura de como adicionar mais segurança a seu Web Service, mais prometo criar um artigo especialmente com esse assunto.


1- Criando o WebService
Crie um novo Web Service, onde inicialmente seu código será como esse:


/// <summary>
/// Summary description for MeuWebService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class MeuWebService : System.Web.Services.WebService
{
	public MeuWebService()
	{
		//Uncomment the following line if using designed components
		//InitializeComponent();
	}

	[WebMethod]
	public string HelloWorld()
	{
		return "Hello World";
	}
}

2- Adicionando um novo método
Crie um método onde esse retorne um DataSet para mais tarde ser consumido por uma aplicação, que no nosso caso será um WebForm:


[WebMethod(Description = "Retorna todos os clientes cadastrados")]
public DataSet getClientes()
{
    DataSet dsClientes = null;
    string connectionString = "Data Source=seu_servidor_sql;User Id=seu_usuario;Password=sua_senha;Initial Catalog=seu_bd;";

    StringBuilder sqlString = new StringBuilder();
    sqlString.Append(" SELECT * FROM CLIENTES ");

    using (System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(connectionString))
    {
        connection.Open();

        using (System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand())
        {
            command.Connection = connection;
            command.CommandText = sqlString.ToString();

            using (System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter(command))
            {
                adapter.Fill(dsClientes);
            }
        }
    }

    return dsClientes;
}

Pronto, já possuímos um Web Service capaz de retornar um DataSet contendo as informações que desejamos, porem esse poderá ser consumido por qualquer aplicação uma vez que for publicada na internet. Então imagine o seguinte cenário, seu Web Service possui tambem um método capaz de excluir todos os seu cliente cadastrado. Isso seria catastrófico. Com essa dica abaixo será possível proteger facilmente o Web Service impedindo que outras aplicações, por mais que estejam acessando o Web Service, não sejam capaz de executar os métodos sem autenticação.

3- Criando a classe de segurança
Crie uma nova classe herdando a classe SoapHeader como no exemplo abaixo:


public class SegurancaClientes : SoapHeader
{
	public string Usuario;
	public string Senha;
}

Você poderá adiciona-lo logo abaixo da declaração dos Namespaces ou criar um novo arquivo de Classe.
Agora dentro do Web Service você deverá estanciar essa nova classe:


public SegurancaClientes Credencial = new SegurancaClientes();

Agora atribua o seguinte código antes da declaração do WebMethod.


[SoapHeader("Credencial")]
[WebMethod(...

Apesar de termos criado o Cabeçalho de autenticação do nosso Web Service utilizando o SoapHeader, não quer dizer que ele está protegido.

4- Como autenticar o WebService
Será necessário criar um novo método, só que esse não irá conter a declaração de [WebMethod], será do tipo private e não precisaremos passar nenhum argumento. Crie um método simples que retorne o Tipo Boolean ou bool e dentro dele crie seu esquema de autenticação utilizando os parâmetros do SoapHeader como no exemplo abaixo.


private Boolean Autenticou()
{
	Boolean Autenticou = false;

	string Usuario = "admin";
	string Senha = "123456";

	if (Credencial.Usuario == Usuario && Credencial.Senha == Senha)
	{
		Autenticou = true;
	}

	return Autenticou;
}

A Credencial.Usuario e Credencial.Senha são as informação que virão de fora do Web Service pela classe de segurança.

Feito tudo isso nosso método de retorno de clientes ficara assim:


[SoapHeader("Credencial")]
[WebMethod(Description = "Retorna todos os clientes cadastrados")]
public DataSet getClientes()
{
    if (!Autenticou())
    {
        throw new Exception("Erro: Usuário não autenticado.");
    }

    DataSet dsClientes = null;
    string connectionString = "sua_connection_string";

    StringBuilder sqlString = new StringBuilder();
    sqlString.Append(" SELECT * FROM CLIENTES ");

    using (System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(connectionString))
    {
        connection.Open();

        using (System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand())
        {
            command.Connection = connection;
            command.CommandText = sqlString.ToString();

            using (System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter(command))
            {
                adapter.Fill(dsClientes);
            }
        }
    }

    return dsClientes;
}

Quando nosso método for invocado de uma aplicação e ele não tiver passado antes a autenticação, ou seja se retornar false, será gerando um Exception para a aplicação e a chamada será interrompida. Abaixo o retorno do erro:

5- Consumindo o Web Service e Autenticando um ObjectDataSource

Adicione em seu WebForm um GridView, um ObjectDataSource e faça as devidas configurações para que ele acesse o WebService e o método desejado.
Feito isso, atribua o evento ObjectCreating ao ObjectDataSource que será onde passaremos nossa autenticação da aplicação como no exemplo abaixo:


protected void objClientes_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
{
	SegurancaClientes Credencial = new SegurancaClientes();
	Credencial.Usuario = "admin";
	Credencial.Senha = "123456";

	Clientes clientes = new Clientes();
	clientes.SegurancaClientesValue = Credencial;

	e.ObjectInstance = clientes;
}

O que ocorrerá acima? Quando o ObjectDataSource for criado no momento que a pagina for carregada (no caso dos WebForms) será realizado a criação do Cabeçalho com a autenticação e enviado para o ObjectDataSource para que ele possa utilizá-lo para autenticar quando os nossos outros métodos forem invocados. Se falhar a autenticação, será gerado um Exception com a mensagem de erro.

Basicamente é isso. Mesmo que você não utilize o ObjectDataSource para consumir seus métodos do Web Service, você poderá chamar seu método informando qual a classe de segurança.
Espero que esse artigo tenha sido de grande utilidade e caso tenham duvida deixem seus comentários.

Abraço e sucesso!!!

25 comentários sobre “Criando um Web Service seguro com SoapHeader

  1. Parabens pelo artigo mais nesse caso estariamos definindo o login e senha no webservice, e caso eu queria criar um login e senha expecifico para cada empresa que acessar meu webservice?

    1. Deverá no trecho do codigo do webservice onde fixamos um login e senha, implementar para que seja feito uma validação no banco de dados.
      Exemplo:


      private Boolean Autenticou()
      {
      Boolean Autenticou = false;

      string Usuario = Credencial.Usuario.Replace("'", "");
      string Senha = Credencial.Senha.Replace("'", "");

      if (Usuario.Length > 0 && Senha.Length > 0)
      {
      DWData dwData = getData();
      StringBuilder sqlString = new StringBuilder();
      sqlString.Append(" SELECT * FROM USUARIOS ");
      sqlString.Append(" WHERE LOGIN = '" + Usuario + "' ");
      sqlString.Append(" AND SENHA = '" + Senha + "' ");

      DataSet dsUsuarios = dwData.getDataSet(sqlString.ToString());
      DataTable dtUsuarios = dsUsuarios.Tables[0];

      Autenticou = dtUsuarios.Rows.Count > 0;
      }

      return Autenticou;
      }

  2. Muito interesante o artigo, mas não consegui executa-lo.
    Estou usando o Visual Studio 2008, não consegui encontrar o tipo DWData,
    Está em qual nomespace?

    Obrigado,

    Jairo

    1. Olá Jairo,

      Essa é uma classe propria que criei para conexao com dados. Utilizei-a somente para exemplificar, porem falhei em nao ter descrito isso ou dado uma outra alternativa.
      Porem, para que o exemplo fique correto, editei o artigo para que você possa testar o exemplo.
      Qualquer problema, me mande uma mensagem e faço a correção.

      Abraço e bom estudo!

  3. Bom dia Raphael, estou tentando implementar a funcionalidade proposta em seu arquivo mas não entendi esse trecho do seu código, que é clientes.SegurancaClientesValue??

    Clientes clientes = new Clientes();
    clientes.SegurancaClientesValue = Credencial;

    Obrigado,

  4. Essa Clientes é o WebService. A propriedade SegurancaClientesValue é onde irei passar a Credencial que foi instanciada previamente. Ela foi criada por conta do SoapHeader que definimos no WebService

  5. Raphael tenho um webservice onde vou disponibilizar a inclusão de registros em um tabela do meu banco de dados, fiz como vc indicou,minha duvida é para chamar o método de inclusão como eu passo para o cliente ?
    fiz assim :
    [SoapHeader(“credencial”)]
    [WebMethod(Description = “Registrar Atendimento.”)]
    public object IncluirAtendimento(string id_spu, int codigo_empresa, int codigo_motivo, int codigo_assunto,
    int codigo_acao, int tipo_usuario, DateTime data_ocorrido, string horario_ocorrido,
    string local_ocorrido, string codigo_linha, string linha, int carro,
    string placa, string observacao, DateTime data_atendimento_inicio, DateTime data_atendimento_final,
    string nome_usuario, string telefone_usuario, string telefones_diversos, string deseja_retorno,
    string rg_usuario, string numero_cartao_usuario, string email_usuario, string endereco_usuario,
    string bairro_usuario, string cidade_usuario, string cep_usuario)

    1. Olá Junior. Suponhamos que você tenha um webservice chamado “wsAtendimento” e que o SoapHeader dele se chama “CredencialAtendimento” e possua “Usuario” e “Senha” como propriedades do tipo String, você irá chamado da seguinte forma:


      CredencialAtendimento objCredencial = new CredencialAtendimento();
      objCredencial.Usuario = "usuario_para_autenticar";
      objCredencial.Senha = "senha_do_usuario";

      wsAtendimento objAtendimento = new wsAtendimento();
      objAtendimento.CredencialAtendimentoValue = objCredencial;
      object o = objAtendimento.IncluirAtendimento(id_spu, codigo_empresa, codigo_motivo,
      codigo_assunto, codigo_acao, tipo_usuario, data_ocorrido, horario_ocorrido,
      local_ocorrido, codigo_linha, linha, carro, placa, observacao, data_atendimento_inicio,
      data_atendimento_final, nome_usuario, telefone_usuario, telefones_diversos,
      deseja_retorno, rg_usuario, numero_cartao_usuario, email_usuario, endereco_usuario,
      bairro_usuario, cidade_usuario, cep_usuario);

      Fui claro? Era essa sua dúvida?
      Qualquer coisa, deixa o comentário.

      Abraço

  6. Raphael dentro do mesmo projeto funciona perfeito, agora se eu criar outro projeto como eu vou passar as credenciais ?
    tentei fazer aqui e não deu certo ele disse que
    “O método ‘IncluirAtendimento(1523,35,1,1,1,1,1,28/02/2013 00:00:00,08:25,Aguanambi,026,messejana,3580,,,07/03/2013 11:42:59,07/03/2013 11:42:59,,,,s,,,,,,fortaleza,60000000)’ não foi encontrado no serviço teste.”

  7. Rafael na verdade preciso passar para um cliente(ele não usa .net) como ele vai consumir meu webservice estava pensando em mandar um exemplo passando a url imaginei assim :

    http://www.teste.com.br/webservice?op=incluiratendimento(os parametro)”

    Mais minha duvida é e a autenticação como passaria ?

    Meu método está assim

    [SoapHeader(“credencial”)]
    [WebMethod(Description = “Registrar Atendimento.”)]

    public object IncluirAtendimento(string id_spu, int codigo_empresa, int codigo_motivo, int codigo_assunto,
    int codigo_acao,int codigo_evento, int tipo_usuario, DateTime data_ocorrido, string horario_ocorrido,
    string local_ocorrido, string codigo_linha, string linha, int carro,
    string placa, string observacao, DateTime data_atendimento_inicio, DateTime data_atendimento_final,
    string nome_usuario, string telefone_usuario, string telefones_diversos, string deseja_retorno,
    string rg_usuario, string numero_cartao_usuario, string email_usuario, string endereco_usuario,
    string bairro_usuario, string cidade_usuario, string cep_usuario)

    1. Na verdade Junior, independente da linguagem que o cliente vai usar, você vai passar para ele o WSDL.

      Exemplo: http://www.servidor.com/webservice.asmx?wsdl

      O que você pode fazer é criar um código de exemplo na linguagem que ele utiliza para auxilia-lo. Outra coisa é passar a URL do webservice sem o WSDL para que ele veja as informações sobre o webservice, tais como WebMetodos e seus parâmetros.

      Normalmente, na empresa que trabalho, eu passo um documento descrevendo todo o webservice com as seguintes informações:

      • Estrutura – Listo todas as classes utilizas alem de descrever as tipagens e tamanhos
      • Metodos- Listo todos os Web Métodos disponíveis no webservice alem de descrever cada parâmetro passado e retornado.
      • Diagrama de classe – Utilizando a ferramenta do próprio Visual Studio, gero o Diagrama das classes utilizadas no webservice
      • Informações adicionais – Incluo informações como URL de homologação e URL de produção

      No meu caso eu não gero código de exemplo em nenhuma outra linguagem, até porque normalmente os cliente que atendo utilizam Visual Basic 6, Delphi e até SAP.

      Espero que essas informações lhe ajude.
      Abraço

  8. Junior, esqueci de mencionar sobre a autenticação, se for em .NET é como lhe exemplifiquei anteriormente, já em outra linguagem fica um pouco mais complicado.
    Talvez a solução para você seja não utilizar o SoapHeader e usar a autenticação passando os dados por parâmetro do webservice, ou seja, para todos os web métodos você coloca o primeiro parâmetro como sendo os dados de autenticação.

  9. Raphael uma saída para o que eu preciso não seria mais fácil fazem um wcf data service, parece que esse cliente usa php, ele me passou como acessar o webservice dele e eu conseguir agora preciso que ele acesse o meu pois iremos trocar informações, vou permitir que ele grave registros no meu banco e ele vai me permitir atualizar esse registro no banco dele.

  10. Raphael eu descomentei a linha no webservice para permitir que ele seja chamado por um script, minha duvida é como eu faria para consumir ele usando json, vc tem algum exemplo ai ?

  11. Bom dia,

    Mais uma impecável ajuda que chega a Portugal!

    Caro Raphael, você neste artigo não colocou
    àquelas janelinhas iniciais de como se começa
    tudo…antes de chegar ao código.
    Neste caso, o inicio é semelhante a qual dos seus
    artigos anteriores? Agradecia ajuda, pois
    estou a preparar-me para um exame (prova) da
    escola.

    Forte abraço de Portugal!! Valeu!

    Gonçalo

  12. Opa cara tudo bom ? muito bom seu tutorial cara quero desenvolver um aplicativo onde quero ter um banco de dados online e um usuário ira manda informaçoes e os outros iram receber mais tenho medo de n se seguro onde só aquele usuário gerente pode meche sabe e os outros só receberam me falaram q o mais seguro é web server teria uma ideia

    1. Olá Goulart, você poderá utilizar o SoapHeader para autentica o usuário. Quando você utiliza o SoapHeader de forma correta, poderá fazer com que somente os usuários autenticados consulte os recursos.
      Suponhamos que sua aplicação consuma um método do webservice que consulta uma lista de documentos para download. Para que o aplicativo faça acesso a esse recurso é necessário que ele faça a autenticação. Quando autenticado, você passa a enviar via SoapHeader esses dados para verificar se o usuário é válido antes de consultar o recurso. Dessa forma você garante que somente usuários cadastrados possam acessa-lo.
      Outro ponto importante que você poderá implementar níveis de acesso a seu aplicativo e permitir que somente o usuário tenha acesso a recursos de seu nível diferenciando assim ADMINISTRADOR de USUÁRIO COMUM.

      Espero que isso lhe ajude a pensar na melhor forma de implementar sua aplicação.
      Abraço e bom estudo!

  13. Raphael, boa tarde!
    Criei o web service seguindo seu excelente tutorial e tudo funcionou bem, até eu tentar consumi-lo via ajax. Não estou conseguindo receber o header, já alterei o web.config: , já tentei passar o xml inteiro, enfim, não consegui. Se tiver como dar uma luz, ficarei muito grato. Segue a chamada ajax:
    $.ajax({
    type: “POST”,
    url: “http://localhost:50815/webservice/webservice.asmx/HelloWorld”,
    contentType: “text/xml; charset=utf-8”,
    dataType: “xml”,
    beforeSend: function (xhr) {
    xhr.setRequestHeader(‘usuario’, ‘admin’);
    xhr.setRequestHeader(‘senha’, ‘123456’);
    },
    success: function (msg) {
    },
    error: function (msg) {
    }
    });

Os comentários estão fechados.