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