由于基于角色的[Authorize]属性,ASP.NET MVC 5 Azure网站无法在某些视图上连接到SQL Azure DB

Smi*_*.09 5 asp.net-mvc connection-string web-config azure azure-sql-database

我们有一个ASP.NET MVC 5网站,当我们使用Visual Studio在本地运行它时,它可以正常工作。在本地运行时,网站连接到两个SQL Express数据库(AspNetUsers和我们的内容数据库)。我们使用实体框架连接到内容数据库。

我们将网站部署到Azure,并将2个数据库迁移到SQL-Azure数据库。

这是web.config中的连接字符串

<connectionStrings>    
<add name="AspNetUsersDatabase" connectionString="Data Source=tcp:ZZZ.database.windows.net,
1433;Initial Catalog=ZZZ;User ID=ZZZ@ZZZ;Password=ZZZ;" 
providerName="System.Data.SqlClient" />

<add name="Group5DatabaseConnectionString" connectionString="Data Source=YYY.database.windows.net,
1433;Initial Catalog=YYY;User ID=YYY@YYY;Password=YYY;" 
providerName="System.Data.SqlClient" />

<add name="Group5DatabaseEntities" connectionString="metadata=res://*/Models.Group5Model.csdl|
res://*/Models.Group5Model.ssdl|res://*/Models.Group5Model.msl;provider=System.Data.SqlClient;
provider connection string=&quot;data source=XXX.database.windows.net,1433;
InitialCatalog=XXX;UserID=XXX@XXX;Password=XXX;MultipleActiveResultSets=True;
App=EntityFramework&quot;"providerName="System.Data.EntityClient" />
</connectionStrings>
Run Code Online (Sandbox Code Playgroud)

似乎这些链接正确链接到了网站,因为此答案中所述的web.config中的连接字符串与Azure中的连接字符串正确匹配,并且网站上的大多数视图都可以正常工作。

但是,当尝试打开两个视图时,出现以下错误:

“ /”应用程序中的服务器错误。

建立与SQL Server的连接时发生与网络相关或特定于实例的错误。服务器未找到或无法访问。验证实例名称正确,并且已将SQL Server配置为允许远程连接。(提供者:SQL网络接口,错误:26-指定服务器/实例时出错)

说明:执行当前Web请求期间发生未处理的异常。
请查看堆栈跟踪,以获取有关错误及其在代码中起源的更多信息。

SQLExpress数据库文件自动创建错误:

连接字符串使用应用程序的App_Data目录中的数据库位置指定本地Sql Server Express实例。

提供者尝试自动创建应用程序服务数据库,因为提供者确定该数据库不存在。要成功检查应用程序服务数据库的存在并自动创建应用程序服务数据库,必须满足以下配置要求:

如果应用程序在Windows 7或Windows Server 2008R2上运行,则必须执行特殊的配置步骤才能启用提供程序数据库的自动创建。有关其他信息,请访问:http : //go.microsoft.com/fwlink/?LinkId=160102。如果应用程序的App_Data目录不存在,则Web服务器帐户必须具有对该应用程序目录的读写权限。这是必需的,因为如果Web服务器帐户尚不存在,它将自动创建App_Data目录。

如果应用程序的App_Data目录已经存在,则Web服务器帐户仅需要对应用程序的App_Data目录的读写权限。这是必需的,因为Web服务器帐户将尝试验证应用程序的App_Data目录中是否已存在Sql Server Express数据库。从Web服务器帐户撤消对App_Data目录的读取访问权限,将阻止提供程序正确确定Sql Server Express数据库是否已存在。当提供程序尝试创建已经存在的数据库的副本时,这将导致错误。由于创建新数据库时会使用Web服务器帐户的凭据,因此需要写访问权限。

必须在计算机上安装Sql Server Express。Web服务器帐户的进程标识必须具有本地用户配置文件。有关如何为计算机帐户和域帐户创建本地用户配置文件的详细信息,请参见自述文档。

似乎以下行在错误消息中最重要:

连接字符串使用应用程序的App_Data目录中的数据库位置指定本地Sql Server Express实例。

无效的两个视图都实例化,AccountController并在其中使用以下方法:

[Authorize]
public class AccountController : Controller
{
    // ...
    public string FindUserArea(string id)
    {
        int locationId = FindUserLocationId(id);                
        Location location = db.Locations.FirstOrDefault(p => p.LocationID == locationId);

        return (string) (location.Suburb.SuburbName + " > "
            + location.Suburb.Region.RegionName + " > "
            + location.Suburb.Region.Province.ProvinceName);
    }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

我们首先怀疑中存在错误AccountController,但另一个也使用上述方法的视图显示正常。然后,我们认为仍然存在与以下问题相关的错误的可能性InitializeSimpleMembership(我们不确定的含义/效果是100%SimpleMembership)。我们遵循了此问题的指导,但是InitializeSimpleMembership根据本文中的建议成功添加并不能解决该错误。

同样,对于两个用户之一,在注册时会出现相同的错误,但是AspNetUserAzure DB仍会更新,并且一旦刷新了新用户的网站登录信息便可以了。

Smi*_*.09 0

在系统地查明错误的根源后,我们发现问题源于基于角色的属性,与此堆栈[Authorize]中的问题完全相同。

\n\n

User.IsInRole()显然,当连接到 SQL-Azure 时,幕后存在一些问题。虽然这通常可以通过启用 Simple Membership -\xe2\x80\x94 来解决,但在我们的例子中,我们无法轻松做到这一点,因为 Simple Membership 要求 IdAspNetUser是 int 而不是字符串(这似乎实现起来相当棘手)与我们使用的身份模型一起使用,因为 Id 本质上需要是一个字符串)。

\n\n

最初我们没有注意到 SimpleMembership 的问题,因为下面的行在 App_Start 中没有足够早地触发,并且我们只有在将其放入全局后才意识到存在问题。

\n\n
WebSecurity.InitializeDatabaseFile("MySecurity.sdf", "Users", "UserID", "Username", true);\n
Run Code Online (Sandbox Code Playgroud)\n\n

所以我们最终也编写了自己的授权方法,如下所示:

\n\n
if (User.Identity.GetUserId() == null)\n{\n    return RedirectToAction("Login", "Account");\n}\nelse\n{\n    var c = new AccountController();\n    string UserRole = c.FindUserRoleById(User.Identity.GetUserId().ToString());\n\n    if (UserRole != "DesiredUserRole")\n    {\n        return RedirectToAction("Login", "Account");\n    }\n    else\n    {\n        // Desired User stuff\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

其中调用FindUserRoleById

\n\n
public string FindUserRoleById(string id)\n{\n    var user = UserManager.FindById(id);\n    var userroles = user.Roles;\n    string userrole = "";\n\n    foreach (var x in userroles)\n    {\n        if (userrole != "Admin")\n        {\n            if (x.Role.Name.ToString() == "Admin")\n            {\n                userrole = "Admin";\n            }\n            else if (x.Role.Name.ToString() == "User2")\n            {\n                userrole = "User2";\n            }\n            else if (x.Role.Name.ToString() == "User3")\n            {\n                userrole = "User3";\n            }\n        }\n    }\n    return userrole;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

显然,这不像[Authorize]属性的简单角色检查那么巧妙,但它在这种情况下达到了目的并且工作得很好。

\n\n

如果有人有更好的解决方案,我仍然会感兴趣。

\n