如何:为数据服务请求指定客户端凭据(Silverlight 客户端)

可以指定在向实现开放式数据协议 (OData) 的数据服务发出请求时所使用的凭据。 为此,必须发出跨域请求、必须在浏览器之外运行,或者必须显式将 HttpStack 属性的值设置为 ClientHttp。 在这些情况下,将使用凭据缓存中的默认凭据。 还可以提供非默认凭据,方法是将 UseDefaultCredentials 属性设置为 false,并为 DataServiceContextCredentials 属性提供 NetworkCredential。 本主题中的示例演示如何显式提供 Silverlight 客户端从数据服务请求数据时使用的凭据。

应用程序访问的 Northwind 示例数据服务是在完成 WCF 数据服务 快速入门时创建的。 此外,还可以使用 OData 网站上发布的公共 Northwind 示例数据服务;该示例数据服务是只读的,当尝试保存更改时会返回错误。

示例

下面的示例来自一个可扩展应用程序标记语言 (XAML) 文件的代码隐藏文件,该 XAML 文件是 Silverlight 应用程序的主页。 该示例显示了一个 LoginWindow 实例,用于收集用户的身份验证凭据,然后在使用 Silverlight 客户端 HTTP 实现向数据服务发出请求时,使用这些非默认凭据。

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();
            }
        }
    }
}

以下 XAML 定义了 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>

以下示例来自 ChildWindow 的代码隐藏页,该子窗口用于在向数据服务发出请求时收集用户的身份验证凭据。

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();
            }
        }

    }
}

下面的 XAML 定义了登录窗口,此窗口是 Silverlight 应用程序的 ChildWindow

    <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>

安全性

本主题中的示例适用以下安全注意事项:

  • 为验证此示例中提供的凭据是否能够正常工作,罗斯文数据服务必须使用一种非匿名访问的身份验证方案。 否则,承载数据服务的网站将不会请求凭据。

  • 用户凭据应仅在执行过程中请求并且不应缓存。 必须始终安全地存储凭据。

  • 使用基本和摘要式身份验证发送的数据不会加密,因此攻击者会看到数据。 此外,基本身份验证凭据(用户名和密码)是以明文形式发送的,会被截取。

请参阅

概念

创建 Silverlight 应用程序 (WCF Data Services)

其他资源

WCF Data Services Tasks for Silverlight

Security