在"移动应用程序(Xamarin.Forms)" + .NET Standard + MobileAppService解决方案,我尝试使用http://blog.isharepoint.co.uk/?p=83中的代码。 await SignInManager.PasswordSignInAsync
方法调用无限期挂起。我使用Postman调用Auth控制器,可以确认收到的电子邮件和密码是否正常。一旦发生挂起,我无法调试以找到错误。在添加自定义身份验证代码之前,该项目工作正常。最后,我在本地系统和Azure Web服务器上都获得了相同的行为。
这是AuthController.cs文件(参见IsPasswordValid方法):
namespace MyAzure.MobileAppService.Controllers
{
public class AuthController : ApiController
{
private ApplicationUserManager _userManager;
private ApplicationSignInManager _signInManager;
private string signingKey, audience, issuer;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.Current.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public AuthController()
{ }
public AuthController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
UserManager = userManager;
SignInManager = signInManager;
}
public IHttpActionResult Post([FromBody] JObject assertion)
{
bool passValid = IsPasswordValid(assertion).Result;
if (passValid)
{
if (Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY") == null)
{
signingKey = ConfigurationManager.AppSettings["SigningKey"];
audience = ConfigurationManager.AppSettings["ValidAudience"];
issuer = ConfigurationManager.AppSettings["ValidIssuer"];
}
else
{
signingKey = Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY");
var azureAppServiceEndpoint = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME");
audience = $"https://{azureAppServiceEndpoint}/";
issuer = $"https://{azureAppServiceEndpoint}/";
}
string username = assertion.GetValue("email").Value<string>();
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, username) },
signingKey,
audience,
issuer,
TimeSpan.FromHours(24));
return Ok(new LoginResult()
{
AuthenticationToken = token.RawData,
User = new LoginResultUser() { UserId = username }
});
}
else // user assertion was not valid
{
return Content(HttpStatusCode.Unauthorized, "Wrong credentials");
}
}
private async Task<bool> IsPasswordValid(JObject assertion)
{
// this is where we would do checks agains a database
try
{
string username = assertion.GetValue("email").ToString();
string password = assertion.GetValue("password").ToString();
var signInStatus = await SignInManager.PasswordSignInAsync(username, password, true, shouldLockout: true);
switch (signInStatus)
{
case SignInStatus.Success:
return true;
case SignInStatus.LockedOut:
return false;
case SignInStatus.RequiresVerification:
return false;
case SignInStatus.Failure:
default:
return false;
}
}
catch (Exception ex)
{
return false;
}
}
}
}
这是ApplicationDbContext.cs
using System.Data.Entity;
using Microsoft.AspNet.Identity.EntityFramework;
using MyAzure.MobileAppService.DataObjects;
using System.Data.Entity.ModelConfiguration.Conventions;
using Microsoft.Azure.Mobile.Server.Tables;
using System.Linq;
namespace MyAzure.MobileAppService.Models
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
private const string connectionStringName = "Name=MS_TableConnectionString";
public DbSet<Item> Items { get; set; }
public ApplicationDbContext()
: base(connectionStringName, throwIfV1Schema: false)
{
Configuration.ProxyCreationEnabled = false;
Configuration.LazyLoadingEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
"ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}
ApplicationUser.cs
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Security.Claims;
using System.Threading.Tasks;
namespace MyAzure.MobileAppService.Models
{
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
}
我在Debug控制台输出中看到的最后一个条目是:
&#39; iisexpress.exe&#39; (CLR v4.0.30319: / LM / W3SVC / 2 / ROOT-1-131689041142085279):已加载 &#39; C:\ Windows \ Microsoft.Net \组件\ GAC_32 \ System.Data.OracleClient的\ v4.0_4.0.0.0__b77a5c561934e089 \ System.Data.OracleClient.dll&#39 ;. 跳过加载符号。模块已优化并具有调试器选项 &#39; Just My Code&#39;已启用。
&#39; iisexpress.exe&#39; (CLR v4.0.30319: / LM / W3SVC / 2 / ROOT-1-131689041142085279):已加载 &#39; EntityFrameworkDynamicProxies-MyAzure.MobileAppService&#39;
在日志中提到System.Data.OracleClient非常奇怪。我在项目中找不到引用它的任何地方。
答案 0 :(得分:1)
混合使用.Result
或.Wait()
等异步和阻止调用可能会导致死锁。
即
...IsPasswordValid(assertion).Result;
使控制器操作异步,然后等待必要的方法调用。
public async Task<IHttpActionResult> Post([FromBody] JObject assertion) {
bool passValid = await IsPasswordValid(assertion);
//...code removed for brevity
}