background
In these days i updating the dotnetage.com to DotNetAge Mvc3 edition. After i upload all files and data i login to dotnetage.com as in the past sth had happend. After logined the FormAuth cookie will lose in a short time so that the current login account will auto logout! The DotNetAge 2 was update to Mvc3 so I doubt that is Razor cause this issues. I google it but could not found any answers.
Something what i do
#1 I install the DotNetAge on local server everything works fine.
#2 It seems to be form authentication fail. I think the cookie timeout is too short so i change the forms configuation and try again and again but it stand still. At last i view the cookie detail in my web browser. The form auth cookie has not expried! It’s easy to see that forms auth had no problem.
#3 The ASP.NET will read the cookies when accept http request. There are two HttpModules respone for authentication : FormsAuthenicationModule and RoleManagerModule. I had to read the source code to know about what have happend in these modules.
In FormsAuthenicationModule there is a private method named OnEnter() it handling all auth reqest :
1: private void OnEnter(object source, EventArgs eventArgs)
2: {
3: this._fOnEnterCalled = true;
4: HttpApplication application = (HttpApplication) source;
5: HttpContext context = application.Context;
6: this.OnAuthenticate(new FormsAuthenticationEventArgs(context));
7: CookielessHelperClass cookielessHelper = context.CookielessHelper;
8: if (AuthenticationConfig.AccessingLoginPage(context, FormsAuthentication.LoginUrl))
9: {
10: context.SetSkipAuthorizationNoDemand(true, false);
11: cookielessHelper.RedirectWithDetectionIfRequired(null, FormsAuthentication.CookieMode);
12: }
13: if (!context.SkipAuthorization)
14: {
15: context.SetSkipAuthorizationNoDemand(AssemblyResourceLoader.IsValidWebResourceRequest(context), false);
16: }
17: }
18:
19:
As we see this method when user request an unanuthorize url it will be redirect to LoginUrl that setting in forms configuation element in web.config.
In DotNetAge is using the Request.IsAuthenticated property confirm the authenicated user. So let’s look in the IsAuthenticated property
1: public sealed class HttpRequest
2: {
3: public bool IsAuthenticated
4: {
5: get
6: {
7: return (((this._context.User != null) && (this._context.User.Identity != null)) && this._context.User.Identity.IsAuthenticated);
8: }
9: }
Now we know that the IsAuthenticated is returns from User.Identity. I think the RoleManagerModule is create the User.Identity from role cookie when accepted the http request.
I think maybe when i login first the role cookie is add to response but it lose when i request other page. So i check my browser and find the cookie name “.MVCROLES” after i login that i can find that cookie and it’s value is corrently.But when i go to another page The Request.IsAuthenticated returns false and the cookie value is empty so DotNetAge recognize current user is not login. But why the role cookie value is empty in second response?
These is a OnLeave method in RoleManagerModule that generate the role cookie and set to response object:
1: private void OnLeave(object source, EventArgs eventArgs)
2: {
3: HttpApplication application = (HttpApplication) source;
4: HttpContext context = application.Context;
5: if (((Roles.Enabled && Roles.CacheRolesInCookie) && !context.Response.HeadersWritten) && (((context.User != null) && (context.User is RolePrincipal)) && context.User.Identity.IsAuthenticated))
6: {
7: if (Roles.CookieRequireSSL && !context.Request.IsSecureConnection)
8: {
9: if (context.Request.Cookies[Roles.CookieName] != null)
10: {
11: Roles.DeleteCookie();
12: }
13: }
14: else
15: {
16: RolePrincipal user = (RolePrincipal) context.User;
17: if (user.CachedListChanged && context.Request.Browser.Cookies)
18: {
19: string str = user.ToEncryptedTicket();
20: if (string.IsNullOrEmpty(str) || (str.Length > 0x1000))
21: {
22: Roles.DeleteCookie();
23: }
24: else
25: {
26: HttpCookie cookie = new HttpCookie(Roles.CookieName, str);
27: cookie.HttpOnly = true;
28: cookie.Path = Roles.CookiePath;
29: cookie.Domain = Roles.Domain;
30: if (Roles.CreatePersistentCookie)
31: {
32: cookie.Expires = user.ExpireDate;
33: }
34: cookie.Secure = Roles.CookieRequireSSL;
35: context.Response.Cookies.Add(cookie);
36: }
37: }
38: }
39: }
40: }
41:
42:
Please notes the hight row: string str = user.ToEncryptedTicket(); follow ToEncryptedTicket() method :
1: [Serializable]
2: public class RolePrincipal : IPrincipal, ISerializable
3: {
4:
5: [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.SerializationFormatter)]
6: public string ToEncryptedTicket()
7: {
8: if (!Roles.Enabled)
9: {
10: return null;
11: }
12: if ((this._Identity != null) && !this._Identity.IsAuthenticated)
13: {
14: return null;
15: }
16: if ((this._Identity == null) && string.IsNullOrEmpty(this._Username))
17: {
18: return null;
19: }
20: if (this._Roles.Count > Roles.MaxCachedResults)
21: {
22: return null;
23: }
24: MemoryStream serializationStream = new MemoryStream();
25: byte[] buf = null;
26: IIdentity identity = this._Identity;
27: try
28: {
29: this._Identity = null;
30: new BinaryFormatter().Serialize(serializationStream, this);
31: buf = serializationStream.ToArray();
32: }
33: finally
34: {
35: serializationStream.Close();
36: this._Identity = identity;
37: }
38: return CookieProtectionHelper.Encode(Roles.CookieProtectionValue, buf, buf.Length);
39: }
40:
41: }
42:
43:
1: internal class CookieProtectionHelper
2: {
3: internal static string Encode(CookieProtection cookieProtection, byte[] buf, int count)
4: {
5: if ((cookieProtection == CookieProtection.All) || (cookieProtection == CookieProtection.Validation))
6: {
7: byte[] src = MachineKeySection.HashData(buf, null, 0, count);
8: if (src == null)
9: {
10: return null;
11: }
12: if (buf.Length >= (count + src.Length))
13: {
14: Buffer.BlockCopy(src, 0, buf, count, src.Length);
15: }
16: else
17: {
18: byte[] buffer2 = buf;
19: buf = new byte[count + src.Length];
20: Buffer.BlockCopy(buffer2, 0, buf, 0, count);
21: Buffer.BlockCopy(src, 0, buf, count, src.Length);
22: }
23: count += src.Length;
24: }
25: if ((cookieProtection == CookieProtection.All) || (cookieProtection == CookieProtection.Encryption))
26: {
27: buf = MachineKeySection.EncryptOrDecryptData(true, buf, null, 0, count);
28: count = buf.Length;
29: }
30: if (count < buf.Length)
31: {
32: byte[] buffer3 = buf;
33: buf = new byte[count];
34: Buffer.BlockCopy(buffer3, 0, buf, 0, count);
35: }
36: return HttpServerUtility.UrlTokenEncode(buf);
37: }
38: }
39:
40:
41:
OK now i know the Role cookie is encrypt by machine key,so i think must be Machine key setting has some problem , so i open the IIS manager and open Machine key setting.
By default the MachineKey is generate automaically. I checked the “Generate a unique key for each application” option and save.
I restart the IIS and login again , fortunately it seems to be resolved!
Conclusion
I have never thought that the Authorzation and MachineKey setting so close! There many default encrypt / decrypt methods in .NET are using MachineKeys such as Html.AntiForgeryToken() and some we unknow , so when we publish the website to remote server and we have some features maybe use encrypt/decrypt the best way is set the Machine key clearly then AutoGenerate.
-
-
阅读(3140)
-
固定链接