![]() | You are viewing Log in Create a LiveJournal Account Learn more | Explore LJ: Life Entertainment Music Culture News & Politics Technology |
A hack into the .NET authentication classes to extract a component that can be called by legacy asp applications to generate the same authentication token used in asp.net applications.
Ting Huang A hack into the .NET authentication classes to extract a component that can be called by legacy asp applications to generate the same authentication token used in asp.net applications. (Link to part I: http://www.wwwcoder.com/main/parentid/25 In part I of the article, I introduced a way to create a single sign-on between asp and asp.net applications. Although the solution is sound, it is not without shortcomings: 1. While the authentication token generated is different each time, it is not time-stamped. Hence, there is no expiration by itself. This could lead to a high risk of being maliciously reused when the token is hijacked. 2. It is not a time-saving or effort-saving solution, because additional development is required on both sides of asp and asp.net applications to manage the authentication token. It would be ideal if the forms authentication mechanism from ASP.NET can be utilized by ASP application to generate or verify authentication token efficiently. Additionally, the token generated should be automatically recognized as a valid token by ASP.NET built-in forms authentication mechanism as well. Inspired by a Microsoft knowledge-base article at http://support.microsoft.com/default.asp What happens behind the scene of the form authentication mechanism in ASP.NET is that when a client request goes through the http pipeline, the FormsAuthenticationModule (a HttpModule) picks up the incoming request and tries to extract authentication ticket from cookie. The process requires decryption and reading configurations via HttpContext. The solution is to extract the core functionalities apart from the FormsAuthentication utilities in ASP.NET and remove the requirements for HttpContext and other related classes and make them into a authentication component that can be called by ASP application. Here is how the solution looks like: The following steps show how to get the solution working. 1. Synchronize the encryption and decryption key Forms authentication in asp.net uses the <machineKey> element in machine.config or web.config for encryption and decryption authentication token: <machineKey validationKey="AutoGenerate|value[,Isola If you have run ASP.NET applications in a server farm, you probably know that the machine key has to be set with the same value on all severs in the farm. This ensures that tokens generated on one of the servers can be validated on another. For the same reason, it is necessary to synchronize the encryption and decryption key in both ASP.NET forms authentication and the custom authentication component. The following link can be used to generate a machine key setting: http://www.eggheadcafe.com/articles/Gene The following is an example of such setting: <machineKey validationKey='6C0D4C65DBD3CF60C037E0BE4 This is the only change has to be made in ASP.NET for the single sign-on to work. 2. Create a COM wrapper for authentication This is the ‘bare bone’ class for the authentication API. public class AuthAPI public void Initialize() public void Initialize2( public string GetAuthCookieValue( public bool IsAuthenticated( Initialize2 method requires validationKey, decryptionKey and validationMode as well as other parameters being passed in order for the custom FormsAuthentication to be initialized. The two other initialization methods will use the default keys in the class. To make the COM call easier, don’t use overloads in C# for the initialization methods. GetAuthCookieValue method works almost the same as GetAuthCookie in ASP.NET FormsAuthentication class except that it doesn’t return a HttpCookie object but rather a string value of the cookie to be written directly to client’s browser. IsAuthenticated method validates the incoming cookie if it is a valid authentication cookie. If a renew action is required for the cookie on the caller, it will return value in the newCookieValue parameter, otherwise, it will be empty. The expiration DateTime value gives convenience to the caller when it performs the renew action. 3. Making a new asp authentication script To retrieve the authentication cookie: <% cookieName = ".ASPXAUTH" Set ulogin = Server.CreateObject("SingleSignon.AuthAP <% Set ulogin = Server.CreateObject("SingleSignon.AuthAP In this article, a custom authentication component is extracted from the ASP.NET forms authentication utilities to remove any HttpContext initialization so that it can be called directly in classic ASP scripts through COM interface. The authentication token obtained via this API component works between ASP.NET and ASP application, with minimum setting in ASP.NET’s forms authentication. It also works efficiently without a web service workaround. There are more that can be done based on this solution. These include: For more information, please see the following resources:Introduction
How Forms Authentication Works in ASP.NET
Solution

4E392056DE5EEEEC0061673BB413DB533D6F8C61
decryptionKey='7EBC8C30854618249344BAF79
{
public AuthAPI()
{}
{
FormsAuthentication.Initialize();
}
public void Initialize1(
string cookieName,
string cookieDomain,
string cookiePath,
bool slidingExpiration,
int timeout,
bool requireSSL
)
{
FormsAuthentication.Initialize(
cookieName,
cookieDomain,
cookiePath,
slidingExpiration,
timeout,
requireSSL
);
}
string cookieName,
string cookieDomain,
string cookiePath,
bool slidingExpiration,
string protection,
int timeout,
bool requireSSL,
string validationKey,
string decryptionKey,
string validationMode
)
{
FormsAuthentication.Initialize(
cookieName,
cookieDomain,
cookiePath,
slidingExpiration,
(FormsProtectionEnum)Enum.Parse(typeof(FormsProtectionEnum), protection, true),
timeout,
requireSSL,
validationKey,
decryptionKey,
(FormsCryptoValidationMode)Enum.Parse(typeof(FormsCryptoValidationMode), validationMode, true)
);
}
string userName,
bool createPersistentCookie)
{
HttpCookie cookie = FormsAuthentication.GetAuthCookie(userNa
return cookie.Value;
}
string cookieValue,
out string newCookieValue,
out DateTime expiration)
{
return FormsAuthentication.IsAuthenticated(cook
}
}
cookieDomain = "yourdomain"
cookiePath = "/"
uLogin.Initialize1 cookieName, cookieDomain, cookiePath, 1, 20, 0
s = uLogin.GetAuthCookieValue(cookieName, false)
Call AddCookie ( cookieName, s )
set ulogin = nothing
%>
isLogin = uLogin.IsAuthenticated(GetCookie(uLogin.F
If not isLogin then
‘ user is not logged in, redirect to login page
Else
‘ user is logged in
If not isempty(newCookieValue) then
'renew cookie
Call AddCookie ( cookieName, newCookieValue, expiration )
End If
End If
Set ulogin = nothing
%>Conclusion
References
ASP.NET security overview (http://support.microsoft.com/default.asp
Encryption and hashing are used in this project to solve a common problem faced by many programmers, which is to create a single sign-on across multiple applications, which might be a mix of legacy ASP applications and ASP.NET applications.
The Scenario
For many membership websites, it is a very common scenario to limit access of some services to members only and require users to login. But oftentimes these different services are served under different applications or even under different platforms. This time we want to solve the problem of making a unified login that can be shared among legacy ASP application and ASP.NET application.
The First Thought
The first thought is always to try to dig out anything that can be utilized in the .NET framework. The build-in FormsAuthentication class under System.Web.Security namespace is the best candidate. By calling the SetAuthCookie(string, bool) method in FormsAuthentication, an authentication ticket for the given user identity can be created and attached to the cookie's collection of the outgoing response. Later by checking the Request.IsAuthenticated value, the user's login status can be verified. This works very well among pure ASP.NET applications. But when it comes to ASP applications, these methods or objects are obviously not available, or I should say, not easy to get implemented in the same way that ASP.NET does. Since we really don¡¯t know the detail inside FormsAuthentication or related classes, there is no easy way we can modify or mimic these classes to make them available to classic ASP application.
How about sharing these classes in GAC that can be called by ASP script? The FormsAuthentication class fails when called from ASP script because it simply needs a FORM to be initialized. If the FormsAuthentication and related classes can be modified, we can probably get rid of some initialization requirements that are not needed.
The Solution
We need to make our own authentication class.
First we create the EncryptString class to serve the Obfuscation of the user's identity as well as to verify the Obfuscated string. A triple DES encryption and a couple of MD5 hashes are involved.</font>
using System;using System.Security.Cryptography;using System.Text;using System.Web;using System.Globalization;namespace SingleLogin{ /// <summary> /// Provides a way to obfuscate the string. /// </summary> public class EncryptString { public EncryptString(){} public bool Verify(string pEncryptedString) { return _Verify(pEncryptedString); } public string EncryptedString { get { return _Encrypt(); } } public string UserName { get { return _UserName; } set { _UserName = value; } } public string Key { get { return _Key; } set { _Key = value; } } private string _Encrypt() { MD5CryptoServiceProvider MD5 = new MD5CryptoServiceProvider(); byte[] bKey = MD5.ComputeHash(Encoding.ASCII.GetBytes(_Key)); byte[] bUserName = MD5.ComputeHash(Encoding.ASCII.GetBytes(_UserName)); byte[] eUserName = MD5.ComputeHash(_Mix(bUserName, bKey)); TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider(); byte[] eKey = des.CreateEncryptor(_Key192, _IV192).TransformFinalBlock(bKey, 0, bKey.Length); return _ByteToHexString(eUserName) + _ByteToHexString(eKey); } private bool _Verify(string pEncryptedString) { try { byte[] eKey = _HexToByteArray(pEncryptedString.Substring(32)); byte[] eUserName = _HexToByteArray(pEncryptedString.Substring(0, 32)); MD5CryptoServiceProvider MD5 = new MD5CryptoServiceProvider(); TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider(); byte[] bKey = des.CreateDecryptor(_Key192, _IV192).TransformFinalBlock(eKey, 0, eKey.Length); byte[] bUserName = MD5.ComputeHash(Encoding.ASCII.GetBytes(_UserName)); byte[] eUserName1 = MD5.ComputeHash(_Mix(bUserName, bKey)); return (_ByteToHexString(eUserName) == _ByteToHexString(eUserName1)); } catch (CryptographicException) { } catch (FormatException) { } return false; } private byte[] _Mix(byte[] pUserName, byte[] pKey) { byte[] retByteArray = new byte[pUserName.Length * 2]; int i = 0, j; foreach(byte b in pUserName) { j = i * 2; retByteArray[j] = pUserName[i]; retByteArray[j+1] = pKey[i]; i++; } return retByteArray; } private string _ByteToHexString(byte[] pByteArray) { string retHexString = ""; foreach(byte b in pByteArray) { retHexString += b.ToString("x2"); } return retHexString; } private static byte[] _HexToByteArray(string pHexString) { int length = pHexString.Length/2; byte[] retByteArray = new byte[length]; for(int i=0; i< length; i++) { retByteArray[i] = Byte.Parse(pHexString.Substring(i*2, 2), NumberStyles.HexNumber); } return retByteArray; } // The key used for the triple DES encryption private readonly byte[] _Key192 = new byte[24] { 142, 216, 90, 16, 178, 40, 8, 32, 35, 167, 34, 80, 226, 200, 125, 192, 2, 94, 51, 204, 139, 35, 14, 19}; // The Initialization Vector for the triple DES encryption private readonly byte[] _IV192 = new byte[24] { 54, 173, 246, 179, 36, 99, 197, 3, 42, 65, 62, 38, 134, 7, 29, 123, 145, 23, 200, 58, 193, 10, 111, 232}; private string _UserName, _Key; }}The process looks like the following:

The Authentication class then can be created:
using System;using System.Web;using System.Text;namespace SingleLogin{ /// <summary> /// Summary description for Authenticate. /// </summary> public class Authentication { public Authentication() { } public bool IsAuthenticated(string pEncString, string pUserName) { EncryptString sqs = new EncryptString(); sqs.UserName = pUserName; return sqs.Verify(pEncString); } public string GetAuthenticateCode(string pUserName) { EncryptString sqs = new EncryptString(); sqs.UserName = pUserName; sqs.Key = _GenerateRandomKey(8); return sqs.EncryptedString; } private string _GenerateRandomKey(int pKeyLength) { Random random = new Random((int)DateTime.Now.Ticks); int charLength = chars.Length; StringBuilder sb = new StringBuilder(); for (int i=0; i< pKeyLength; i++) { int idx = random.Next(0, charLength); sb.Append(chars.Substring(idx, 1)); } return sb.ToString(); } private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; }}To test our solution in ASP application, we first register our assembly with GAC. a reference can be found at http://msdn.microsoft.com/msdnmag/issues/0
The shortcut to register the assembly in GAC is to locate the SingleLogin.dll that can be built in our solution (see the attached solution package) and run the following two .NET commands:
regasm SingleLogin.dll
gacutil /i SingleLogin.dll
After that, you can run the following ASP script on the web server to test if the correct authenticated string can be generated based on the user's identity.
<% Set test = Server.CreateObject("SingleLogin.Authentication") ' get the authenticate string cookie = test.GetAuthenticateCode("demo") response.write cookie ' test the authenticate string with the original username name = test.IsAuthenticated(cookie, "demo") response.write "<br>" & name ' test the authenticate string with a wrong username name = test.IsAuthenticated(cookie, "ting") response.write "<br>" & name set test = nothing%>
A similar ASP.NET web sample is also included in the attached solution package.
When the applications share the same authentication methods, a single sign-on can then be created among them.
Some Final Thoughts
There are some risks to authenticate users in the way we do. For example, the authentication cookie can be hijacked and the hijacker can reuse the cookie to authenticate himself. But this happens anyway even if you are authenticating users with the build-in FormsAuthentication class. Another vulnerability is the computed key can be compromised and used to generate the authentication ticket. In this case, the attacker has to compromise the triple DES encryption.
Still, there is plenty of space to think of for restructuring or customizing the process of encryption. Any comments are welcome here.
References
.NET Interop: Get Ready for Microsoft .NET by Using Wrappers to Interact with COM-based Applications
http://msdn.microsoft.com/msdnmag/issues/0
Secure Query Strings: Preventing the Tampering of Data Passed between Applications
http://www.dotnetjunkies.com/how%20to/99
Now ASP.NET has 3 kinds of authentication -
Passport - nobody uses it anymore, and that is SSO by definition anyway.
Windows - SSO is like a moot point here.
Forms - This is where the problem begins. So thats all I'm gonna discuss here.
Well, single sign on is almost always implemented using a central ticketing authority. The idea being similar to the concept of visiting a bar. You get a "token" stamped on the back of your hand as you walk into the bar, and then everywhere in the bar, you are recognized as "over 21".
Similarly, in a ticketing based SSO implementation, a central ticketing authority issues you a "ticket" at logon. Then you can integrate with any other system by passing the "ticket", rather than your authentication credentials. Then as long as the protected system can verify the "ticket" as being valid, you're in. Else, you're out.
Anyway, so the first thing you need to do is setup a ticket verification webservice. The idea being, anytime you successfully authenticate, you will be issued a ticket, which will ideally be a GUID, stored as cookie that expires when the user hits the CROSS button on his browser. Then you can circumvent the membership API's "Authenticate" method, by instead verifying the ticket - IF one is present.
So lets assume that the ticket is being passed as a querystring called "ssoToken". Obviously, if someone else sniffed your ticketID, then he could masquerade as you - so this approach requires some kind of encryption.
So in the Page_Load for your login.aspx page, write the following code -
protected void Page_Load(object sender, EventArgs e)
{
string unescapedtokenID = Uri.UnescapeDataString(Request.QueryString[
string tokenID = ParseURL(unescapedtokenID);
if (IsTokenValid(tokenID))
{
FormsAuthentication.SetAuthCookie(GetUserName(tokenID), false);
Response.Redirect(unescapedtokenID);
}
}
private string ParseURL(string unescapedTokenID)
{
UriBuilder bldr = new UriBuilder("http://dummyurl" + unescapedTokenID);
QueryStringParser coll = new QueryStringParser(bldr.Query);
return coll["ssoToken"];
}
internal class QueryStringParser : System.Collections.Specialized.NameValueCollection
{
internal QueryStringParser(string s)
{
if (s.Length != 0) s = s.Substring(1);
int num1 = (s != null) ? s.Length : 0;
for (int num2 = 0; num2 < num1; num2++)
{
int num3 = num2;
int num4 = -1;
while (num2 < num1)
{
char ch1 = s[num2];
if (ch1 == '=')
{
if (num4 < 0)
{
num4 = num2;
}
}
else if (ch1 == '&')
{
break;
}
num2++;
}
string text1 = null;
string text2 = null;
if (num4 >= 0)
{
text1 = s.Substring(num3, num4 - num3);
text2 = s.Substring(num4 + 1, (num2 - num4) - 1);
}
else
{
text2 = s.Substring(num3, num2 - num3);
}
base.Add(HttpUtility.UrlDecode(text1, Encoding.ASCII), HttpUtility.UrlDecode(text2, Encoding.ASCII));
if ((num2 == (num1 - 1)) && (s[num2] == '&'))
{
base.Add(null, string.Empty);
}
}
}
}
Thats it !! Put all these together, and you have at your hands SSO using Membership API.
Single Sign-On (SSO) is a hot topic these days. Most clients I worked with have more than one web application running under different versions of .NET framework in different subdomains, or even in different domains and they want to let the user login once and stay logged in when switching to a different web site. Today we will implement SSO and see if we can make it work in different scenarios. We will start with a simple case and gradually build upon it:
1. SSO for parent and child application in the virtual sub-directory
Lets assume that we have two .NET applications - Foo and Bar, and Bar is running in a virtual sub-directory of Foo (http://foo.com/bar). Both applications implement Forms authentication. Implementation of Forms authentication requires you to override the Application_AuthenticateRequest, where you perform the authentication and upon successful authentication, call FormsAuthentication.RedirectFromLoginPag
<
authentication mode="Forms"><
authentication mode="Forms">The important attributes here are name and protection. If you make them match for both Foo and Bar applications, they will both write and read the same cookie using the same protection level, effectively providing SSO:
<
authentication mode="Forms">When protection attribute is set to "All", both encryption and validation (via hash) is applied to the cookie. The default validation and encryption keys are stored in the machine.config file and can be overridden in the application’s web.config file. The default value is this:
<
machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey=" AutoGenerate,IsolateApps" validation="SHA1" />IsolateApps means that a different key will be generated for every application. We can’t have that. In order for the cookie to be encrypted and decrypted with the same key in all applications either remove the IsolateApps option or better yet, add the same concrete key to the web.config of all applications using SSO:
<
machineKey validationKey="F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902" decryptionKey="F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902If you are authenticating against the same users store, this is all it takes – a few changes to the web.config files.
2. SSO using different authorization credentials (username mapping)
But what if the Foo application authenticates against its own database and the Bar application uses Membership API or some other form of authentication? In this case the automatic cookie that is created on the Foo is not going to be any good for the Bar, since it will contain the user name that makes no sense to the Bar.
To make it work, you will need to create the second authentication cookie especially for the Bar application. You will also need a way to map the Foo user to the Bar user. Lets assume that you have a user "John Doe" logging in to the Foo application and you determined that this user is identified as "johnd" in the Bar application. In the Foo authentication method you will add the following code:
FormsAuthenticationTicket
fat = new FormsAuthenticationTicket(1, "johnd", DateTime.Now, DateTime.Now.AddYears(1), true, "");FormsAuthentication.RedirectFromLoginPag
User names are hard-coded for demonstration purposes only. This code snippet creates the FormsAuthenticationTicket for the Bar application and stuffs it with the user name that makes sense in the context of the Bar application. And then it calls RedirectFromLoginPage to create the correct authentication cookie for the Foo application. If you changed the name of forms authentication cookie to be the same for both applications (see our previous example), make sure that they are different now, since we are not using the same cookie for both sites anymore:
<
authentication mode="Forms"><
authentication mode="Forms">Now when the user is logged in to Foo, he is mapped to the Bar user and the Bar authentication ticket is created along with the Foo authentication ticket. If you want it to work in the reverse direction, add similar code to the Bar application:
FormsAuthenticationTicket
fat = new FormsAuthenticationTicket(1, "John Doe", DateTime.Now, DateTime.Now.AddYears(1), true, "");FormsAuthentication.RedirectFromLoginPag
Also make sure that you have the <machineKey> element in web.config of both applications with matching validation and encryption keys.
3. SSO for two applications in two sub-domains of the same domain
Now what if Foo and Bar are configured to run under different domains http://foo.com and http://bar.foo.com. The code above will not work because the cookies will be stored in different files and will not be visible to both applications. In order to make it work, we will need to create domain-level cookies that are visible to all sub-domains. We can’t use RedirectFromLoginPage method anymore, since it doesn’t have the flexibility to create a domain-level cookie. So we do it manually:
FormsAuthenticationTicket
fat = new FormsAuthenticationTicket(1, "johnd", DateTime.Now, DateTime.Now.AddYears(1), true, "");FormsAuthenticationTicket
fat = new FormsAuthenticationTicket(1, "John Doe", DateTime.Now, DateTime.Now.AddYears(1), true, "");Note the highlighted lines. By explicitly setting the cookie domain to ".foo.com" we ensure that this cookie will be visible in both http://foo.com and http://bar.foo.com or any other sub-domain. You can also specifically set the Bar authentication cookie domain to "bar.foo.com". It is more secure, since other sub-domains can’t see it now. Also notice that RFC 2109 requires two periods in the cookie domain value, therefore we add a period in the front – ".foo.com"
Again, make sure that you have the same <machineKey> element in web.config of both applications. There is only one exception to this rule and it is explained in the next secion.
4. SSO when applications run under different versions of .NET
It is possible that Foo and Bar applications run under different version of .NET. In this case the above examples will not work. It turns out that ASP.NET 2.0 is using a different encryption method for authorization tickets. In ASP.NET 1.1 it was 3DES, in ASP.NET 2.0 it is AES. Fortunately, a new attribute was introduced in ASP.NET 2.0 for backwards compatibility
<
machineKey validationKey="F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902" decryptionKey="F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902Setting decryption="3DES" will make ASP.NET 2.0 use the old encryption method, making the cookies compatible again. Don’t try to add this attribute to the web.config of the ASP.NET 1.1 application. It will cause an error.
5. SSO for two applications in different domains.
We’ve been quite successful creating shared authentication cookies so far, but what if Foo and Bar are in different domains – http://foo.com and http://bar.com? They cannot possibly share a cookie or create a second cookie for each other. In this case each site will need to create its own cookies, and call the other site to verify if the user is logged in elsewhere. One way to do it is via a series of redirects.
In order to achieve that, we will create a special page (we’ll call it sso.aspx) on both web sites. The purpose of this page is to check if the cookie exists in its domain and return the logged in user name, so that the other application can create a similar cookie in its own domain. This is the sso.aspx from Bar.com:
<%
@ Page Language="C#" %><
script language="C#" runat="server">void Page_Load() uri.Query = uri.Query + "&ssoauth=" + fat.Name; // add logged-in user name to the query
}
catch
{
}
}
Response.Redirect(uri.ToString()); // redirect back to the caller
}
</
script>This page always redirects back to the caller. If the authentication cookie exists on Bar.com, it is decrypted and the user name is passed back in the query string parameter ssoauth.
On the other end (Foo.com), we need to insert some code into the http request processing pipeline. It can be in Application_BeginRequest event or in a custom HttpHandler or HttpModule. The idea is to intercept all page requests to Foo.com as early as possible to verify if authentication cookie exists:
1. If authentication cookie exists on Foo.com, continue processing the request. User is logged in on Foo.com
2. If authentication cookie doesn’t exist, redirect to Bar.com/sso.aspx.
3. If the current request is the redirect back from Bar.com/sso.aspx, analyse the ssoauth parameter and create an authentication cookie if necessary.
It looks pretty simple, but we have to watch out for infinite loops:
// see if the user is logged in
if
(c != null && c.HasKeys) // the cookie exists!// the authentication cookie doesn't exist - ask Bar.com if the user is logged in there
if
(uri.Host != "bar.com" || uri.Path != "/sso.aspx") // prevent infinite loopThe same code should be placed on both sites, just make sure you are using correct cookie names (.FooAuth vs. .BarAuth) on each site. Since the cookie is not actually shared, the applications can have different <machineKey> elements. It is not necessary to synchronize the encryption and validation keys.
Some of us may cringe at the security implications of passing the user name in the query string. A couple of things can be done to protect it. First of all we are checking the referrer and we will not accept the ssoauth parameter from any source other then bar.com/sso.aspx (or foo.com/ssp.aspx). Secondly, the name can easily be encrypted with a shared key. If Foo and Bar are using different authentication mechanisms, additional user information (e.g. e-mail address) can be passed along similarly.
6. SSO for mixed-mode authentication (Forms and Windows)
So far we have dealt with Forms authentication only. But what if we want to authenticate Internet users via Forms authentication first and if it fails, check if the Intranet user is authenticated on the NT domain? In theory we can check the following parameter to see if there is a Windows logon associated with the request:
Request.ServerVariables["LOGON_USER"]
However, unless our site has Anonymous Access disabled, this value is always empty. We can disable Anonymous Access and enable Integrate Windows Authentication for our site in the IIS control panel. Now the LOGON_USER value contains the NT domain name of the logged in Intranet user. But all Internet users get challenged for the Windows login name and password. Not cool. We need to be able to let the Internet users login via Forms authentication and if it fails, check their Windows domain credentials.
One way to solve this problem is to have a special entry page for Intranet users that has Integrate Windows Authentication enabled, validates the domain user, creates a Forms cookie and redirects to the main web site. We can even conceal the fact that Intranet users are hitting a different page by making a Server.Transfer.
There is also an easier solution. It works because of the way IIS handles the authentication process. If anonymous access is enabled for a web site, IIS is passing requests right through to the ASP.NET runtime. It doesn’t attempt to perform any kind of authentication. However, if the request results in an authentication error (401), IIS will attempt an alternative authentication method specified for this site. You need to enable both Anonymous Access and Integrated Windows Authentication and execute the following code if Forms authentication fails:
if
(System.Web.HttpContext.Current.Request.SWhen this code executes, it will check the domain user and will get an empty string initially. It will then terminate the current request and return the authentication error (401) to IIS. This will make the IIS use the alternative authentication mechanism, which in our case is Integrated Windows Authentication. If the user is already logged in to the domain, the request will be repeated, now with the NT domain user information filled-in. If the user is not logged in to the domain, he will be challenged for the Windows name/password up to 3 times. If the user cannot login after the third attempt, he will get the 403 error (access denied).
Conclusion
We have examined various scenarios of Single Sign-On for two ASP.NET applications. It is also quite possible to implement SSO for heterogeneous systems spawning across different platforms. Ideas remain the same, but the implementation may require some creative thinking.
本文描述了如何搭建典型的Windows Mobile开发环境。介绍了相应的IDE、SDK及辅助开发工具的安装及常用配置,并对Windows Mobile下一代开发环境的发展前景做出展望。本文适合初次接触Windows Mobile开发的开发人员。
Windows Mobile是微软公司操作系统产品线上重要的一环。与Windows 9x/NT/XP/2003相比,Windows Mobile作为完整产品线出现在公众面前的时间非常短,但它的发展却异常迅速,而在这一发展过程
在展开本文的内容之前需要说明的一点是,Windows Mobile平台开发主要包含以下两方面内容:
l 平台级开发
l 应用级开发
因为Windows Mobile平台构建于微软的Windows CE系列操作系统之上(当前正式商用版本为Windows CE .NET 4.21),而Windows CE操作系统具有非常良好的模块化特性,因此开发人员,特别是手机设备厂商,可以通过微软
然而,绝大多数的开发人员并不需要去构建一个全新的、个性化的系统,他们只需要关心如
本文中提供的大量开发工具,除单独标出的以外,均可在http://msdn.microsoft.com/mobility/downl
为了进行Windows Mobile开发,我们需要搭建相应的开发环境。所幸的是,这一环境搭建起来非常简单。
为了降低Windows Mobile的开发难度,提高开发人员的开发效率,微软把.NET Framework移植到了Windows Mobile设备上,针对Windows Mobile设备及Windows CE .NET操作系统设计了.NET Compact Framework(以下简称.NET CF)。而Visual Studio .NET 2003正是开发.NET CF应用程序的最佳IDE。在Visual Studio .NET 2003的安装中默认集成了.NET CF 1.0,同时提供了Pocket PC 2002的开发模板,可以直接用来开发Pocket PC 2002应用程序。
您也可以选择Visual Studio 2005作为开发工具,目前最高版本为Beta 1 Refresh。在Visual Studio 2005中,默认提供了Pocket PC/Smartphone 2003 SE(第二版)的开发模板,同时将Pocket PC 2003 SE开发使用.NET CF版本升级到2.0 (beta)。
以上环境已经完全可以基于.NET CF开发Windows Mobile应用程序。但如果您想使用C/C++语言来进行native开发,或者想在Windows Mobile设备上开发COM组件及应用,那么可以选择eVC作为开发工具。目前eVC的最高版本为4.0 + SP4,请确保安装了最新的补丁包。
也许您会问:既然已经有了Visual Studio .NET环境,为什么还要使用eVC作为开发工具呢?这主要是因为,当前的.NET CF功能相对而言还很不完整,有很多Windows CE .NET操作系统的核心功能没有被封装入.NET CF,如:加密/解密、网络底层通讯、COM互操作、RAPI等等。为了完成上述操作,我们或者使用平台调用(P/Invoke)以托管方式开发,或者使用eVC直接针对系统底层进行native开发。对于COM互操作,目前.NET CF还不提供COM互操作(Interop)机制,只能使用eVC进行C/C++ COM开发。好消息是,在.NET CF 2.0中将提供功能强大的COM互操作特性支持,同时也将在Visual C++ 2005环境中同时提供managed及native开发功能,这样所有的开发工作将完全整合在Visual Studio 2005同一环境中。
为了开发Pocket PC 2003或Smartphone 2003应用程序,您还需要安装Pocket PC 2003 SDK或Smartphone 2003 SDK。值得注意的是,这两款SDK中所包含的模拟器均为英语环境,如果您想使用简体中文环境的模拟器,请去微软网站上单
如果您使用Visual Studio 2005开发Windows Mobile应用程序,则不需要单独安装相应的SDK,因为SDK已经被默认集成至了Visual Studio 2005环境中。
这是Pocket PC上推荐使用的嵌入式数据库产品,它与SQL Server桌面版之间可以实现良好的数据同步及互操作,目前正式版的最新版本为2.0。在SQL Server 2005 Beta 2测试版中,包含有SQL Server CE 2.0的升级版本SQL Server 2005 Mobile Edition——不过该版本目前只支持Pocket PC 2003系统。预计SQL Server Mobile Edition在下一个版本中才能提供对Smartphone的支持。
在完成了上述开发环境的搭建之后,您就可以在Visual Studio .NET中开发和调试应用程序,并在设备/模拟器中进行部署。但是,如果您还需要进一步执行下述操作,那么请按本节所述继续搭建ActiveSync环境。可能的执行操作包括(但不限于):
l 向设备/模拟器中复制文件
l 从设备/模拟器中删除文件
l 与设备/模拟器同步数据
l 开发和调试包含RAPI (Remote API)功能的应用
l ……
ActiveSync是一款管理Windows Mobile设备连接、与PC进行数据同步的工具。如果您开发时使用的是真实设备,则最好事先安装ActiveSync使之与PC机连接。如果您使用的是模拟器,则除了安装ActiveSync以外,还需要安装下述工具。
如果您使用的是模拟器软件,除了安装ActiveSync外,还必须安装Connect Emulator with ActiveSync这款工具软件。它将使您可以直接通过ActiveSync连接到模拟器上。
除了上述基本开发工具以外,在Windows Mobile开发社区中还存在着大量的辅助工具,包括应用程序框架、工具、控件等等。其中有一些由
由几位长年活跃于Windows Mobile开发者社区的MVP共同开发的OpenNETCF.org Smart Device Framework (以下简称为SDF),几天前刚刚获得了Pocket PC Magazine评出的2004年度最佳.NET开发框架大奖。熟悉SDF的开发者都会认为,对于这一奖项,OpenNETCF的确当之无愧,因为SDF几乎已经成了Windows Mobile开发者不可或缺的工具。他们对.NET CF的不足给出了及时的弥补,在SDF中,您可以找到Configuration、XML Serialization、 Notification、Interop Services、WSE 2.0等一系列.NET CF当前版本尚未提供的功能,以及诸如ListBoxEx等优秀的控件。更值得一提的是,SDF是一项完全免费的开源项目。这几位MVP为整个Windows Mobile开发者社区所做的贡献也许真的无法用金钱来衡量。
下载地址:http://www.opennetcf.org。
这是微软继Pocket PC/Smartphone 2003 SDK之后为开发人员提供的另一套开发辅助工具集。其中包含不少非常值得一用的优秀工具,如:
l ActiveSync Remote Display:把连接在PC上的智能设备影像通过PC屏幕放大显示
l CECopy:以命令行的方式向所连接的智能设备传输文件
l Hopper:用于Pocket PC 2003的用户输入压力测试工具
l PPC Command Shell:用于Pocket PC 2003的命令行工具
l TypeIt:用于Smartphone 2003的辅助文字输入工具
l ……
这是微软近期推出的Windows Mobile开发工具及资料集,其中包含上述所有产品及工具,以及一系列相关的文档、教程及参考资
好了,对于如何搭建Windows Mobile的开发环境就介绍到这里了。可以肯定地说,随着微软公司支持力度的增加和Windows Mobile开发者社区的成熟,Windows Mobile开发将面临越来越有利的良好环境。
Yes, the title may look like this post should be on an amateur blog and that it will be full of references to clip art and animated gifs, but this is serious. I’ve compiled a list of what I think are 25 ways to improve your website in as little time as possible. All can be done in a matter of minutes. Now, a website is hard work and usually there are no quick fixes but this list should provide you with a few pointers to make some updates today. If you like, it can also be used as a basis for a quality check document.
Because it matters. If people can’t use your site, they won’t stay.
Because you want to be found. (On-page tips only here)
Design is our passion. Let it show.
That’s why people visit the site.
Because it’s not all about you.
And there we go. I hope this list is useful and has shown you how easy it is to improve various aspects of your website and its marketing. This isn’t exhaustive by any means - as I said earlier it takes effort and time to really get your site near-perfect. Evolution is the key: tweak, feedback, measure and repeat. Oh, and before you go looking, no, I don’t always practice what I preach!