隨著Windows Azure Platform的發展,其支援的平台及語言也越來越多,行動裝置自然也是重點之一,從去年開始,Windows Azure Toolkit for Windows Phone率先推出,
緊接著for iOS與Android版本也釋出了。10個月後的今天,這些Windows Azure Toolkits隨著Windows Azure Platform的發展逐步演進
The Windows Azure Toolkits – Integrated ACS with iOS、Windows Phone、Android and Windows 8
文/黃忠成
隨著Windows Azure Platform的發展,其支援的平台及語言也越來越多,行動裝置自然也是重點之一,從去年開始,Windows Azure Toolkit for Windows Phone率先推出,
緊接著for iOS與Android版本也釋出了。10個月後的今天,這些Windows Azure Toolkits隨著Windows Azure Platform的發展逐步演進,除了修正了許多Bugs之外,
還加入了新的生力軍:Windows Azure Toolkits for Windows 8。
圖1
這些Toolkits的功能其實都差不多,主要是提供Storage Services(Table、Queue、Blob)存取能力,還有與ACS(Access Control Service)整合的能力,
在新加入的Windows Azure Toolkit for Windows 8中還支援了Service Bus。
本文先將重點擺在ACS部分,其餘的Storage Services部分可請讀者先下載去年Tech Days的投影片及範例,未來我會抽空補充這一部分。
http://www.code6421.com/TechDays2011/Publish.zip
關於ACS
ACS是Windows Azure Platform所提供的服務之一,主要是負責使用者身分驗證的動作,ACS本身是一個中介者,協助應用程式使用不同的Identity Providers(Facebook、
Google、Windows Live、ADFS、Open ID)來進行使用者驗證的動作。
應用程式使用ACS來進行驗證最大的好處在於可允許使用者透過主流的Identity Providers來登入你提供的服務,在現今這個社群導向的年代,使用行動裝置的使用者應該
都至少擁有Facebook、Windows Live、Google、Yahoo中的一個帳號,而ACS支援這些主流社群網站,也就是說你的服務可以不必要求使用者註冊,也不必儲存使用者的帳號密碼,
透過ACS,應用程式只需要取得需要的資料即可(like Email),也因為不須註冊就可以使用服務,使用者可以跳過繁瑣的註冊動作直接使用服務,這將提升使用者試用你的服務的意願。
另外,ACS也可作為一種Single Sign-On(單一簽入)的解決方案,不過這不在本文的討論範圍。
設定ACS
首先,你得先有Windows Azure帳號才能使用ACS,需要的請至以下網址申請測試帳號(免費的哦)
http://www.windowsazure.com/zh-tw/
有了帳號後,就可到Windows Azure Portal中啟用ACS。
圖2
點選上方的新增來添加一個服務命名空間。
圖3
圖4
於此填入命名空間的名稱及選擇國家/地區,完成後等待此服務空間啟用。
圖5
本文的範例是使用Facebook作為驗證的Identity Provider,所以需要至http://developers.facebook.com/來申請建立一個應用程式(注意,
你必須擁有Facebook帳號及通過手機簡訊驗證才能建立Facebook應用程式)。
圖6
注意App Domains及Website with Facebook Login欄位,此處需填入你的ACS 網站網址,通常是<服務命名空間>.accesscontrol.windows.net。
接著使用瀏覽器開啟https://<服務命名空間>.accesscontrol.windows.net,便可看到類似下圖的畫面。
圖7
點選識別提供者,接著再點選新增。
圖8
此處選擇Facebook應用程式。
圖9
這裡需輸入一個顯示名稱,此處使用Facebook,接著要填入之前建立Facebook的應用程式識別碼及密碼(應用程式密鑰)。
接著點選左方的信賴憑證者應用程式來新增一個應用程式。
圖7
圖10
此處需輸入顯示名稱及領域,領域必須是一個URL,可使用localhost(這只是一個識別名稱),接著選擇權杖格式為SWT,下方會出現要求產生憑證的部分。
圖11
此處只須點選產生後按下儲存即可,這樣就算完成ACS整合Facebook Identity Provider的部分了,接下來就是開始透過應用程式連接ACS來進行與Facebook的整合驗證。
Windows Phone 7
透過Windows Azure Toolkit for Windows Phone,應用程式可直接使用其提供的Library&Controls來存取Storage Services及ACS,讀者可以由以下網址下載。
要連接ACS,只需建立一個新的Windows Phone Application專案,並將WAToolkitForWP7\Samples\WP7.1\Libraries\SL.Phone.Federation目錄下的.csproj加到方案中,
再由Windows Phone Application專案加入對SL.Phone.Federation的參考即可。
圖12
接著修改App.xaml,加入以下內容。
圖13
建立一個新的頁面(SignInControl),並加入Azure Tookit所提供的Login控制項。
圖14
Realm需對應信賴者憑證應用程式中的領域設定。
圖10
ServiceNamespace則對應ACS命名空間(我們的ACS網址是demoacs3.accesscontrol.windows.net,所以此處為demoacs3)。
修改主頁面(MainPage.xaml)。
……………………
using SL.Phone.Federation.Utilities;
namespace DemoACS
{
public partial class MainPage : PhoneApplicationPage
{
RequestSecurityTokenResponseStore _rstrStore = null;
// Constructor
public MainPage()
{
InitializeComponent();
_rstrStore = (RequestSecurityTokenResponseStore)App.Current.Resources["rstrStore"];
}
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
if (!_rstrStore.ContainsValidRequestSecurityTokenResponse())
{
NavigationService.Navigate(new Uri("/SignInControl.xaml", UriKind.Relative));
}
else
{
textBlock1.Text = "thanks for your login";
}
}
}
}
完成後測試,將可看到以下的畫面。
圖15
圖16
輸入帳密後,正確的話就可看到圖17。
圖17
Android
Windows Azure Toolkit for Android可由以下網址下載。
https://github.com/WindowsAzure-Toolkits/wa-toolkit-android
下載後解壓,透過Eclipse將WAToolkitForAndroid\library\accesscontrol Import到現在使用的Workspace,接著建立一個Android Application Project,Build SDK請選擇Android 2.2,
修改此Project的Properties中Android頁面設定,將accesscontrol這個Project新增為Library。
圖18
修改Project中的AndroidManifest.xml如下。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.demoacsb"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="8" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SuccessLoginActivity"
android:label="@string/title_activity_success_login" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".UnsuccessLogin"
android:label="@string/title_activity_unsuccess_login" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.microsoft.samples.windowsazure.android.accesscontrol.login.AccessControlLoginActivity" android:label="@string/app_name" />
<activity android:name="com.microsoft.samples.windowsazure.android.accesscontrol.login.AccessControlWebAuthActivity" android:label="@string/app_name" />
</application>
</manifest>
加入Resource Strings。
/res/values/strings.xml
<resources>
<string name="app_name">DemoACSB</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_main">MainActivity</string>
<string name="cloud_ready_acs_namespace">demoacs3</string>
<string name="cloud_ready_acs_realm">http://www.code6421.com</string>
<string name="cloud_ready_acs_symmetric_key">9FhZFekCsz1YsWpjdiQxJAbh2AhW3WS/aQ----blx3g=</string>
<string name="title_activity_success_login">SuccessLoginActivity</string>
<string name="title_activity_unsuccess_login">UnsuccessLogin</string>
</resources>
注意cloud_ready_acs_symmetric_key欄位,這裡要放置ACS中的憑證金鑰,另外cloud_ready_acs_realm則是對應信賴者憑證應用程式中的領域設定。
加入兩個Activity。
SuccessLoginActivity.java
package com.example.demoacsb;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v4.app.NavUtils;
public class SuccessLoginActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_success_login);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_success_login, menu);
return true;
}
}
/res/layout/activity_success_login.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="@dimen/padding_medium"
android:text="Login OK"
tools:context=".SuccessLoginActivity" />
</RelativeLayout>
UnsuccessLogin.java
package com.example.demoacsb;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v4.app.NavUtils;
public class UnsuccessLogin extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_unsuccess_login);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_unsuccess_login, menu);
return true;
}
}
/res/layout/activity_unsuccess_login.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="@dimen/padding_medium"
android:text="Login Fail"
tools:context=".UnsuccessLogin" />
</RelativeLayout>
在主要的Activity對應的Layout加入一個Button。
res/layout/activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="162dp"
android:text="Login" />
</RelativeLayout>
鍵入以下的程式碼。
MainActivity.java
package com.example.demoacsb;
import android.os.Bundle;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.support.v4.app.NavUtils;
import com.microsoft.samples.windowsazure.android.accesscontrol.core.IdentityProvidersRepository;
import com.microsoft.samples.windowsazure.android.accesscontrol.login.AccessControlLoginActivity;
import com.microsoft.samples.windowsazure.android.accesscontrol.login.AccessControlLoginContext;
import com.microsoft.samples.windowsazure.android.accesscontrol.swt.SimpleWebTokenHandler;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button manualLoginButton = (Button) findViewById(R.id.button1);
manualLoginButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
doManualLogin();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
private void doManualLogin() {
if (!validateACSConfiguration()) return;
String acsNamespace = getString(R.string.cloud_ready_acs_namespace);
String acsRealm = getString(R.string.cloud_ready_acs_realm);
String acsSymmKey = getString(R.string.cloud_ready_acs_symmetric_key);
Intent intent = new Intent(this, AccessControlLoginActivity.class);
AccessControlLoginContext loginContext = new AccessControlLoginContext();
loginContext.IdentityProviderRepository = new IdentityProvidersRepository(
String.format(
"https://%s.accesscontrol.windows.net/v2/metadata/IdentityProviders.js?protocol=javascriptnotify&realm=%s&version=1.0",
acsNamespace, acsRealm));
loginContext.AccessTokenHandler = new SimpleWebTokenHandler(acsRealm, acsSymmKey);
loginContext.SuccessLoginActivity = SuccessLoginActivity.class;
loginContext.ErrorLoginActivity = com.example.demoacsb.UnsuccessLogin.class;
intent.putExtra(AccessControlLoginActivity.AccessControlLoginContextKey, loginContext);
startActivity(intent);
}
private boolean validateACSConfiguration() {
String acsNamespace = getString(R.string.cloud_ready_acs_namespace);
String acsRealm = getString(R.string.cloud_ready_acs_realm);
String acsSymmKey = getString(R.string.cloud_ready_acs_symmetric_key);
if (isValidConfigurationValue(acsNamespace) && isValidConfigurationValue(acsRealm) && isValidConfigurationValue(acsSymmKey))
return true;
AlertDialog dialog = new AlertDialog.Builder(this).create();
dialog.setTitle("Configuration Error");
dialog.setCanceledOnTouchOutside(true);
dialog.setCancelable(true);
dialog.setMessage("Please review your ACS configuration and try again");
dialog.show();
return false;
}
private boolean isValidConfigurationValue(String value) {
return !(value.startsWith("{") || value.trim() == "");
}
}
完成後執行前,請特別注意要使用Android 2.2的模擬器,因為2.2以上的模擬器在WebView物件處理上有Bugs,無法處理Scripting Injection部分。
圖19
圖20
圖21
圖22
iOS
Windows Azure Toolkit for iOS 可由以下網址下載。
https://github.com/WindowsAzure-Toolkits/wa-toolkit-ios/
下載後須先透過XCode 開啟WAToolkitForIOS\library\watoolkitios-lib.xcodeproj這個Project,然後進行編譯後取得.a及對應的.h檔案。
接著透過XCode建立一個Single View的Project,先取名為DemoACS,將先前取得的.h檔案複製到此Project目錄下的watoolkitios-lib目錄中,並將.a檔案新增進此專案中,
接著修改DemoACS-Prefix.pch加入import。
DeniACS-Prefix.pch
//
// Prefix header for all source files of the 'DemoACS' target in the 'DemoACS' project
//
#import
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
#ifdef __OBJC__
#import
#import
#import "watoolkitios-lib/WAToolkit.h"
#endif
然後修改MainStoryboard_iPhone.storyboard加入一個Button,並修改DemoACSViewController.h加入一個method。
DemoACSViewController.h
//
// DemoACSViewController.h
// DemoACS
//
// Created by on 12/7/6.
// Copyright 2012年 __MyCompanyName__. All rights reserved.
//
#import
@interface DemoACSViewController : UIViewController {}
- (IBAction)loginAction:(id)sender;
@end
緊接著修改DemoACSViewController.m。
//
// DemoACSViewController.m
// DemoACS
//
// Created by on 12/7/6.
// Copyright 2012年 __MyCompanyName__. All rights reserved.
//
#import "DemoACSViewController.h"
NSString * const ACSNamespace = @"demoacs3";
NSString * const ACSRealm = @"http://www.code6421.com";
NSString * const NameIdentifierClaim = @"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
NSString * const AccessTokenClaim = @"http://www.facebook.com/claims/AccessToken";
@implementation DemoACSViewController- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{ [super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
- (IBAction)loginAction:(id)sender {
WACloudAccessControlClient *acsClient = [WACloudAccessControlClient accessControlClientForNamespace:ACSNamespace realm:ACSRealm];
[acsClient showInViewController:self allowsClose:NO withCompletionHandler:^(BOOL authenticated) {
if (!authenticated) {
UIAlertView *dialog = [[UIAlertView alloc] initWithTitle:@"Info" message:@"Login Fail" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[dialog show];
} else {
UIAlertView *dialog = [[UIAlertView alloc] initWithTitle:@"Info" message:@"Login Successed" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[dialog show];
}
}];
}
@end
最後透過Interface Builder把loginAction與畫面上的Button建立繫結,然後修改此Project的Build Settings中的Other Link Flags為 -ObjC -all_load 即可。
圖23
圖24
圖25
圖26
Windows 8
Windows Azure Toolkit for Windows 8可由下列網址下載。
http://watwindows8.codeplex.com/
不過此版本目前僅支援Consumer Preview及Visual Studio 2012 Beta,要動點手腳才能正常使用,另外,其ACS部分僅提供使用protocol為WS-Federation模式的驗證,跟我們前面使用
javascriptnotify為protocol的方式不同,前者目前需要一個收取claims的網站,因此我修改了Toolkit中的ACS部分,使其支援javascriptnotify驗證模式(載點在文末)。
請先下載我所提供的ACS Library,然後建立一個Windows Metro style App 專案,加入兩個類別。
LoginEventArgs.cs
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
namespace Windows8.CSharp.Identity.AccessControl
{
using System;
using Windows8.Identity.AccessControl;
public class LoginEventArgs : EventArgs
{
public LoginEventArgs(IAuthenticationResult result)
{
this.Result = result;
}
public IAuthenticationResult Result { get; private set; }
}
}
ProvidersInfoEventArgs.cs
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
namespace Windows8.CSharp.Identity.AccessControl
{
using System;
using Windows8.Identity.AccessControl;
public class ProvidersInfoEventArgs : EventArgs
{
public ProvidersInfoEventArgs(IIdentityProvidersInfo info)
{
this.ProvidersInfo = info;
}
public IIdentityProvidersInfo ProvidersInfo { get; private set; }
}
}
接著加入一個UserControl,取名為ACSLogin。
ACSLogin.xaml
<UserControl x:Class="Windows8.CSharp.Identity.AccessControl.ACSLogin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<Style x:Key="TitleStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="#707070" />
<Setter Property="FontFamily" Value="Segoe UI Light" />
<Setter Property="FontSize" Value="16" />
</Style>
<Style x:Key="ListBoxTextStyle" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe UI Light" />
<Setter Property="FontSize" Value="16" />
<Setter Property="Padding" Value="14" />
</Style>
</UserControl.Resources>
<StackPanel Name="root">
<TextBlock Text="Select one of the following identity providers to authenticate using ACS:" Style="{StaticResource TitleStyle}" TextWrapping="Wrap" />
<ProgressBar x:Name="ProgressBar" Margin="0 50 0 0" IsIndeterminate="True" Width="500" HorizontalAlignment="Left" Visibility="Collapsed" />
<ListBox x:Name="IdentityProviderList" Margin="0 25 0 0" HorizontalAlignment="Left" BorderThickness="0" Width="500" Visibility="Collapsed">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Style="{StaticResource ListBoxTextStyle}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</UserControl>
ACSLogin.xaml.cs
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
namespace Windows8.CSharp.Identity.AccessControl
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows8.Identity.AccessControl;
public sealed partial class ACSLogin
{
public static readonly DependencyProperty ACSNamespaceProperty = DependencyProperty.Register("AccessControlNamespace", typeof(string), typeof(ACSLogin), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty RealmProperty = DependencyProperty.Register("Realm", typeof(string), typeof(ACSLogin), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty BouncerReplyUrlProperty = DependencyProperty.Register("BouncerReplyUrl", typeof(string), typeof(ACSLogin), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty BouncerEndUrlProperty = DependencyProperty.Register("BouncerEndUrl", typeof(string), typeof(ACSLogin), new PropertyMetadata(string.Empty));
private AccessControlManager accessControlManager;
public ACSLogin()
{
InitializeComponent();
accessControlManager = new AccessControlManager();
}
public string AccessControlNamespace
{
get
{
return (string)GetValue(ACSNamespaceProperty);
}
set
{
SetValue(ACSNamespaceProperty, value);
}
}
public string Realm
{
get
{
return (string)GetValue(RealmProperty);
}
set
{
SetValue(RealmProperty, value);
}
}
public string BouncerReplyUrl
{
get
{
return (string)GetValue(BouncerReplyUrlProperty);
}
set
{
SetValue(BouncerReplyUrlProperty, value);
}
}
public string BouncerEndUrl
{
get
{
return (string)GetValue(BouncerEndUrlProperty);
}
set
{
SetValue(BouncerEndUrlProperty, value);
}
}
public void Login()
{
this.ConfigureAccessControlManager();
this.ProgressBar.Visibility = Windows.UI.Xaml.Visibility.Visible;
var getOperation = accessControlManager.GetIdentityProviders(true);
getOperation.Completed = ((info, status) =>
{
var ipInfo = info.GetResults();
this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
if (OnProviderListRetrieved != null) OnProviderListRetrieved(this, new ProvidersInfoEventArgs(ipInfo));
foreach (var ip in ipInfo.IdentityProviderList)
IdentityProviderList.Items.Add(ip);
this.IdentityProviderList.SelectionChanged += new SelectionChangedEventHandler(IdentityProviderListItemSelected);
this.ProgressBar.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
this.IdentityProviderList.Visibility = Windows.UI.Xaml.Visibility.Visible;
});
});
}
public event EventHandler OnLogin;
public event EventHandler OnProviderListRetrieved;
private void IdentityProviderListItemSelected(object sender, SelectionChangedEventArgs e)
{
var item = this.IdentityProviderList.SelectedItem as IIdentityProvider;
IdentityProviderList.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
var loginOperation = item.Login(root, true);
loginOperation.Completed = ((info, status) =>
{
if (OnLogin != null) OnLogin(this, new LoginEventArgs(info.GetResults()));
});
}
private void ConfigureAccessControlManager()
{
this.accessControlManager.AccessControlNamespace = this.AccessControlNamespace;
this.accessControlManager.Realm = this.Realm;
this.accessControlManager.BouncerReplyUrl = this.BouncerReplyUrl;
this.accessControlManager.BouncerEndUrl = this.BouncerEndUrl;
}
}
}
最後在MainPage中添加ACSLogin控制項即可。
<Page
x:Class="App13.MainPage"
IsTabStop="false"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App13"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:loginControl="using:Windows8.CSharp.Identity.AccessControl"
mc:Ignorable="d" Loaded="MainPage_Loaded_1">
<Grid x:Name="root" Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<loginControl:ACSLogin x:Name="login" AccessControlNamespace="demoacs3" Realm="http://www.code6421.com" BouncerEndUrl="http://localhost" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows8.CSharp.Identity.AccessControl;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace App13
{
///
/// An empty page that can be used on its own or navigated to within a Frame.
///
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
login.OnLogin += login_OnLogin;
}
void login_OnLogin(object sender, LoginEventArgs e)
{
new MessageDialog(e.Result.Token).ShowAsync();
}
///
/// Invoked when this page is about to be displayed in a Frame.
///
///
Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private void MainPage_Loaded_1(object sender, RoutedEventArgs e)
{
login.Login();
}
}
}
圖27
圖28
圖29
就這樣,打完收工,下次有機會再來聊聊Storage Access部分。
ACS Library for Windows 8
http://www.code6421.com/BlogPics/W8ACS.zip