我有一个使用OWIN承载令牌授权的WebApi应用程序。这是使用OWIN TestServer进行单元测试的。测试非常简单:它向服务器创建10个同时的授权请求,并验证每个请求是否收到响应。
测试在我的开发计算机上正常运行但在Amazon Web Services上失败并且跟踪堆栈跟踪:
MESSAGE:
System.AggregateException : One or more errors occurred.
----> System.Security.Cryptography.CryptographicException : Access is denied.
+++++++++++++++++++
STACK TRACE:
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at MyTestMethod()
--CryptographicException
at System.Security.Cryptography.ProtectedData.Protect(Byte[] userData, Byte[] optionalEntropy, DataProtectionScope scope)
at System.Security.Cryptography.DpapiDataProtector.ProviderProtect(Byte[] userData)
at Microsoft.Owin.Security.DataHandler.SecureDataFormat`1.Protect(TData data)
at Microsoft.Owin.Security.OAuth.OAuthAuthorizationServerHandler.<InvokeTokenEndpointAsync>d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Owin.Security.OAuth.OAuthAuthorizationServerHandler.<InvokeAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Owin.Security.Infrastructure.AuthenticationMiddleware`1.<Invoke>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Owin.Testing.OwinClientHandler.<>c__DisplayClass1.<<SendAsync>b__0>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Microsoft.Owin.Testing.OwinClientHandler.<SendAsync>d__6.MoveNext()
Google搜索此问题并未提供完全匹配。
测试方法:
public void MyTestMethod()
{
const int loginNumber = 10;
List<Tuple<string, string>> userList = new List<Tuple<string, string>>();
for (int i = 0; i < loginNumber; ++i)
userList.Add(new Tuple<string, string>("SomeName", "SomePassword"));
using (var server = TestServer.Create<Startup>())
{
var loginTasks = userList.Select(item => server.CreateRequest("/token").And(x => x.Content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("username", item.Item1),
new KeyValuePair<string, string>("password", item.Item2),
new KeyValuePair<string, string>("grant_type", "password")
})).PostAsync());
HttpResponseMessage[] loginTasksResults = Task.WhenAll(loginTasks).Result; // AggregatedException here
// ...
}
}
启动课程
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
NinjectWebCommon.Start();
config.DependencyResolver = new NinjectDependencyResolver(NinjectWebCommon.bootstrapper.Kernel);
app.UseWebApi(config);
app.MapSignalR();
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new MyAuthorizationProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
授权提供商
public class MyAuthorizationProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (context.UserName == null || context.Password == null)
{
context.SetError("invalid_grant", "The user name or password is empty.");
return;
}
string authenticationId = Guid.NewGuid().ToString("n");
ClaimsIdentity oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
oAuthIdentity.AddClaim(new Claim("sub", context.UserName));
oAuthIdentity.AddClaim(new Claim("role", "user"));
oAuthIdentity.AddClaim(new Claim("auth_id", authenticationId));
context.Validated(oAuthIdentity);
}
}