何もかもこの夏の暑さのせいです
ようやっとプロジェクトの設定を変更するときが来ました。
長かったです。
……もっと短くまとめられるはずなんですけどねー。
Microsoftのエバンジェリストの方々はすごいです。
ACSの設定忘れ
ACSの設定で、1か所設定しなければいけない部分を設定し忘れていました。
それは、「規則グループ」です。
これを設定していないと、ACSはIDプロバイダから何を取得していいのかわからず、エラーになります。
まずはACSの設定ポータルで「規則グループ」を選び、自動で作成されている規則を選択します。
![acstest32 acstest32](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIIhTBWspjzetfSPzQLZMPKavNtJCXttarSNE39ru9ED_mG1kjkAj05ZaBOVcbRubbLBF7OjLDi0kEAbqXa36sqx1fpmJtkLA4Gui4bZcQqzT3kZfuZf3_yjuZA48-IttHr6g00EsidJ_J/?imgmax=800)
選択したら、「ルール」の欄に「生成」のリンクがありますので選択します。
![acstest33 acstest33](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEippZgiYEOnoC7ngWFF1tXpxC3NoemdSDRIJqqxhSxIYHN1YB1muzzbIlEkctOAAvPFZgODmdYpIGW4D6eehUGb221Nd9o_SJoM9kGKjXLrPa9QiogU0BwP6FnE08dQqkVPIkkepLwOn10s/?imgmax=800)
デフォルトの状態で規則を生成してくれますので、全てのIDプロバイダにチェックが入っている状態で「生成」をクリックします。
![acstest34 acstest34](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP0ir3-hofSynLyhnd_5BPnlR6P7_zLdSgwW9_wldLIQYKK7DIIIHyAEeqviaq3uBmU-PcCMxnlfv0z540JarixwQT4PEqsTdWcw2wH6QtKgQsdyI83EEjkzMzlOb2SuU-KtOoRr9OrBy-/?imgmax=800)
最後に、真ん中近辺にある「保存」のボタンをクリックして生成した規則を保存しておきます。
これでIDプロバイダから指定のClaimが取得できるようになります。
VisualStudio2012のプロジェクトの設定
ここまででACSの設定は済んでいますよね。
基本的なところはデフォルトにしてしまったので、特に難しいものでもなかったと思います。
さて、次はそのACSを使用するようにプロジェクトを設定していくことにします。
今回は画像少な目に……。少な目に……(涙目)
ルートページの作成
何はなくとも、/ で表されるルートのページを作っておきましょう。
ASP.NET MVC4を選びましたので、コントローラーを作成していけばページは作られます。
前回作成したプロジェクト内にControllersというディレクトリがありますので、そこを右クリックし、「追加」→「コントローラー」を選択します。
![acstest20 acstest20](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTRLvFHgLBOQ2F5CDi2P3q5nOWo2fRIh05ulCSsTB8-DIWuQ-X3v7KCnD-IvyN2wKi7sFLSP92-18WIjHiDN5_6Fl412iP4wPMOHaAgsxuzTEcxWd3U9h6Wv3U-da_yvl_hVrz6u3Uvssw/?imgmax=800)
上記のようなダイアログが表示されますので、「コントローラー名」の欄にHomeControllerとなるように入力、また、テンプレートはEmpty MVC controllerを選び「追加」をクリックします。
次に、Viewsディレクトリで右クリックし、「追加」→「新規ディレクトリ」を選択します。
すると、新しいディレクトリが作成されますので、ディレクトリ名をHomeに変更します。
そして、今変更したHomeディレクトリで右クリックし、「追加」→「表示...」を選択し、ビュー名をIndexとしたファイルを作成します。
![acstest24 acstest24](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgK_g4O9qV8WHkOhmzk4b9oqvPjK0QOMUWpWJhYFqBgdL-nRQkr8aLGMUgxh3N1l9N3x7a1OoLUjlnP9BknCZjUtkKni1HgRSXyzWNi70AVx4T8CzRoyqppeME9RU7sonhBlvypB1FrqANg/?imgmax=800)
作成の際に、「レイアウトまたはマスターページを使用する」の下の欄に_Layout.cshtmlを選択するように設定しておきます。
これにより、レイアウトにテンプレート的な何かを使用することができるようになります。
(この場合、_Layout.cshtmlファイルですね)
![acstest23 acstest23](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0XzSgykP_RLghytUBBon7_NV7JqqZKvO364oXHO3rCoTR8Ax3j5ZImvFHOlNbTYGiHUf_TA_etpXE_Bt7jP120LTD3ZfIDkDh56FK-nC6SgjpuONSolCoyyizmJ1w1f6aeIt0x4mt9_4N/?imgmax=800)
これで / の設定は終わりました。
ためしにF5でデバッグをしてみると、「Index」と書いたページが表示されます。
基本的にASP.NET MVC4に関することは今回は扱いません。
ざっと手順だけ書きますので、なぜそういうことをしているのかは自分で勉強してください。
(気が向いたらまた書くかもしれませんが)
ログインページの作成
さて、やっとログインのページを作成するときが来ました。
ACSの設定からもわかるように、ログイン関係はLoginControllerの1ファイルで済ませるつもりです。
先ほどのHomeControllerと同様に、LoginControllerを作成し、次のような関数を作成します。
public class LoginController : Controller
{
public ActionResult Index()
{
string returnUrl = this.Request.Params["ReturnUrl"];
if (string.IsNullOrEmpty(returnUrl))
{
returnUrl = "/";
}
Session["login_return_url"] = returnUrl;
return View();
}
public ActionResult LoggedIn()
{
string returnUrl = (string)Session["login_return_url"];
Session.Remove("login_return_url");
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
return Redirect("/");
}
public ActionResult Error()
{
var t = this;
Session.Remove("login_return_url");
return Redirect("/");
}
}
ログイン後に表示するURLがパラメータになってわたってくるときがありますので、その対処のみ入れてあります。
次に、表示ファイルを作りますが、今度は Views\Login\Index.cshtml を以下の内容で作成します。
ここでは、実行状況を判定して、読み込むURLを変更しています。
読み込み元はACSの「アプリケーション統合」→アプリケーション名→「オプション2」に記されているURLをコピペして記載し、JSONP用の戻り関数を「setProviders」に設定したものです。
アプリケーション名で選択する際、ローカルとWebSites用と両方とも辿って記載しています。
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Login</h2>
<div>
<ul id="providers"></ul>
</div>
@section scripts {
<script type="text/javascript">
function setProviders(data) {
for (var i = 0; i < data.length; i++) {
$("#providers").append("<li><a href=\"" + data[i].LoginUrl + "\">" + data[i].Name + "</a></li>");
}
}
</script>
@if (Request.IsLocal)
{
<script type="text/javascript" src="https://acstst.accesscontrol.windows.net:443/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=http%3a%2f%2flocalhost%3a30994%2f&reply_to=http%3a%2f%2flocalhost%3a30994%2flogin%2floggedin&context=&request_id=&version=1.0&callback=setProviders"></script>
}
else
{
<script type="text/javascript" src="https://acstst.accesscontrol.windows.net:443/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=https%3a%2f%2facstest.azurewebsites.net%2f&reply_to=https%3a%2f%2facstest.azurewebsites.net%2flogin%2floggedin&context=&request_id=&version=1.0&callback=setProviders"></script>
}
}
基本のソースコードはここまでです。
WIFを生成物に含めるようにする
前々回に設定したWIF3.5の環境。
実はDLLをプロジェクトに読み込んでやらなければいけません。
一応、WIFで認証処理されますので……。
プロジェクトの「参照設定」→「参照の追加」を選択。
出てきたダイアログ内で、「アセンブリ」→「拡張子」と選び、一覧の中から「Microsoft.IdentityModel」にカーソルを合わせ、名前の左に出てきた四角をクリックします。
すると、チェックがつきますので、OKをクリックします。
ソリューションエクスプローラーに戻ってきたら、「詳細設定」内に増えている「Microsoft.IdentityModel」を右クリックし、プロパティを選びます。
プロパティ設定画面が表示されますので、「ローカルコピー」の欄をTrueに変更します。
こうすることで、Microsoft.IdentityModelが生成物の中に含められ、使用できるようになります。
(WebSitesにはデフォルトでは入っていないのです)
WIF SDKを使用してACSを使用するように設定する
すみません、今回超長いです。
まだまだやること沢山あります。
見捨てないください。
WIF SDKに入っているFedUtil.exeを使用して認証部分をACSを使用するように変更します。
FedUtil.exeは通常 C:\Program Files (x86)\Windows Identity Foundation SDK\v3.5 にあるはずです。
起動するとまずは変更するWeb.Configの位置と、領域のURLを入力します。
ここではまずローカル環境のものを設定しておきます。
入力後、Nextを押すとhttpでセキュアじゃない、と言われますが気にせず続けます。
すると、ACSのWS-FederationのURLを入力する画面に切り替わるので、ACSの設定の「アプリケーション統合」に記載されているWS-FederationメタデータのURLを入力します。
あとはアプリが終了するまでNextやFinishやらのボタンを押していけばおっけー。
Web.Configがいろいろ書き換えられますが、まず手直しするのが以下の部分。
</appSettings>
<!--
<location path="FederationMetadata">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
-->
<system.web>
<!--
<authorization>
<deny users="?" />
</authorization>
-->
<authentication mode="Forms"><forms loginUrl="~/login" timeout="2880" /></authentication>
<!--
<authentication mode="None" />
-->
<httpRuntime requestValidationMode="2.0" />
<compilation debug="true" targetFramework="4.0">
初期設定のままだと、どのページを見てもログインしていなければログインページにリダイレクトされます。
ちょっとウザいので、デフォルトリダイレクトはさせずに、ログインが必要な場合のとび先を /login になるように変更しておきます。
また、認証プロトコル的にエラーになるので、その回避(httpRuntimeの行)を行っておきます。
そして、重要な変更はだいたい以下のようにWeb.Configに追加されています。
<microsoft.identityModel>
<service>
<audienceUris>
<add value="http://localhost:30994/" />
</audienceUris>
<federatedAuthentication>
<wsFederation passiveRedirectEnabled="true" issuer="https://acstst.accesscontrol.windows.net/v2/wsfederation" realm="http://localhost:30994/" requireHttps="false" />
<cookieHandler requireSsl="false" />
</federatedAuthentication>
<applicationService>
<claimTypeRequired>
<!--Following are the claims offered by STS 'https://acstst.accesscontrol.windows.net/'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
<claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
<!--<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" optional="true" />-->
<!--<claimType type="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider" optional="true" />-->
</claimTypeRequired>
</applicationService>
<issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<trustedIssuers>
<add thumbprint="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" name="https://acstst.accesscontrol.windows.net/" />
</trustedIssuers>
</issuerNameRegistry>
<certificateValidation certificateValidationMode="None" />
</service>
</microsoft.identityModel>
ここで設定したのはローカルデバッグ環境用なので、WebSitesにデプロイする際の設定も行っておきます。
上記の部分をWeb.Configからコピーし、Web.Release.Configに貼り付けます。
そして、領域部分を書き換え、デプロイ時に反映されるように変更します。
変更後のWeb.Release.Configは以下のようになります。
<?xml version="1.0"?>
<!-- For more information on using Web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an atrribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your Web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
<microsoft.identityModel>
<service>
<audienceUris>
<add value="https://acstest.azurewebsites.net/" xdt:Transform="Replace" />
</audienceUris>
<federatedAuthentication>
<wsFederation passiveRedirectEnabled="true" issuer="https://acstst.accesscontrol.windows.net/v2/wsfederation" realm="https://acstest.azurewebsites.net/" requireHttps="true" xdt:Transform="Replace" />
<cookieHandler requireSsl="true" xdt:Transform="Replace" />
</federatedAuthentication>
<applicationService>
<claimTypeRequired>
<!--Following are the claims offered by STS 'https://acstst.accesscontrol.windows.net/'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
<claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
<!--<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" optional="true" />-->
<!--<claimType type="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider" optional="true" />-->
</claimTypeRequired>
</applicationService>
<issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<trustedIssuers>
<add thumbprint="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" name="https://acstst.accesscontrol.windows.net/" />
</trustedIssuers>
</issuerNameRegistry>
<certificateValidation certificateValidationMode="None" />
</service>
</microsoft.identityModel>
</configuration>
実際のところ、必要なのは xdt:Transform="Replace" を付加した3行分だけなんですけど。
このように設定しておくことで、環境を切り替えることができるようになっています。
WebSites対応
さて、最後の大詰めです。
現状で動かしてみると、ローカルのデバッグ環境では問題なく動くのに、デプロイしてみるとログイン直後にエラーになってしまいます。
これはなにやら、セキュリティ的なエラーが発生しているらしいです。
(System.Security.Cryptography.CryptographicException)
ここはもう、そういうもんなのです。
……というわけにもいかず。
調べてみると、代替案があることがわかります。
VS2012に戻り、ソリューションエクスプローラーの「参照設定」を右クリックし、「NuGetパッケージの管理」を選びます。
NuGetが開いたなら、「identity」で検索をし、出てきた候補の中から「Thinktecture.IdentityModel」をインストールします。
インストールが済んだら、Web.Configに以下の設定を追加します。
<microsoft.identityModel>
<service>
<audienceUris>
<add value="http://localhost:30994/" />
</audienceUris>
<federatedAuthentication>
<wsFederation passiveRedirectEnabled="true" issuer="https://acstst.accesscontrol.windows.net/v2/wsfederation" realm="http://localhost:30994/" requireHttps="false" />
<cookieHandler requireSsl="false" />
</federatedAuthentication>
<applicationService>
<claimTypeRequired>
<!--Following are the claims offered by STS 'https://acstst.accesscontrol.windows.net/'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
<claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
<!--<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" optional="true" />-->
<!--<claimType type="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider" optional="true" />-->
</claimTypeRequired>
</applicationService>
<issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<trustedIssuers>
<add thumbprint="2362F91373ED4C69F2B9CBE528100F2B33386B78" name="https://acstst.accesscontrol.windows.net/" />
</trustedIssuers>
</issuerNameRegistry>
<certificateValidation certificateValidationMode="None" />
<!-- ここから追加 -->
<securityTokenHandlers>
<remove type="Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add type="Thinktecture.IdentityModel.Web.MachineKeySessionSecurityTokenHandler, Thinktecture.IdentityModel" />
</securityTokenHandlers>
<!-- ここまで追加 -->
</service>
</microsoft.identityModel>
このあたりのことは、
Dominick BaierさんのBlogに詳細が書かれています。
……ま、まあ、難しいことはわからないんだけどねっ!
夜中でも暑いです
なんとか駆け足でざっと書いてみましたが……。
ますますもって長い!
いらん事書きすぎです。
実際、慣れてしまえば10分かからない作業なのに……。
とりあえず、ここまでの変更でWindows8 + VisualStudio2012でWebSitesでも無事にACSを使ったサイトを作れるようになりました。
本当はこんなに苦労しなくても、Azureが.Net4.5環境に移行してくれればIdentity and Access toolsだけでかたが付くんですけど。ふぅ。