User.IsInRole failing after migrating SimpleMembership database to Identity 2

不想你离开。 提交于 2019-12-22 13:31:39

问题


I successfully updated my application from MVC4-EF5 to MVC5-EF6.

I then migrated my database from Simplemebership to Identity 2.0.

I can login but User.IsInRole always returns false. All the proper tables AspNetUsers, AspNetRoles, AspNetUserRoles, AspNetLogins and AspNetClaims have been created, the spelling and type of all columns are correct and they have been populated with the data from the SimpleMembership tables (which have been deleted).

I have a user "MyUser" which is Id = 2 in the AspNetUsers table and a role "XYZ" which is Id = 2 in the AspNetRoles table and User 2 is mapped to Role 2 in the AspNetUserRoles table.

When logged in as "MyUser"

var user = _db.Users.SingleOrDefault(u => u.UserName == User.Identity.Name);
user does get set to "MyUser" but

User.IsInRole("XYZ") returns false.

I added a test variable, var testrole = Roles.GetRolesForUser();, when testrole is examined in debug it returns an empty string array {string[0]}. Running User.IsInRole("XYZ") from the Immediate Window returns false.

I've read through every document I can find on Identity, Identity 2.0 and migrating from SimpleMembership and Identity 1.0 and I can't find any implementation requirements (beyond what I've done in my posted code) that I have missed to make Identity work (and it is working at some level since then login works).

I have used Identity 2.0's extensibility hook to implement INT for my Primary keys.

WebSecConfig.cs: (called from Global.asax.cs) (removed this code for UPDATE: 1)

using WebMatrix.WebData;

namespace MyApplication
{ 
  public static class WebSecConfig
  {
    public static void RegisterWebSec()
    {
      WebSecurity.InitializeDatabaseConnection 
        ("MyApplication", "AspNetUsers", "Id", "UserName", autoCreateTables: true);
    }
  }
}

ApplicationUser:

  public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
  { 
    [StringLength(15)]
    public new string UserName { get; set; }
    public int AcId { get; set; }
    public int LcId { get; set; }
  }

  public class CustomRole : IdentityRole<int, CustomUserRole>
  {
    public CustomRole() { }
    public CustomRole(string name) { Name = name; }
  }

  public class CustomUserRole : IdentityUserRole<int> { }
  public class CustomUserClaim : IdentityUserClaim<int> { }
  public class CustomUserLogin : IdentityUserLogin<int> { }

IdentityDbContext:

  public class MyDb : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>
  {
    public MyDb() : base("MyApplication") { }

    // public virtual DbSet<UserProfiles> Users { get; set; }
    public virtual DbSet<MyTable> MyTables { get; set; } // properties marked virtual for Mocking override
    ...

  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    base.OnModelCreating(modelBuilder);
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
  }

Account Controller:

  [RequireHttps]
  [Authorize]
  public class AccountController : Controller
  {
    private readonly IUserService _userService;

    public UserManager<ApplicationUser, int> UserManager { get; private set; }

    public AccountController() 
    : this(new UserService(), new UserManager<ApplicationUser, int>(new UserStore<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>new MyDb()))) { } 

    public AccountController(IUserService userService, UserManager<ApplicationUser, int> userManager)
    { _userService = userService; UserManager = userManager; }

    // GET: /Account/Login
    [AllowAnonymous]
    public ActionResult Login() { return View(); }

    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Login(LoginVm vM)
    { 
      if (ModelState.IsValid) 
      { 
        var user = UserManager.Find(vM.UserName, vM.Password);
        if (user != null)
        { 
          FormsAuthentication.SetAuthCookie(user.UserName, false);
          return RedirectToAction("UserRouting", "Home");
        }
      } 
      ModelState.AddModelError("", "The user name or password provided is incorrect.");

      return View(vM);
    }

Home Controller:

using System;
using System.Web.Mvc;
using MyApplication.Models;
using System.Linq;
using System.Web.Security;

namespace MyApplication.Controllers
{
  [Authorize]
  public class HomeController : Controller
  {
    private readonly MyDb _db = new MyDb(); 

    public ActionResult UserRouting()
    {
      var user = _db.Users.SingleOrDefault(u => u.UserName == User.Identity.Name);
      var testrole = Roles.GetRolesForUser();  // for testing, remove from production
      if (User.IsInRole("XYZ")) { // mycode }
      ...
     }
  }

Migration Script: (modeled on SimpleMembershipToIdentityMigration.sql and modified for Identity 2.0 and INT Primary Keys)

    /****** Object: Table [dbo].[AspNetRoles] Script Date: 4/30/14 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

IF OBJECT_ID('dbo.AspNetUserRoles', 'U') IS NOT NULL
    DROP TABLE [dbo].[AspNetUserRoles]
GO
--IF OBJECT_ID('dbo.AspNetUserLogins', 'U') IS NOT NULL
--  DROP TABLE [dbo].[AspNetUserLogins]
--GO
IF OBJECT_ID('dbo.AspNetUserClaims', 'U') IS NOT NULL
    DROP TABLE [dbo].[AspNetUserClaims]
GO
IF OBJECT_ID('dbo.AspNetRoles', 'U') IS NOT NULL
    DROP TABLE [dbo].[AspNetRoles]
GO
IF OBJECT_ID('dbo.AspNetUsers', 'U') IS NOT NULL
    DROP TABLE [dbo].[AspNetUsers]
GO

CREATE TABLE [dbo].[AspNetUsers] (
    --[Id]                                    NVARCHAR (128) NOT NULL,
    [Id]                                      INT            NOT NULL,
    [UserName]                                NVARCHAR (15)  NULL,
    [AcId]                                    INT            NOT NULL,
    [LcId]                                    INT            NOT NULL,
    [Email]                                   NVARCHAR (256) NULL,
    [EmailConfirmed]                          BIT            DEFAULT ((0)) NULL,
    [PasswordHash]                            NVARCHAR (MAX) NULL,
    [SecurityStamp]                           NVARCHAR (MAX) NULL,  
    [PhoneNumber]                             NVARCHAR (MAX) NULL,
    [PhoneNumberConfirmed]                    BIT            DEFAULT ((0)) NULL,
    [TwoFactorEnabled]                        BIT            DEFAULT ((0)) NULL,
    [LockoutEndDateUtc]                       DATETIME       NULL,
    [LockoutEnabled]                          BIT            DEFAULT ((0)) NULL,
    [AccessFailedCount]                       INT            DEFAULT ((0)) NOT NULL,
    [CreateDate]                              DATETIME       NULL,
    CONSTRAINT [PK_dbo.AspNetUsers] PRIMARY KEY CLUSTERED ([Id] ASC)
);
GO
CREATE TABLE [dbo].[AspNetRoles] (
    --[Id]   NVARCHAR (128) NOT NULL,
    [Id]   INT            NOT NULL,
    [Name] NVARCHAR (256) NOT NULL,
    CONSTRAINT [PK_dbo.AspNetRoles] PRIMARY KEY CLUSTERED ([Id] ASC)
);
GO
CREATE TABLE [dbo].[AspNetUserRoles] (
    -- [UserId] NVARCHAR (128) NOT NULL,
    -- [RoleId] NVARCHAR (128) NOT NULL,
    [UserId] INT            NOT NULL,
    [RoleId] INT            NOT NULL,
    CONSTRAINT [PK_dbo.AspNetUserRoles] PRIMARY KEY CLUSTERED ([UserId] ASC, [RoleId] ASC),
    CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[AspNetRoles] ([Id]) ON DELETE CASCADE,
    CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_RoleId]
    ON [dbo].[AspNetUserRoles]([RoleId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_UserId]
    ON [dbo].[AspNetUserRoles]([UserId] ASC);
GO
CREATE TABLE [dbo].[AspNetUserLogins] (
    --[UserId]        NVARCHAR (128) NOT NULL,
    [UserId]        INT            NOT NULL,
    [LoginProvider] NVARCHAR (128) NOT NULL,
    [ProviderKey]   NVARCHAR (128) NOT NULL,
    CONSTRAINT [PK_dbo.AspNetUserLogins] PRIMARY KEY CLUSTERED ([UserId] ASC, [LoginProvider] ASC, [ProviderKey] ASC),
    CONSTRAINT [FK_dbo.AspNetUserLogins_dbo.AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_UserId]
    ON [dbo].[AspNetUserLogins]([UserId] ASC);
GO

CREATE TABLE [dbo].[AspNetUserClaims] (
    [Id]         INT            IDENTITY (1, 1) NOT NULL,
    [ClaimType]  NVARCHAR (MAX) NULL,
    [ClaimValue] NVARCHAR (MAX) NULL,
    -- [UserId]    NVARCHAR (128) NOT NULL,
    [UserId]     INT            NOT NULL,
    CONSTRAINT [PK_dbo.AspNetUserClaims] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_dbo.AspNetUserClaims_dbo.AspNetUsers_User_Id] FOREIGN KEY ([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_User_Id]
    ON [dbo].[AspNetUserClaims]([UserId] ASC);
GO

INSERT INTO AspNetUsers(Id, UserName, AcId, LcId, PasswordHash, SecurityStamp, CreateDate )
SELECT UserProfile.UserId, UserProfile.UserName, UserProfile.BaId, UserProfile.OfcId,
webpages_Membership.Password, webpages_Membership.PasswordSalt, CreateDate
FROM UserProfile
LEFT OUTER JOIN webpages_Membership ON UserProfile.UserId = webpages_Membership.UserId
GO

INSERT INTO AspNetRoles(Id, Name)
SELECT RoleId, RoleName
FROM webpages_Roles
GO

INSERT INTO AspNetUserRoles(UserId, RoleId)
SELECT UserId, RoleId
FROM webpages_UsersInRoles
GO

IF OBJECT_ID('dbo.webpages_OAuthMembership', 'U') IS NOT NULL
    DROP TABLE [dbo].[webpages_OAuthMembership]
GO

IF OBJECT_ID('dbo.webpages_UsersInRoles', 'U') IS NOT NULL
    DROP TABLE [dbo].[webpages_UsersInRoles]
GO
IF OBJECT_ID('dbo.webpages_Roles', 'U') IS NOT NULL
    DROP TABLE [dbo].[webpages_Roles]
GO
IF OBJECT_ID('dbo.UserProfile', 'U') IS NOT NULL
    DROP TABLE [dbo].[UserProfile]
GO
IF OBJECT_ID('dbo.webpages_Membership', 'U') IS NOT NULL
    DROP TABLE [dbo].[webpages_Membership]
GO

IF OBJECT_ID('dbo.__MigrationHistory', 'U') IS NOT NULL
    DROP TABLE [dbo].[__MigrationHistory]
GO

Have I missed something on configuring Identity 2.0 or is this a bug in Identity 2.0?

UPDATE 1: Removed the Global.asax.cs call to WebSecConfig. This had been masking a different issue with User.IsInRole.

Set breakpoint on var user in Home Controller UserRouting Action
Run app in debug and login in as "MyUser".
Step in and user gets set to "MyUser".
Step into var testrole and it throws 'Object reference not set to an instance of an object.'

Re-start and login again as "MyUser".
Step in and user gets set to "MyUser".
From Immediate Window enter User.IsInRole("XYZ") and it returns:

A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
A first chance exception of type 'System.Web.HttpException' occurred in System.Web.dll
A first chance exception of type 'System.Web.HttpException' occurred in System.Web.dll
A first chance exception of type 'System.Web.HttpException' occurred in System.Web.dll

And opens a Warning dialogue box with:

HttpException was unhandled  
An unhandled exception of type 'System.Web.HttpException" occurred in System.Web.dll
Additional information: Unable to connect to SQL Server database.

My basic Database setup is correct or I wouldn't have been able to login. This leads me to believe there is a small discrepancy in how I ran the migration. Does anyone have an idea where I tripped up.

UPDATE 2: I refactored the code and migration script to use the default nvarchar for the Primary Keys and got the same results as Update 1.

UPDATE 3:

Web.Config:

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />

    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <sectionGroup name="nwebsec">
      <!-- For information on how to configure NWebsec please visit: http://nwebsec.codeplex.com/wikipage?title=Configuration -->
      <section name="httpHeaderSecurityModule" type="NWebsec.Modules.Configuration.HttpHeaderSecurityConfigurationSection, NWebsec, Version=3.0.2.0, Culture=neutral, PublicKeyToken=3613da5f958908a1" requirePermission="false" />
    </sectionGroup>
    <sectionGroup name="elmah">
      <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
      <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
      <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
      <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
    </sectionGroup>

  <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --></configSections>
  <connectionStrings>
    <add name="MyApplication" connectionString="data source=.\SqlExpress;Integrated Security=SSPI;MultipleActiveResultSets = true;initial catalog=MyDb" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="elmah.mvc.disableHandler" value="false" />
    <add key="elmah.mvc.disableHandleErrorFilter" value="false" />
    <add key="elmah.mvc.requiresAuthentication" value="true" />
    <add key="elmah.mvc.IgnoreDefaultRoute" value="false" />
    <add key="elmah.mvc.allowedRoles" value="Admin,CorpS" />
    <!--<add key="elmah.mvc.allowedUsers" value="*" />-->
    <add key="elmah.mvc.route" value="elmah" />
  </appSettings>
  <!--
    For a description of web.config changes see http://go.microsoft.com/fwlink/?LinkId=235367.

    The following attributes can be set on the <httpRuntime> tag.
      <system.Web>
        <httpRuntime targetFramework="4.5.1" />
      </system.Web>
  -->
  <system.web>
    <customErrors mode="Off" />
    <compilation debug="true" targetFramework="4.5.1" />
    <httpRuntime targetFramework="4.5" useFullyQualifiedRedirectUrl="true" maxRequestLength="100000" enableVersionHeader="false" />
    <!-- value is Kb, need to avoid crash with file upload of large files -->
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="15" />
    </authentication>
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Optimization" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
      </namespaces>
    </pages>
    <!--<roleManager enabled="true" defaultProvider="simple">
      <providers>
        <clear />
        <add name="simple" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData" />
      </providers>
    </roleManager>
    <membership defaultProvider="simple">
      <providers>
        <clear />
        <add name="simple" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
      </providers>
    </membership>-->
    <httpModules>
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
    </httpModules>
    <sessionState cookieName="My_SessionId" />
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />

    </handlers>
    <modules>
      <add name="NWebsecHttpHeaderSecurityModule" type="NWebsec.Modules.HttpHeaderSecurityModule, NWebsec, Version=3.0.2.0, Culture=neutral, PublicKeyToken=3613da5f958908a1" />
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
      <remove name="RoleManager" />
    </modules>
    <httpProtocol>
      <customHeaders>
        <clear />
      </customHeaders>
     </httpProtocol>
     <security>
       <requestFiltering>
         <hiddenSegments>
           <add segment="NWebsecConfig" />
         </hiddenSegments>
       </requestFiltering>
      </security>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.6.1.0" newVersion="5.6.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.6.1.0" newVersion="5.6.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.6.1.0" newVersion="5.6.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Razor" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly><assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" /></dependentAssembly>
      <dependentAssembly><assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" /></dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages.Razor" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
    <dependentAssembly><assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" /><bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" /></dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2" />
      </dependentAssembly>
    </assemblyBinding>
    <!-- This prevents the Windows Event Log from frequently logging that HMAC1 is being used (when the other party needs it). -->
    <legacyHMACWarning enabled="0" />
    <!-- When targeting ASP.NET MVC 3, this assemblyBinding makes MVC 1 and 2 references relink
         to MVC 3 so libraries such as DotNetOpenAuth that compile against MVC 1 will work with it.
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
     -->
  </runtime>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
  <system.net>
    <defaultProxy enabled="true" />
    <settings>
      <!-- This setting causes .NET to check certificate revocation lists (CRL) 
           before trusting HTTPS certificates.  But this setting tends to not 
           be allowed in shared hosting environments. -->
      <!--<servicePointManager checkCertificateRevocationList="true"/>-->
    </settings>
  </system.net>
  <nwebsec>
    <httpHeaderSecurityModule xmlns="http://nwebsec.com/HttpHeaderSecurityModuleConfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="NWebsecConfig/HttpHeaderSecurityModuleConfig.xsd">
      <redirectValidation enabled="true"> <add allowedDestination="https://localhost/" /> </redirectValidation>
      <securityHttpHeaders>
        <x-Frame-Options policy="Deny" />
        <strict-Transport-Security max-age="365" />
        <x-Content-Type-Options enabled="true" />
      </securityHttpHeaders>
    </httpHeaderSecurityModule>
  </nwebsec>
  <elmah>
    <security allowRemoteAccess="false" />
  </elmah>

</configuration>

I needed to add <remove name="RoleManager" /> to the node as part of the MVC4-MVC5 migration, which I have now done.

Now I'm back to my original problem. User.IsInRole always returns false. If I run User.IsAuthenticated from the Immediate Window it returns

'System.Security.Principal.IPrincipal' does not contain a definition for 'IsAuthenticated' and no extension method 'IsAuthenticated' accepting a first argument of type 'System.Security.Principal.IPrincipal' could be found (are you missing a using directive or an assembly reference?)

I was able to get Identity.UserManager.IsInRole to work.

Home Controller: (revised to use UserManager)

using System;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using MyApplication.Models;
using System.Linq;
//using System.Web.Security;

namespace MyApplication.Controllers
{
  [Authorize]
  public class HomeController : Controller
  {
    private readonly MyDb _db = new MyDb();
    public UserManager<ApplicationUser> UserManager { get; private set; }
    public HomeController()  
      : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new MyDb()))) { }
    public HomeController(UserManager<ApplicationUser> userManager)  
    { UserManager = userManager; }

    public ActionResult UserRouting()
    {
      var user = _db.Users.SingleOrDefault(u => u.UserName == User.Identity.Name);
      //var testrole = Roles.GetRolesForUser();  // for testing, remove from production
      if (UserManager.IsInRole(user.Id, "XYZ")) { // mycode }
      ...
     }
  }

This indicates that my database migration was correct.

I have dozens of calls to User.IsInRole in Controllers, Views, Custom Attributes, methods and classes in my app. To my understanding User.IsInRole is still a valid method in MVC5-EF6. If it is still considered a valid method rather than having to do a major refactoring of this legacy app I pose again my original question "why is User.IsInRole failing after SimpleMembership to Identity 2.0 migration"?

I've tested User.IsInRole in an Identity 1.0 MVC app and it works. I updated the app to Identity 2.0 and it still works. So I think it is safe to say that User.IsInRole is supported with Identity 2.0.


回答1:


After further investigation I found the problem to be that I never successfully logged in and since the User wasn't authenticated it caused the User.IsInRole to fail.

   var user = UserManager.Find(vM.UserName, vM.Password);

When you examine user in debug "MyUser" has been found but if you drill down further you find UserName is set to null.

User
    --{System.Data.Entity.DynamicProxies.ApplicationUser_...}   Identity.Models.ApplicationUser{System.Data.Entity.DynamicProxies.ApplicationUser_...} …
        base    {System.Data.Entity.DynamicProxies.ApplicationUser_...} Identity.Models.ApplicationUser {System.Data.Entity.DynamicProxies.ApplicationUser_...}
            base    {System.Data.Entity.DynamicProxies.ApplicationUser_...} Microsoft.AspNet.Identity.EntityFramework.IdentityUser  …
                base    {System.Data.Entity.DynamicProxies.ApplicationUser_...} Microsoft.AspNet.Identity.EntityFramework.IdentityUser<string,Microsoft.AspNet.Identity ...
                    AccessFailedCount   0           int
                    Claims              Count = 0   System.Collections.Generic.ICollection<Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim>  …
                    Email               null        string
                    EmailConfirmed      false       bool
                    Id                  "2"         string
                    LockoutEnabled      false       bool
                    LockoutEndDateUtc   null        System.DateTime?
                    Logins              Count = 0   System.Collections.Generic.ICollection<Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin> …
                    PasswordHash        "xxxxxx"    string
                    PhoneNumber         null        string
                    PhoneNumberConfirmed    false   bool
                    Roles               Count = 2   System.Collections.Generic.ICollection<Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole> …
                    SecurityStamp       ""          string
                    TwoFactorEnabled    false       bool
                    UserName            null        string

In ApplicationUser I overrode the UserName.

[StringLength(15)]
public new string UserName { get; set; }

When I removed the UserName override everything started working (I left UserName as a nvarchar(15), not null). The UserName override in ApplicationUser was the problem. If you want to change the nvarchar sizes you apparently just need to do that in the migration script.

Migrations from SimpleMembrship to Identity 2.0 works. User.IsInRole also works with Identity 2.0.



来源:https://stackoverflow.com/questions/23434279/user-isinrole-failing-after-migrating-simplemembership-database-to-identity-2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!