Compartir a través de


Cómo: Especificar las credenciales del cliente para una solicitud de servicio de datos (Cliente de Silverlight)

Puede especificar las credenciales que se usarán al realizar una solicitud a un servicio de datos que implemente el Open Data Protocol (OData). Para ello, debe realizar una solicitud entre dominios, realizar la ejecución fuera del explorador o establecer explícitamente el valor de la propiedad HttpStack en ClientHttp. En estos casos, se emplean las credenciales predeterminadas de la memoria caché de credenciales. También puede proporcionar credenciales no predeterminadas si establece la propiedad UseDefaultCredentials en false y suministra una NetworkCredential para la propiedad Credentials del DataServiceContext. En el ejemplo de este tema se muestra cómo proporcionar explícitamente credenciales que el cliente de Silverlight usará cuando solicite datos del servicio de datos.

Cuando complete el tutorial rápido de Servicios de datos de Microsoft WCF se creará el servicio de datos de ejemplo Northwind al que tiene acceso la aplicación. También puede usar el servicio de datos de ejemplo Northwind público que está en el sitio web de OData; este servicio de datos de ejemplo es de solo lectura y si se intentan guardar los cambios, devuelve un error.

Ejemplo

El ejemplo siguiente procede de la página de código subyacente de un archivo de lenguaje XAML que es la página principal de la aplicación de Silverlight. En este ejemplo se muestra una instancia de LoginWindow para recopilar las credenciales de autenticación del usuario y, a continuación, se usa estas credenciales no predeterminadas cuando se realiza una solicitud al servicio de datos mediante la implementación HTTP del cliente de Silverlight.

Imports ClientCredentials.Northwind
Imports System.Data.Services.Client
Imports System.Windows.Data
Imports System.Net

Partial Public Class MainPage
    Inherits UserControl

    ' Create the binding collections and the data service context.
    Private binding As DataServiceCollection(Of Customer)
    Private context As NorthwindEntities
    Private customerAddressViewSource As CollectionViewSource

    ' Instantiate the service URI and credentials.
    Dim serviceUri As Uri = New Uri("https://localhost:54321/Northwind.svc/")
    Private credentials As NetworkCredential = New NetworkCredential()

    Public Sub Main()
        InitializeComponent()
    End Sub

    Private Sub MainPage_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)

        ' Get credentials for authentication.
        Dim login As LoginWindow = New LoginWindow()
        AddHandler login.Closed, AddressOf loginWindow_Closed
        login.Show()
    End Sub

    Private Sub loginWindow_Closed(ByVal sender As Object, ByVal e As EventArgs)
        Dim userName = String.Empty
        Dim domain = String.Empty
        Dim password = String.Empty

        ' Get back the LoginWindow instance.
        Dim login As LoginWindow = CType(sender, LoginWindow)

        If login.DialogResult = True AndAlso Not login.userNameBox.Text Is String.Empty Then

            ' Instantiate the binding collection.
            binding = New DataServiceCollection(Of Customer)()

            ' Instantiate the context.
            context = New NorthwindEntities(serviceUri)

            ' Register the LoadCompleted event for the binding collection.
            AddHandler binding.LoadCompleted, AddressOf binding_LoadCompleted

            ' Define an anonymous LINQ query that returns a collection of Customer types.
            Dim query = From c In context.Customers
                        Where c.Country = "Germany"
                        Select c

            ' Get the user name and domain from the login.
            Dim qualifiedUserName As String() = login.userNameBox.Text.Split(New [Char]() {"\"c})
            If qualifiedUserName.Length = 2 Then
                domain = qualifiedUserName(0)
                userName = qualifiedUserName(1)
            Else
                userName = login.userNameBox.Text
            End If
            password = login.passwordBox.Password

            ' Select the client HTTP stack and set the credentials.
            context.HttpStack = HttpStack.ClientHttp
            context.UseDefaultCredentials = False
            context.Credentials = _
                New NetworkCredential(userName, password, domain)

            Try
                ' Execute the query asynchronously.
                binding.LoadAsync(query)
            Catch ex As Exception
                Dim cw = New ChildWindow()
                cw.Content = ex.Message
                cw.Show()
            End Try
        ElseIf login.DialogResult = False Then        
            Dim cw = New ChildWindow()
            cw.Content = "Login cancelled."
            cw.Show()
        End If
    End Sub
    Private Sub binding_LoadCompleted(ByVal sender As Object, ByVal e As LoadCompletedEventArgs)
        If e.Error Is Nothing Then
            serviceUriLabel.Content = serviceUri.ToString()

            ' Load all pages of Customers before binding.
            If Not binding.Continuation Is Nothing Then
                binding.LoadNextPartialSetAsync()
            Else
                ' Load your data here and assign the result to the CollectionViewSource.
                customerAddressViewSource = _
                    CType(Me.Resources("customerViewSource"), CollectionViewSource)
                customerAddressViewSource.Source = binding
            End If
        Else

            ' Display the error message from the data service.
            Dim cw = New ChildWindow()
            cw.Content = e.Error.Message
            cw.Show()
        End If
    End Sub
End Class
using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using ClientCredentials.Northwind;
using System.Data.Services.Client;

namespace ClientCredentials
{
    public partial class MainPage : UserControl
    {
        // Create the binding collections and the data service context.
        private DataServiceCollection<Customer> binding;
        NorthwindEntities context;
        CollectionViewSource customerAddressViewSource;

        // Instantiate the service URI and credentials.
        Uri serviceUri = new Uri("https://localhost:12345/Northwind.svc/");
        NetworkCredential credentials = new NetworkCredential();

        public MainPage()
        {
            InitializeComponent();
        }

        private void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            // Get credentials for authentication.
            LoginWindow login = new LoginWindow();
            login.Closed += new EventHandler(loginWindow_Closed);
            login.Show();
        }

        private void loginWindow_Closed(object sender, EventArgs e)
        {
            string userName = string.Empty;
            string domain = string.Empty;
            string password = string.Empty;

            // Get back the LoginWindow instance.
            LoginWindow login = (LoginWindow)sender;

            if (login.DialogResult == true && login.userNameBox.Text != string.Empty)
            {
                // Instantiate the binding collection.
                binding = new DataServiceCollection<Customer>();

                // Instantiate the context.
                context =
                    new NorthwindEntities(serviceUri);

                // Register the LoadCompleted event for the binding collection.
                binding.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(binding_LoadCompleted);

                // Define an anonymous LINQ query that returns a collection of Customer types.
                var query = from c in context.Customers
                            where c.Country == "Germany"
                            select c;


                // Get the user name and domain from the login.
                string[] qualifiedUserName = login.userNameBox.Text.Split(new char[] {'\\'});
                if (qualifiedUserName.Length == 2)
                {
                    domain = qualifiedUserName[0];
                    userName = qualifiedUserName[1];
                }
                else
                {
                    userName = login.userNameBox.Text;
                }
                password = login.passwordBox.Password;

                // Select the client HTTP stack and set the credentials.
                context.HttpStack = HttpStack.ClientHttp;
                context.UseDefaultCredentials = false;
                context.Credentials = 
                    new NetworkCredential(userName, password, domain);

                try
                {
                    // Execute the query asynchronously.
                    binding.LoadAsync(query);
                }
                catch (Exception ex)
                {
                    ChildWindow cw = new ChildWindow();
                    cw.Content = ex.Message;
                    cw.Show();
                }
            }
            else if (login.DialogResult == false)
            {
                ChildWindow cw = new ChildWindow();
                cw.Content = "Login cancelled.";
                cw.Show();
            }
        }

        private void binding_LoadCompleted(object sender, LoadCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                serviceUriLabel.Content = serviceUri.ToString();

                // Load all pages of Customers before binding.
                if (binding.Continuation != null)
                {
                    binding.LoadNextPartialSetAsync();
                }
                else
                {
                    // Load your data here and assign the result to the CollectionViewSource.
                    customerAddressViewSource =
                        (CollectionViewSource)this.Resources["customerViewSource"];
                    customerAddressViewSource.Source = binding;
                }
            }
            else
            {
                // Display the error message from the data service.
                ChildWindow cw = new ChildWindow();
                cw.Content = e.Error.Message;
                cw.Show();
            }
        }
    }
}

El código XAML siguiente define la página principal de la aplicación de Silverlight.

    <UserControl x:Class="ClientCredentials.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="312" d:DesignWidth="577" 
             xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
             xmlns:my="clr-namespace:ClientCredentials" Loaded="MainPage_Loaded">
    <UserControl.Resources>
        <CollectionViewSource x:Key="customerViewSource" 
                              d:DesignSource="{d:DesignInstance my:Northwind.Customer, CreateList=True}" />
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White" DataContext="" Height="312" Width="577"
          VerticalAlignment="Top" HorizontalAlignment="Left">
        <Grid.RowDefinitions>
            <RowDefinition Height="203*" />
            <RowDefinition Height="119*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="336*" />
        </Grid.ColumnDefinitions>
        <sdk:DataGrid AutoGenerateColumns="False" Height="213" HorizontalAlignment="Left" 
                      ItemsSource="{Binding Source={StaticResource customerViewSource}}" 
                      Name="customerDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" 
                      VerticalAlignment="Top" Width="553" Margin="12,44,0,0" 
                      Grid.RowSpan="2" Grid.ColumnSpan="1">
            <sdk:DataGrid.Columns>
                <sdk:DataGridTextColumn x:Name="customerIDColumn" Binding="{Binding Path=CustomerID}" 
                                        Header="Customer" Width="80" />
                <sdk:DataGridTextColumn x:Name="addressColumn" Binding="{Binding Path=Address}" 
                                        Header="Address" Width="180" />
                <sdk:DataGridTextColumn x:Name="cityColumn" Binding="{Binding Path=City}" 
                                        Header="City" Width="120" />
                <sdk:DataGridTextColumn x:Name="countryColumn" Binding="{Binding Path=Country}" 
                                        Header="Country" Width="80" />
                <sdk:DataGridTextColumn x:Name="postalCodeColumn" Binding="{Binding Path=PostalCode}" 
                                        Header="Postal Code" Width="90" />
                <sdk:DataGridTextColumn Binding="{Binding Path=CompanyName}" Header="CompanyName" />
                <sdk:DataGridTextColumn Binding="{Binding Path=ContactName}" Header="ContactName" />
                <sdk:DataGridTextColumn Binding="{Binding Path=Phone}" Header="Phone" />
            </sdk:DataGrid.Columns>
        </sdk:DataGrid>
        <sdk:Label Grid.Row="0" Grid.Column="0" Height="26" HorizontalAlignment="Left" Margin="16,12,0,0" 
                   Name="serviceUriLabel" VerticalAlignment="Top" Width="550"  />
    </Grid>
</UserControl>

El siguiente ejemplo proviene de la página de código subyacente de la ChildWindow que se usa para recopilar las credenciales de autenticación del usuario antes de realizar una solicitud al servicio de datos.

Imports System.ComponentModel

Partial Public Class LoginWindow
    Inherits ChildWindow

    Public Sub New()
        InitializeComponent()
    End Sub

    Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click
        Me.DialogResult = True
    End Sub

    Private Sub CancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles CancelButton.Click
        Me.DialogResult = False
    End Sub

    Private Sub LoginWindow_Closing(ByVal sender As System.Object, ByVal e As CancelEventArgs)
        If Me.DialogResult = True AndAlso _
                    (Me.userNameBox.Text = String.Empty OrElse Me.passwordBox.Password = String.Empty) Then
            e.Cancel = True
            Dim cw As ChildWindow = New ChildWindow()
            cw.Content = "Please enter name and password or click Cancel."
            cw.Show()
        End If
    End Sub
End Class
using System;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;

namespace ClientCredentials
{
    public partial class LoginWindow : ChildWindow
    {
        public LoginWindow()
        {
            InitializeComponent();
        }

        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = true;
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
             this.DialogResult = false;
        }

        private void LoginWindow_Closing(object sender, CancelEventArgs e)
        {
            if (this.DialogResult == true &&
                    (this.userNameBox.Text == string.Empty || this.passwordBox.Password == string.Empty))
            {
                e.Cancel = true;
                ChildWindow cw = new ChildWindow();
                cw.Content = "Please enter name and password or click Cancel.";
                cw.Show();
            }
        }

    }
}

El código XAML siguiente define la ventana de inicio de sesión que es una ChildWindow de la aplicación de Silverlight.

    <controls:ChildWindow x:Class="ClientCredentials.LoginWindow"
           xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
           Width="400" Height="200" 
           Title="LoginWindow" xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" Closing="LoginWindow_Closing">
    <StackPanel Name="LayoutRoot" Orientation="Vertical" VerticalAlignment="Top">
        <StackPanel Orientation="Horizontal">
            <TextBlock Height="25" HorizontalAlignment="Left" Margin="10,20,0,0" Name="userNameLabel" VerticalAlignment="Top" 
                       Width="80" Text="User name:"/>
            <TextBox Height="23" HorizontalAlignment="Left" Margin="10,20,0,0"  Name="userNameBox" VerticalAlignment="Top" 
                     Width="150" Text="DOMAIN\login"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
            <TextBlock Height="25" HorizontalAlignment="Left" Margin="10,20,0,0" Name="pwdLabel" Width="80" Text="Password:"/>
            <PasswordBox Height="23" HorizontalAlignment="Left" Margin="10,20,0,0" Name="passwordBox" Width="150" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Height="80" VerticalAlignment="Top">
            <Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" Width="75" Height="23" 
                HorizontalAlignment="Right" Margin="8" />
            <Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75" Height="23" 
                HorizontalAlignment="Right" Margin="8" />
        </StackPanel>
    </StackPanel>
</controls:ChildWindow>

Seguridad

Las siguientes consideraciones de seguridad son aplicables al ejemplo de este tema:

  • Para comprobar que funcionan las credenciales proporcionadas en este ejemplo, el servicio de datos de Northwind debe usar un esquema de autenticación que no sea de acceso anónimo. De lo contrario, el sitio web que hospede el servicio de datos no solicitará las credenciales.

  • Las credenciales de usuario solo se deben solicitar durante la ejecución y no se deben almacenar en la memoria caché. Las credenciales se deben almacenar siempre de forma segura.

  • Los datos enviados con la autenticación básica e implícita no se cifran. Por tanto, los puede ver un adversario. Además, las credenciales de autenticación básica (nombre de usuario y contraseña) se envían como texto no cifrado y se pueden interceptar.

Vea también

Conceptos

Crear aplicaciones de Silverlight (WCF Data Services)

Otros recursos

WCF Data Services Tasks for Silverlight

Security