I have created an ASP.NET MVC3 (Razor) web application, and replaced the membership provider with the universal membership provider by MS, installed via NuGet, and the MDF database with a SQL Server Compact 4 SDF database.
Everything works fine with this SDF, I can manage users and roles from WSAT, be validated when logging in my development workstation, etc. Anyway, I'm experiencing some weird behavior and maybe it's just something other users have encountered: as soon as I deploy the application to the production server, if I try to login with any user account (there are about 60 accounts) I am rejected with an invalid password error. I also downloaded back the SDF database into my development machine, and it works with any account.
I can add that I created all the accounts programmatically, from an external list, with a code like this:
MembershipUser user = Membership.GetUser(sName);
if (user != null)
{
Membership.CreateUser(sName, sPassword, sEmail);
AddUserToRole(sName, bIsAdmin? "administrator" : "member");
}
Anyway they all look OK and active in WSAT.
I tried with both administrator and member accounts, but I get the same result. Yet, the database is the same I use locally with success. I have also listed on a page all the users accounts with their lock state, just in case, but they all appear OK, and none is missing, both online and locally. Any hint?
My configuration is like:
...
<connectionStrings>
<clear />
<add name="ApplicationServices"
connectionString="Data Source=|DataDirectory|\Organizer.sdf;"
providerName="System.Data.SqlServerCe.4.0" />
<add name="DefaultConnection"
connectionString="Data Source=|DataDirectory|\Organizer.sdf;"
providerName="System.Data.SqlServerCe.4.0" />
</connectionStrings>
...
<membership defaultProvider="DefaultMembershipProvider">
<providers>
<clear />
<add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
</providers>
</membership>
<profile defaultProvider="DefaultProfileProvider">
<providers>
<clear />
<add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
</providers>
</profile>
<roleManager enabled="true" defaultProvider="DefaultRoleProvider">
<providers>
<clear />
<add connectionStringName="ApplicationServices" applicationName="/" name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" />
<add applicationName="/" name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" />
<add name="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicatio开发者_JS百科nName="/" />
</providers>
</roleManager>
...
UPDATE: during some random attempts, I tried to set one of the accounts to a clear password format, instead of the default hashed one, and this works, while all the other unchanged accounts keep showing the same issue. So I must infer there is something about the hashed password which does not work with SqlServer Compact and/or my web configuration. Does anybody know about issues in this area with CF4?
Sorry to bother, I found the solution myself, at least I hope this can be useful to other newcomers: the problem was in the machine key for password hashing. I assumed it was equal in my developer machine and in my production server, but this was not the case. I placed the key directly in my web.config so that this setting overrides the machine-level one if any. Thus, set the machine key like:
<machineKey validationKey="87172C641DE5712040FA42BFEBF230DD8D62D38CBAD58D67FA427414B767C3A64393747FCD46D5CE7E03E9798DD0491EDE6EB52FF1AC3D28FD4AEA2F693CCD32" decryptionKey="58F39AA799F12FF83F230607EAE74AD228E53D5519C51121129C21A841E385FD" validation="SHA1" decryption="AES" />
in system.web of your web.config. You can generate the key values with their XML code from here:
http://aspnetresources.com/tools/machineKey
In my case, all my user accounts were already there, so I could not simply recreate them from my original import list. I rather used some code to reset all their passwords. To this end, keep in mind that more often than not Membership.ChangePassword fails by returning false, and at least for me it was not the case that the user was locked (I checked this because this is said to be the typical cause of the failure). So, if you cannot simply change the password, first reset it and then re-set it, like:
MembershipUser user = Membership.GetUser(sName);
if (user == null)
{
// user not found...
continue;
}
if (user.IsLockedOut) user.UnlockUser();
string sTemp = user.ResetPassword();
bool b = user.ChangePassword(sTemp, sPassword);
// b is false on failure
I did it in a console application, so in this case remember to add the same system.web sections into its app.config or the passwords will be hashed with some other value.
I then re-uploaded my SDF together with the new web.config (keys must match!), and now it works. Side note: often, in VS2010 SDF based databases behave strangely: e.g. Membership.GetUser() returns null even when the user name is there. In this case I just get a new copy of the untouched SDF and try again. Even closing and reopening VS may help. So, always keep a copy of your SDF before editing... (and remember that for console apps it is copied in the debug/bin directory, it is that you are going to use).
Thanks to all readers anyway
精彩评论