IIS e SQL Server

Como disse na crónica anterior, a Microsoft está sempre a mudar a forma de trabalhar, os processos de comunicação, as configurações dos programas e dos sistemas, tudo.

Depois de instalar o Windows 2012 Server, com IIS e SQL Server Express, criei uma aplicação em C# para Web, no Visual Studio e tentei fazer o deployment para o servidor.

O SQL Server estava a funcionar, e conseguia aceder a ele localmente e remotamente, através do SQL Server Management Studio. Algumas pistas da forma de configurar este ambiente, estão na tal crónica anterior.

Criei uma base de dados simples, para teste, e conseguia aceder a ela, modificá-la, local e remotamente. Para isso tive que abri o porto 1433 em TCP, nas firewalls de Linux que ficam entre as máquinas Windows. No entanto, a aplicação Web C# não conseguia aceder à BD.

Quando escrevi a 1ª edição do meu livro Linguagens WEB, a Microsoft tinha acabado de lançar a primeira versão do dotNet. Usei as strings de ligação (connection strings) e as configurações dos sistemas de então. Mais tarde, na 4ª edição, tive que mudar tudo, pois a Microsoft decidiu alterar uma série de coisas, inclusive o nome da máquina local, que por vezes é localhost, outras vezes é (local), é como lhes apetece. Mas as strings de ligação à BD e as configurações também mudaram nessa altura. Além disso, passou a existir um servidor IIS Express no Visual Studio, para que os deployments não fossem feitos logo no servidor de produção, e assim, poder-se fazer debug.

Agora, mudou tudo outra vez: strings de ligação, formas de autenticação, utilizadores do sistema, configurações, e por aí fora. Ficam aqui registadas as configurações necessárias para fazer o deployment local (no IIS Express) e remoto (no IIS de produção). Na minha configuração, decidi ter apenas uma base de dados no servidor de produção, portanto, tanto o IIS de produção, como o virtual acedem à mesma BD.

A configuração é a seguinte: no Windows Server 2012, está o servidor Web (IIS) de produção e a BD (SQL Server Express); na máquina de desenvolvimento, está o Visual Studio, com um IIS Express.

No ficheiro Web.Debug.config, coloquei a seguinte string de ligação:

<connectionStrings>
<add name="BD"
connectionString="Data Source=192.168.4.201\SQLEXPRESS;Initial Catalog=escola;User Id=sa;Password=Pass-xxxx"
providerName="System.Data.SqlClient" />
</connectionStrings>

No ficheiro Web.Release.config, coloquei a seguinte string de ligação:

<connectionStrings>
<add name="BD"
connectionString="Data Source=localhost\SQLEXPRESS;Initial Catalog=escola;Integrated Security=SSPI"
providerName="System.Data.SqlClient" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
</connectionStrings>

A ligação no Web.Release.config pode ser feita através de autenticação integrada, pois os dois servidores (IIS e SQL Server) estão na mesma máquina. O mesmo já não acontece no Web.Debug.config, pois o IIS Express está a correr na máquina de desenvolvimento e tem que aceder à BD remotamente, por isso precisa de usar credenciais de autenticação: nome de utilizador e password.

Mas as coisas não são assim tão simples. É necessário que o SQL Server aceite estas dusa formas de autenticação, para permitir o acesso aos dados.

Na versão anterior destas confusões, desculpe, configurações da Microsoft, havia um utilizador IUSR_<máquina> que era responsável pelo IIS, e bastava autorizá-lo junto do SQL Server. Agora tudo mudou. E provavelmente vai mudar de novo daqui a 2 ou 3 anos. Mas continuemos.

Configurar o SQL Server para aceitar pedidos locais do IIS

Para conseguir chegar a esta configuração, os dois sites seguintes foram fundamentais:
Configuring IIS, ASP.NET, and SQL Server
Add IIS 7 AppPool Identities as SQL Server Logons
O primeiro diz como configurar corretamente um utilizador para aceder ao SQLEXPRESS, mas falha no Login name. O segundo diz qual é o login name, que no meu caso foi: IIS APPPOOL\DefaultAppPool

Eis a lista de instruções. De notar que o utilizador está errado: não é NT AUTHORITY\NETWORK SERVICE, mas sim IIS APPPOOL\DefaultAppPool, como indica o 2º site.

Adicionar o utilizador à lista de utilizadores com permissões

Open SQL Server Management Studio (shortcut: Start -> Run -> ssms)
Connect when prompted.
Expand Security and then expand Logins.
If you don’t have the network service listed (should be NT AUTHORITY\NETWORK SERVICE):
Right-click on the Logins folder and select New Login.
At the top, in the ‘Login Name’ field, enter NETWORK SERVICE. If it refuses to accept that, try entering NT AUTHORITY\NETWORK SERVICE.
Now select the Server Roles tab on the left.
You can tick any role you like, but for me I will give it ‘public’ access.
Now select the User Mapping tab on the left.
Tick all the databases you want to allow this service account to access.
In the Schema column for each selected database, set the value to dbo (or whatever schema you are using in your database).
Then, select one database row at a time and set the following permissions for it:
db_datareader
db_datawriter
public
Now click OK.
If you do have the network service account listed, edit that login entry and then follow steps (5 – 9) above.

Autorizar o acesso a uma BD

Expand Databases on the left.
Expand the Security folder and then expand Users.
The service account should be listed there. Right-click and Properties on the service account (for us, NT AUTHORITY\NETWORK SERVICE).
Select the Securables tab on the left.
Click on the Search button.
Select ‘Specific Objects’ and click OK.
Now click the Object Types button.
Scroll down and tick Schemas. Click OK.
In the textbox below, enter the schema you are giving access for (the same as Step 7 above). In our case, it will be dbo. Click OK.
At the bottom, select all the permissions you want to give for that database. In my case, I need quite extensive access to my database so I will be selecting these permissions:
Alter
Control
Create Sequence
Delete
Execute
Insert
References
Select
Update
Now click OK.

E aqui vai a correção à primeira caixa (nome correto do utilizador do IIS)

In SQL Server Management Studio, look for the Security folder (the security folder at the same level as the Databases, Server Objects, etc. folders…not the security folder within each individual database)
Right click logins and select “New Login”
In the Login name field, type IIS APPPOOL\YourAppPoolName – do not click search
Fill whatever other values you like (i.e., authentication type, default database, etc.)
Click OK

Para o acesso remoto, através do IIS virtual, a página seguinte foi fundamental, principalmente a 2ª resposta:
SQL Network Interfaces, error: 26 – Error Locating Server/Instance Specified

“Every time a client makes a connection to SQL Server named instance, we will send a SSRP UDP packet to the server machine UDP port 1434.”
Make sure the SQL Browser service is running on the server.
If the firewall is enabled on the server, you need to put sqlbrowser.exe and/or UDP port 1434 into exception.

Foi necessário pôr a correr, em modo automático, o serviço SQL Server Browser, abrir o porto 1434 em UDP, na firewall do Windows Server e dos sistemas Linux intermédios.

Já agora, fica aqui o resto do código da aplicação

Default.aspx

<%@ Page Language="c#" AutoEventWireup="false" Codebehind="Ola.aspx.cs" Inherits="ola.Ola"%>
<% ola.Ola t = new ola.Ola(); %>

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1>Teste</h1>
<p>Mensagem: <% Response.Write(t.Message); %></p>
<p>IPv6: <% Response.Write(t.IPv6); %></p>
<p>IP: <% Response.Write(t.IP); %></p>
<p>Data: <% Response.Write(t.date); %></p>
<p>Connection String : <% Response.Write(t.conString); %></p>
<p>Dados: <% Response.Write(t.dados); %></p>
</body>
</html>

Ola.aspx.cs

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Web;

namespace ola
{
    public class Ola : System.Web.UI.Page
    {
        public string Message;
        public string IPv6;
        public string IP;
        public string date;
        public string dados;
        public string conString;
        private SqlConnection ligacao;

        public Ola ()
        {
            Message = "Aplicação de teste";
            IPv6 = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList.GetValue(0).ToString();
            IP = HttpContext.Current.Request.Params["HTTP_CLIENT_IP"] ?? HttpContext.Current.Request.UserHostAddress;
            date = DateTime.UtcNow.ToString("dd/MM/yyyy HH:mm zzz");
            
            System.Configuration.Configuration rootWebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("/wwwroot");
            System.Configuration.ConnectionStringSettings connString;
            connString = rootWebConfig.ConnectionStrings.ConnectionStrings["BD"];
            conString = connString.ConnectionString;
            dados = "";
            ligacao = new SqlConnection(conString);
            try
            {
                ligacao.Open();
                String sql = "SELECT * FROM aluno";
                SqlCommand comando = new SqlCommand(sql, ligacao);
                using (SqlDataReader dbDados = comando.ExecuteReader())
                {
                    while (dbDados.Read())
                    {
                        dados += dbDados.GetString(1) + ", " + dbDados.GetString(2) + ";";
                    }
                }
                ligacao.Close();
            }
            catch (Exception e)
            {
                dados = "ERRO: " + e.Message;
            }
        }


    }
}

E o resultado

Teste
Mensagem: Aplicação de teste
IPv6: fe80::7c07:5225:9e81:8b40%12
IP: 192.168.0.54
Data: 12/08/2018 20:27 +01:00
Connection String : Data Source=localhost\SQLEXPRESS;Initial Catalog=escola;Integrated Security=SSPI
Dados: Maria, Lisboa;João, Porto;

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *