認証なしで完全に正常に動作する WCF データ サービスを作成しました。次に、以下のようにアプリケーションに HttpModule を追加して、認証コードを追加したいと思いました。
public class BasicIdentity : IIdentity
{
string _name;
public BasicIdentity(string name)
{
this._name = name;
}
string IIdentity.AuthenticationType
{
get { return "Custom SCHEME"; }
}
bool IIdentity.IsAuthenticated
{
get { return true; }
}
string IIdentity.Name
{
get { return _name; }
}
}
public class BasicPrincipal : IPrincipal
{
string[] _roles;
IIdentity _identity;
public BasicPrincipal(string name, params string[] roles)
{
this._roles = roles;
this._identity = new CustomIdentity(name);
}
public IIdentity Identity
{
get { return _identity; }
}
public bool IsInRole(string role)
{
return _roles.Contains(role);
}
}
public class BasicAuthenticationProvider
{
public static bool Authenticate(HttpContext context)
{
//if (!HttpContext.Current.Request.IsSecureConnection)
// return false;
if (!HttpContext.Current.Request.Headers.AllKeys.Contains("Authorization"))
return false;
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
IPrincipal principal;
if (TryGetPrincipal(authHeader, out principal))
{
HttpContext.Current.User = principal;
return true;
}
return false;
}
private static bool TryGetPrincipal(string authHeader, out IPrincipal principal)
{
//
// WARNING:
// our naive – easily mislead authentication scheme
// blindly trusts the caller.
// a header that looks like this:
// ADMIN username
// will result in someone being authenticated as an
// administrator with an identity of ‘username’
// i.e. not exactly secure!!!
//
//var protocolParts = authHeader.Split(' ');
//if (protocolParts.Length != 2)
//{
// principal = null;
// return false;
//}
//else if (protocolParts[0] == "ADMIN")
//{
// principal = new CustomPrincipal(
// protocolParts[1],
// "Administrator", "User"
// );
// return true;
//}
//else if (protocolParts[0] == "USER")
//{
// principal = new CustomPrincipal(
// protocolParts[1],
// "User"
// );
// return true;
//}
//else
//{
// principal = null;
// return false;
//}
var creds = ParseAuthHeader(authHeader);
if (creds != null && TryGetPrincipal(creds, out principal))
return true;
principal = null;
return false;
}
private static string[] ParseAuthHeader(string authHeader)
{
// Check this is a Basic Auth header
if (
authHeader == null ||
authHeader.Length == 0 ||
!authHeader.StartsWith("Basic")
) return null;
// Pull out the Credentials with are seperated by ':' and Base64 encoded
string base64Credentials = authHeader.Substring(6);
string[] credentials = Encoding.ASCII.GetString(
Convert.FromBase64String(base64Credentials)
).Split(new char[] { ':' });
if (credentials.Length != 2 ||
string.IsNullOrEmpty(credentials[0]) ||
string.IsNullOrEmpty(credentials[0])
) return null;
// Okay this is the credentials
return credentials;
}
private static bool TryGetPrincipal(string[] creds, out IPrincipal principal)
{
if (creds[0] == "Administrator" && creds[1] == "SecurePassword")
{
principal = new GenericPrincipal(
new GenericIdentity("Administrator"),
new string[] { "Administrator", "User" }
);
return true;
}
else if (creds[0] == "JoeBlogs" && creds[1] == "Password")
{
principal = new GenericPrincipal(
new GenericIdentity("JoeBlogs"),
new string[] { "User" }
);
return true;
}
else
{
principal = null;
return false;
}
}
}
public class BasicAuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest
+= new EventHandler(context_AuthenticateRequest);
}
void context_AuthenticateRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
if (!BasicAuthenticationProvider.Authenticate(application.Context))
{
application.Context.Response.Status = "401 Unauthorized";
application.Context.Response.StatusCode = 401;
application.Context.Response.AddHeader("WWW-Authenticate", "Basic");
application.CompleteRequest();
}
}
public void Dispose() { }
}
ここで、BasicAuthenticationModule は web.config に次のように登録されます。
<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</assemblies>
</compilation>
<httpModules>
<add name="BasicAuthenticationModule"
type="ODataEmptyWebApp.BasicAuthenticationModule"/>
</httpModules>
</system.web>
ブラウザで WCF Data Service を実行すると、認証ウィンドウが表示されます。無効な資格情報を入力すると、正しい資格情報を要求し続けます。正しい資格情報、つまり Administrator/SecuredPassword を入力すると、ウィンドウが閉じて空のページが表示されます。ページのソース コードは次のとおりです。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv="Content-Type"
content="text/html; charset=windows-1252"></HEAD>
<BODY></BODY></HTML>
コードをデバッグすると、HttpModule を追加する前に呼び出されていた WCF Data Service の Initialize メソッドが呼び出されないことがわかりました。
コントロールが Initialize メソッドに渡されない理由がわかりません。実行するには明示的に何かを追加する必要がありますか??