by WebSurfer
2018年8月19日 14:10
IIS Express でも Visual Studio でプロジェクトのプロパティを設定することで Windows 認証が使えるという話を書きます。
上の画像は Visual Studio Community 2015 のテンプレートで[空]の Web Forms アプリケーションプロジェクトを作り、「ソリューションエクスプローラー」ウィンドウでプロジェクトを選択し、その「プロパティ」ウィンドウで[Windows 認証]の設定をデフォルトから変えて[有効]にしたところです。
上の画像では[匿名認証]の設定もデフォルトから変えて[無効]に設定していますが、そうしておかないと、匿名のままアクセスできてしまう(Windows 認証はスルーされてしまう)ので注意してください。
上記のように設定してから、Visual Studio で[デバッグ(D)]⇒[デバッグなしで開始(H)」(または[デバッグの開始(S)])をクリックすると Web アプリは IIS Express 上で実行され、以下の画像のように認証情報を入力するダイアログが表示されます。(2020/11/26 追記: いつの間のかこのダイアログは出なくなっていました。詳しくは下の追記を見てください)
そのダイアログに有効な Windows アカウント名とパスワードを入力して[OK]をクリックすれば認証は通って画面が表示されます。User.Identity.Name でアカウント名も取得できます。(そのあたりの動作は IIS を使った時のものと同じ)
実は自分は IIS Express で Windows 認証が使えることを知らなくて、わざわざローカル IIS を使って Windows 認証のテストをしてました。(汗)
MSDN Forum のスレッド「Request.ServerVariables("REMOTE_USER")が空の文字列を返す」の Q&A の際に調べて初めて知った次第です。
なお、Windows 認証は Windows の機能に依存するもので、IE と IIS の組み合わせでなければ実現できないそうですので注意してください。(他の組み合わせでできたとしてもたまたまで、Microsoft が保証しているわけではなさそうです)
----- 2020/11/26 追記 -----
いつの間にか認証ダイアログが出ないまま Windows 認証が通るようになっていました。下の Fiddler での要求・応答のキャプチャ画像を見てください。
シングルサインオンで認証済みのユーザーのアクセスしてくるとかでない限り、最初の要求で応答が返ってきた時(上の画像の #1 が該当)に認証ダイアログが出るはずなのですが、なぜか出ないまま自動的に認証が進んでログインできてしまいます。
Page.User.Identity で取得できる認証関係の情報は、以下の画像の通り Windows 認証が通ったときものになっています。
OS は同じ Windows 10 Pro 64-bit で、ブラウザも同じ IE11、Visual Studio のバージョンも同じ 2015 で試したのですが・・・ 違いは OS のバージョンが 20H2 にアップデートされたぐらいしか思い当たりません。それで何か変わったのでしょうか。
上記は IIS Express で ASP.NET アプリを動かして試したときの話です。IIS ではどうなのかは未確認です。
by WebSurfer
2015年6月8日 15:47
先の記事 では、基本認証の場合、IIS で資格情報がキャッシュされるという話を書きました。
この記事では、統合 Windows 認証においてもそれと似た話があって、User.Identity.Name で取得されるログインユーザーの Windows アカウント名が Web サーバーのキャッシュから取得されるという話を書きます。
Windows 認証が有効になっていて、かつ、現在ページ要求を行っているユーザーがログイン済みの場合、User.Identity.Name で取得されるのは DOMAIN\USERNAME の形式で表されるドメインユーザー名になります。(詳しくは先の記事 ASP.NET の ID オブジェクト を見てください)
ところが、Active Directory ドメインコントローラーでユーザー名を変更しても、User.Identity.Name で取得されるログインユーザー名に変更が反映されません(古いままになります)。
その具体例は stackoverflow の記事 IIS Returning Old User Names to my application に出ていますので、興味がありましたらそれをみて下さい。(手抜きですみません)
その理由は、Microsoft サポートの記事 KB946358 に書かれていますように、Web サーバーにユーザー名がキャッシュされ、キャッシュに情報が存在する場合はドメインコントローラーに照会せず、キャッシュされたユーザー名の情報を返すからです。
対応策は、
-
ドメインユーザー名には将来にわたって変更する必要がないもの(例:社員番号)を使用する、または、
-
KB946358 に従って Web サーバーのレジストリを書き換えてキャッシュを無効にする。
といったところでしょうか。
ただ、キャッシュするのはドメインコントローラーの負荷とネットワークトラフィックの削減のためだそうですので、キャッシュを無効にするとパフォーマンスに影響がありそうというところが気がかりではありますが・・・
by WebSurfer
2011年2月5日 16:48
ASP.NET の承認は、"ユーザー" に加えて "ロール" というレベルでの管理をサポートしています。ロール管理では、ユーザーをロールに割り当てることにより、ユーザーのグループを単位として扱うことができます。 詳しくは MSDN ライブラリの ASP.NET のロールによる承認の管理 が参考になると思います。
Forms 認証の場合は、サイト管理者がロールを定義し、ロール情報は SQL Server などのデータベースに格納します。
一方、Windows 認証の場合は Windows アカウントを利用しますので、ロールはそのユーザーが属するグループになります。従って、ロールを定義するのはサーバーの管理者で、ロール情報は Active Directory ドメインコントローラーに格納されるということになります。
当然、Windows 認証の場合もロールによるアクセス制限は可能です。例えば、あるフォルダにロールによるアクセス制限をしておくと、そのロールに属さないユーザーがフォルダ内のページを要求すると下の画像のようなダイアログが出てきます。
ロールによるアクセス制限は、Forms 認証と同様に web.config で定義します。フォルダ直下の web.config に以下のような定義をしておくと、Domain Users グループに属するユーザー以外のアクセスは拒否されます。
<configuration>
<system.web>
<authorization>
<allow roles="<ドメイン名>\Domain Users" />
<deny users="*" />
</authorization>
</system.web>
</configuration>
なお、Windows 認証の場合、web.config でロールを有効にする(web.config で <roleManager enabled="true" /> とする)のは意味がなさそうです。というより、そういう設定はしてはいけないようです。
web.config でロールを有効にすると、Page.User は System.Security.Principal.IPrincipal ではなく、それから派生した System.Web.Security.RolePrincipal になります。そこで WindowsPrincipal オブジェクトを取得するため (WindowsPrincipal)User とすると、"型 'System.Web.Security.RolePrincipal' のオブジェクトを型 'System.Security.Principal.WindowsPrincipal' にキャストできません。" というエラーになりますので。
ここからは先は余談です。
Windows アカウントでは、ユーザーもグループもセキュリティ識別子 (SID) を認識に用いています。よく知られた SID は MSDN ライブラリの Windows サーバー オペレーティング システムの既知のセキュリティ識別子 に一覧がありますので見てください。
SID は S-1-1-0 のように人間にとっては意味不明の文字列ですが、これを Everyone のように ACL エディタで使われている名前に変換する方法を忘れないように書いておきます。
Everyone のような名前は NTAccount オブジェクトから取得できます。NTAccount オブジェクトは IdentityReference.Translate メソッドで取得できます。その例を以下のコードに示します。これを実行すると、上の画像のように表示されます。
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Security.Principal" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
WindowsPrincipal principal = (WindowsPrincipal)User;
// IsInRole("S-1-2-35-545") ではダメ。
if (principal.IsInRole("BUILTIN\\Users"))
{
Label1.Text = "BUILTIN\\Users";
}
WindowsIdentity identity =
(WindowsIdentity)principal.Identity;
SecurityIdentifier sid = identity.Owner;
StringBuilder sb = new StringBuilder();
NTAccount nt = (NTAccount)sid.Translate(typeof(NTAccount));
sb.Append("ユーザーの SID: " + nt.Value +
" (" + sid.ToString() + ")<br /><br />");
sb.Append("ユーザーが属するグループ<br />");
IdentityReferenceCollection irc = identity.Groups;
foreach (IdentityReference ir in irc)
{
NTAccount ntAccount =
(NTAccount)ir.Translate(typeof(NTAccount));
sb.Append(" " + ntAccount.Value +
" (" + ir.Value + ")<br />");
}
Label2.Text = sb.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
ユーザ: <asp:LoginName ID="LoginName1" runat="server" />
<br />
ロール: <asp:Label ID="Label1" runat="server" />
<hr />
<h1>Windows Users のページ</h1>
<asp:Label ID="Label2" runat="server" />
<hr />
<asp:HyperLink ID="HyperLink1"
runat="server"
NavigateUrl="~/Default.aspx">
入り口に戻る
</asp:HyperLink>
</div>
</form>
</body>
</html>