在我们的后端,我们有几个需要通信的服务。到目前为止,他们还没有获得任何形式的授权。所以任何人都可以与任何服务进行通信。现在我们想为所有服务引入授权。因此,我们编写了以下
Authorize
-属性:
public class BackendServiceAuthorizationAttribute : AuthorizeAttribute
{
private static readonly Lazy<UserAuthorizationProvider> lazyUserAuthorizationProvider =
new Lazy<UserAuthorizationProvider>(() => new UserAuthorizationProvider(), true);
private static UserAuthorizationProvider authorizationProvider => lazyUserAuthorizationProvider.Value;
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext == null)
throw new ArgumentNullException(nameof(actionContext));
if (!IsAuthorized(actionContext))
{
EventLog.WriteEntry($"Backend-System", "Unauthorized request", EventLogEntryType.Warning);
HandleUnauthorizedRequest(actionContext);
}
else
{
EventLog.WriteEntry($"Backend-System", "Authorized request", EventLogEntryType.Warning);
}
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if (actionContext == null)
throw new ArgumentNullException(nameof(actionContext));
return IsUserInGroup(actionContext) || IsUserAuthorized(actionContext);
}
private bool IsUserAuthorized(HttpActionContext actionContext)
{
bool isUserAuthenticated = false;
try
{
string currentUserId = DoClean(actionContext.RequestContext.Principal.Identity.Name);
isUserAuthenticated = authorizationProvider.IsUserAuthenticated(currentUserId);
}
catch (Exception e)
{
EventLog.WriteEntry($"Backend-System", $"Exception in IsUserAuthorized: {e}\nRequest-Principal: {actionContext.RequestContext.Principal}\nRequest: {actionContext.Request}");
}
return isUserAuthenticated;
}
private const string SoftwareAdmins = "Backend-Software-Administratoren";
private const string ServerAdmins = "Backend-Server-Administratoren";
private bool IsUserInGroup(HttpActionContext actionContext)
{
bool isInRole = false;
try
{
isInRole = actionContext.ControllerContext.RequestContext.Principal != null && (
actionContext.ControllerContext.RequestContext.Principal.IsInRole(SoftwareAdmins) ||
actionContext.ControllerContext.RequestContext.Principal.IsInRole(ServerAdmins));
}
catch (Exception e)
{
EventLog.WriteEntry($"Backend-System", $"Exception in IsUserInGroup: {e}");
}
return isInRole;
}
private static string DoClean(string userName)
{
string retval = userName;
if (retval.Contains("\\"))
{
string[] retsplits = retval.Split('\\');
retval = retsplits[retsplits.Length - 1];
}
if (retval.Contains("@"))
{
string[] retsplits = retval.Split('@');
retval = retsplits[0];
}
return retval.ToUpper();
}
}
UserAuthorizationProvider
只是一个允许的用户 ID 初始化一次的类。
在每台服务器上运行服务的用户不是 SoftwareAdmins 或 ServerAdmins 的成员。所以我们点击 IsUserAuthorized-Method。
在每个服务器上的 Windows 事件日志中,我看到 IsUserAuthorized 抛出异常,然后我看到来自
OnAuthorization
的条目,其中写着:Unauthorized request
。事件日志中的下一个条目是 Authorized request
。
所以我假设,一个请求会多次调用 Authorize-Attribute。
我想知道是否可以避免这种异常?
在异常消息中,我可以看到
actionContext.RequestContext.Principal
为空,因此对 actionContext.RequestContext.Principal.Identity
的调用会引发异常。在第二个请求中必须填写。
所有服务都提供使用 OWIN 框架托管的自托管 REST-API。服务的配置如下所示:
public void Configuration(IAppBuilder appBuilder)
{
HttpListener listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication | AuthenticationSchemes.Anonymous;
HttpConfiguration config = new HttpConfiguration();
config.DependencyResolver = controllerDependencyResolver;
config.MapHttpAttributeRoutes();
config.EnsureInitialized();
appBuilder.UseWebApi(config);
}
使用 REST-API 的
HttpClient
被包装在名为“RestClient”的类中。要创建 RestClient
的实例,我们使用以下代码:
private RestClient GetRestClient()
{
RestClientSettings restClientSettings = RestClientSettings.CreateDefault();
restClientSettings.UseDefaultCredentials = false;
ICredential cred = CredentialProvider.GetCredential(CredentialType.BackendSystem);
restClientSettings.Credentials = new NetworkCredential(cred.GetUserName(), cred.GetPwdSec(), "our_domain");
RestClient client = new RestClient($"{targetUrl}", restClientSettings);
return client;
}
所以我的问题是,为什么第一次尝试授权失败并且
actionContext.RequestContext.Principal
为空?
我自己解决了。我必须提供
AuthenticationSchemes.Negotiate