|
|
HOME > CHAPTER 18 > FREE
CHAPTER Windows authentication is perfect in intranet environments where everyone signs
on to the website with the same credentials used to log into Windows. For
sites meant for public viewing on the Internet, Windows authentication will
not usually be appropriate. Visitors will be prompted for their user name and
password and when they do not have an account in the specified domain (e.g.
guest users), they won't be able to access content. The good news is that there
are other ways to authenticate a user. ASP.NET supports at least two
alternatives. The first uses Microsoft Passport, which users may already use
to authenticate against other websites. In order to implement Passport, you
have to subscribe to its services, which can be too costly for a small
website. Another alternative is to use
Forms authentication, which is the topic of this chapter. Users enter
credentials into a form. The web application uses this information to decide
whether or not the user has access to the resource they are requesting.
Consider implementing Forms authentication when: ·
You have portions of the site that are open to
the public and portions that are meant only for a select group of people. ·
You have membership
details stored in an external database that contains the information required
for authentication. ·
When groups of users
do not have Microsoft Windows accounts. ·
When users have Windows accounts but are not
authenticated to the same domain as the MCMS server. About Forms Authentication
Forms authentication is a
non-Microsoft-specific mechanism, which existed before ASP.NET. In those
days, developers created custom forms consisting of a user name and password
field. When a user clicked on the Login button, the form triggered code that checked to see if
the user had access to the site. The result was usually stored as a token in
the form of a cookie or a session variable. The code that did the checking
involved quite a bit of logic. Not only did the developer need to ensure that
the code was triggered on all pages that required authentication, he or she
also had to manage the state of the token throughout the site. Today, with ASP.NET
technologies, you can find ready-to-use solutions that make implementing
Forms authentication much easier. MCMS has its own flavor of Forms
authentication in the form of its own set of classes in the Microsoft.ContentManagement.Web.Security namespace. Unlike ASP.NET Forms authentication, MCMS Forms authentication
performs the additional step of mapping users' credentials to the rights
groups they are assigned to.
As before, you still have to
design the form that contains the user name and password fields. However,
instead of building the logic for authenticating the user from scratch you
can make use of the classes provided by ASP.NET and MCMS to do the job for
you. How It Works
In order for Forms
authentication to work, IIS must allow anonymous access to resources.
Otherwise, Windows authentication would take over and visitors without valid
credentials would continue to get a network prompt for a user name and
password. Once anonymous access has been
enabled, all requests for a page get passed on by IIS to the ASP.NET web
application. The application checks to see if the page requires
authentication. Don't forget to ensure that the files in the file system do not have
restrictions that prevent the anonymous Internet user account from reading
them.
When guest access has been
enabled in MCMS and the guest account has been granted access to the page,
the application serves the page without displaying the login page. Should the
page require authentication, the application does another check to see if the
user has been previously authenticated by looking for a valid ticket. If a
valid ticket is not found, the user is redirected to a login form to enter a
user name and password. The logic to determine whether
a user has access to the requested MCMS objects is performed by the Microsoft.ContentManagement.Web.Security.CmsAuthorization module. The user is granted access to a page only if: ·
A valid user name and password have been
entered. ·
The user belongs to
the group of users specified in the <authorization> tag of the application's Web.config file. ·
The user belongs to
a subscriber/author/editor/moderator/template designer/channel
manager/administrator rights group assigned to the channel of the requested
page. ·
Guest access has
been enabled and the guest account has been granted access to the requested
page. Once the user has been successfully
authenticated, a ticket is issued in the form of two cookies—an ASP.NET
session cookie and an MCMS session cookie. The cookies are set on the client
machine. In subsequent attempts to access other pages on the site, the
browser will automatically send these cookies to the server. The ASP.NET web
application checks the cookies to see if the associated ticket is still
valid. A table that compares different authentication mechanisms for ASP.NET
is available at http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnnetsec/html/SecNetAP05.asp.
Like regular cookies, both
cookies have a lifespan. Through code, you can choose to set a temporary
cookie or a persistent cookie. Temporary cookies last only for the duration
of the browser session. When the browser is closed, the temporary cookie is
removed. Persistent cookies can endure across different browser sessions. It
is the responsibility of the server to check any persistent or temporary
cookie to see if it is still valid. The ASP.NET cookie has the lifespan
specified in the <forms> tag
in the Web.config file. The
MCMS cookie is kept alive for the duration of time specified in the SCA. Once
either cookie expires or is deleted, the user will be directed back to the
login page to get authenticated again when a page is next requested. Since cookies are required for Forms authentication to work, the
user's browser must allow cookies for the site.
To implement Forms
authentication, you would need to go through the following steps: 1.
Configure IIS to
allow anonymous access for the web application. 2.
Configure the Web.config file of the application. 3.
Create the login
page. If you have portions of the
site open for public viewing where it is not appropriate to authenticate
users, you would need to perform these additional steps: 4.
Choose or create an
MCMS Guest account. 5.
Turn on Guest access
for the site using the SCA. 6.
Assign the Guest
account subscriber rights to channels meant for public viewing. We will perform all these
tasks to implement Forms authentication for the TropicalGreen site. Configuring IIS to Allow Anonymous
Access
Open Internet Services Manager by selecting Start |
Programs | Administrative Tools | Internet Services Manager. In the tree on the left pane, browse to the
TropicalGreen virtual directory (e.g. Computer Name | Default Web Site |
TropicalGreen). Right-click on the node that
represents your website (e.g. the Default Web Site). Select Properties from the pop-up menu. The Properties dialog appears. Select the Directory
Security tab and in the section
labeled Anonymous access and authentication control, click the Edit… button. The Authentication Methods
dialog opens. In the Anonymous access section, ensure that the Anonymous
access option is checked. For Windows 2000, click the Edit… button in the Anonymous access section to view details of the anonymous user account.
Ensure that the properties of the anonymous account are set as follows:
These are the default settings
of IIS. Unless these settings have been changed, you should not need to set
them. With Internet Services Manager
open, right-click on the TropicalGreen virtual directory. Repeat the steps above to ensure that
anonymous access is configured for that virtual directory. When you are done, close all
open dialog boxes by clicking their OK
buttons. With IIS configured for
anonymous access, all requests for pages of the TropicalGreen website will be
passed on to ASP.NET. Configure Settings in the
Web.config File
Next we configure the Web.config file of the TropicalGreen project to use Forms
authentication. Open the Web.config file of the TropicalGreen project. Look for the <authentication> tag. By default, it is set to use Windows
authentication. Comment out the line <authentication
mode="Windows" /> and add in the code highlighted below to change the
authentication mechanism to Forms. <!-- AUTHENTICATION
This section sets the authentication policies of the application.
Possible modes are "Windows", "Forms",
"Passport" and "None" --> <!-- authentication
mode="Windows" / --> <authentication
mode="Forms"> </authentication> Between the <authentication> tags, add the <forms> tag. Here, we name the cookie TropicalGreenAuthCookie, set the login form path to /tropicalgreen/Login.aspx (we will build this later), configure the protection
level to all and the cookie
timeout period to 30 minutes. <forms
name="TropicalGreenAuthCookie"
path="/"
loginUrl="/tropicalgreen/Login.aspx"
protection="All"
timeout="30"> </forms> The cookie timeout must be set
to the Cookie Lifetime in
minutes set in the SCA. The default setting is 30 minutes. If the two values
do not match, you may run into problems when one cookie expires before the
other. For a detailed discussion of what each attribute in the <form> tag means, check out the Microsoft online documentation
available at http://www.microsoft.com/resources/documentation/iis/6/all/proddocs/en-us/aaconformselement.mspx.
Directly below the closing </authentication> tag, add the <authorization> tag (if it does not already exist). We will add the <allow> tag with the users attribute set to *
to allow all users to access the site. <authorization> <allow
users="*"></allow> </authorization> Finally, check that the <httpModules> section contains a reference to the Microsoft.ContentManagement.Web.Security.CmsAuthorizationModule DLL. This library is automatically added to the Web.config file if you created the project using the wizard or if
the project is MCMS-enabled. It contains classes used to map authenticated
users to the MCMS rights groups they are assigned to. You can remove the
module only when all of the following is true: ·
You have enabled
guest access. ·
You only expect
guests to view pages on the site. The entire site—all its channels, postings,
templates and resources—are available to guests, and there is no need for any
kind of authentication. <httpModules> <add
type="Microsoft.ContentManagement.Web.Security.CmsAuthorizationModule,
Microsoft.ContentManagement.Web,
Version=5.0.1200.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35"
name="CmsAuthorizationModule" /> . . . code continues . . . </httpModules> Save and close the Web.config file. Creating the Login Page
Once you have configured the TropicalGreen site to use
Forms authentication, the first time you view any page within the site, you
will be redirected to the form located at /tropicalgreen/login.aspx. However, that file does not exist yet! Let's
create it. With the TropicalGreen solution
open in Visual Studio .NET, add a new web form to the root of the
TropicalGreen project and name it Login.aspx. Set the Page Layout to FlowLayout. Toggle to HTML view. Between the <head> tags add a link to the stylesheet created in Chapter 7. <LINK
href="/tropicalgreen/Styles/Styles.css"
type="text/css" Between the <form> tags, add a table consisting of descriptive labels,
textboxes for entering the domain, user name, and password, and a sign in
button. <div align="center"> <table> <tr> <td
colspan="2">
<h1>Enter your Username and Password</h1>
<hr noshade> </td> </tr> <tr> <td
class="BodyText">Domain:</td> <td
class="BodyText">
<asp:TextBox Runat="server" ID="txtDomain"
Text="">
</asp:TextBox> </td> </tr> <tr> <td
class="BodyText">Username:</td> <td
class="BodyText">
<asp:TextBox runat="server" ID="txtUsername">
</asp:TextBox> </td> </tr> <tr> <td
class="BodyText">Password:</td> <td
class="BodyText">
<asp:TextBox Runat="server" ID="txtPassword"
TextMode="Password">
</asp:TextBox> </td> </tr> <tr>
<td></td> <td>
<asp:Button Runat="server" ID="btnSignIn"
Text="Sign In"></asp:Button> </td> </tr> <tr> <td
class="BodyText" colspan="2">
<asp:Label Runat="server" ID="lblErrorMessage"
ForeColor="red">
</asp:Label> </td> </tr> </table> </div> Toggle to Design view. The form appears as shown below. While in Design view, double-click on the form to get to the code-behind
file. Import the Microsoft.ContentManagement.Web.Security namespace. This namespace contains the classes we will
use to process the logic required to authenticate the user using Forms
authentication. Also import the Microsoft.ContentManagement.Publishing namespace. using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using
System.Web.UI.HtmlControls; using
Microsoft.ContentManagement.Web.Security; using
Microsoft.ContentManagement.Publishing; namespace TropicalGreen {
. . . code continues. . . } Toggle back to Design view and double-click on the btnSignIn button. In the btnSignIn_Click() event handler we will string together the user name and
domain in the format winNt://(domain)/(username) to form the MCMS user name. Next, we will use the CmsFormsAuthentication.AuthenticateAsUser() method to get an authentication ticket from MCMS. If the
user is not a valid MCMS user, the ticket will be null. Any error messages
are written to the lblErrorMessage
label. Add the following code within the btnSignIn_Click() event handler: private void
btnSignIn_Click(object sender, System.EventArgs e) { string
username; string
domain; string
password; string
user; try {
username = txtUsername.Text;
domain = txtDomain.Text.ToUpper();
password = txtPassword.Text;
//string the domain and userName together to get the user's account
user = "winNt://" + domain + "/" + username;
//get a ticket
CmsAuthenticationTicket
ticket=CmsFormsAuthentication.AuthenticateAsUser
(user,password); }
catch(Exception ex) {
lblErrorMessage.Text = ex.Message; } } Once the user has been
validated, we will use the RedirectFromLoginPage() method to set the cookie and send the user back to the
page he or she was previously viewing. A ReturnUrl querystring is appended to the URL when the user is
first brought to the Login page and stores the address of the page the user
requested. The RedirectFromLoginPage() method uses the URL stored in the ReturnUrl query string to decide which page to redirect to. Add
code in the btnSignIn_Click() event
handler as shown below. private void
btnSignIn_Click(object sender, System.EventArgs e) { . . . code
continues . . . try { . . . code
continues . . . //get a
ticket
CmsAuthenticationTicket
ticket=CmsFormsAuthentication.AuthenticateAsUser
(user,password); if(ticket!=null) {
if(Request.QueryString["ReturnUrl"]!=null) {
CmsFormsAuthentication.RedirectFromLoginPage(ticket,true,false); } }
//the redirection did not occur, the user does not have access
//to the page
lblErrorMessage.Text = "Access denied";
} catch(Exception ex) {
lblErrorMessage.Text = ex.Message; } } The RedirectFromLoginPage() method does more than redirection. It accepts three
parameters: ·
authenticationTicket is the ticket used to generated the cookies. ·
setAspNetCookie
decides whether or not an additional ASP.NET cookie will be created. The
ASP.NET cookie contains additional information that is not found in the MCMS
cookie. ·
createPersistentCookie decides whether or not the cookie will persist across
browser sessions. If it is set to false, when the user closes and re-opens
the browser or opens another window, he or she will get the Login page again.
Once the ticket lifetime as set in the SCA has elapsed, even if createPersistentCookie is set to true, the user will be requested to login
again. We are not using this feature, but you can use it to save the login
for your users with some tweaking of the code. With this information, a
temporary cookie is set in the memory of the browser. Persistent cookies are
stored in the file system. Next, we will consider the
case where the user navigates to http://localhost/tropicalgreen/login.aspx. Unlike the previous case, the ReturnUrl parameter has not been set and therefore cannot be used. To handle this situation we
will use the SetAuthCookie() method.
This method is similar to the RedirectFromLoginPage() method, with the main difference being that the SetAuthCookie() method only sets the cookie based on the ticket. It does
not perform any redirections. Add the condition to consider
the case where a ReturnUrl query
string is not found. In this case, the authentication cookies will be created
by SetAuthCookie(). We then
explicitly call the Response.Redirect() method to bring the user to the TropicalGreen home page.
private void
btnSignIn_Click(object sender, System.EventArgs e) { . . . code
continues . . . try { . . . code
continues . . .
if(ticket!=null) {
if(Request.QueryString["ReturnUrl"]!=null)
{
CmsFormsAuthentication.RedirectFromLoginPage(ticket,true,false);
}
else
{
CmsFormsAuthentication.SetAuthCookie(ticket,true,false);
Channel root = CmsHttpContext.Current.Searches.GetByPath
("/Channels/TropicalGreen") as Channel;
if(root!=null)
{
Response.Redirect(root.Url);
}
}
} .
. . code continues . . . } . . . code
continues . . } The SetAuthCookie() method is typically used when you need to suppress the
redirection. The Login page is complete. Save and build the solution. Close login.aspx and its code-behind file. Since the Login page could be processed before the user has been
authenticated by MCMS, ensure that the Login page: 1. Is not a posting based on
a template. 2. Does not access any CmsHttpContext members, including references to channels, Logging on to the Site
Open Internet Explorer and navigate to http://localhost/tropicalgreen/plantcatalog. You should see the login box shown in the following
screenshot. Look at the address bar in
Internet Explorer and you will see that the URL of the login page has been
appended with a querystring holding a value for a parameter called ReturnUrl. The value stored in the ReturnUrl querystring is a URL-encoded path back to the page that
you have requested to see. If you have set up MCMS to work within a single domain, consider
storing the domain name in a centrally managed location such as an
application setting in the Web.config file. In this way, you can retrieve its value when
calling the CmsFormsAuthentication
Enter your administrative user
name and password. For example:
If you have entered valid
credentials, you will be brought to the Plant Catalog page. Click on any of
the plant fact sheets. Notice that you are not asked to enter your
credentials again: You will remain authenticated for as long as the cookie
lasts. The CmsFormsAuthentication Class
In the example above, we used the CmsFormsAuthentication.AuthenticateAsUser() method to get the authentication ticket. There are other
ways to retrieve this ticket, detailed next. ·
AuthenticateAsCurrentUser()
logs in with the credentials of the current Windows
user. Using this method, you do not have to ask for user names and passwords
as these are already obtained from the WindowsIdentity.GetCurrent() method. However, because you have configured IIS to
allow anonymous access to the site, you will always be using the credentials
of the ASPNET account. ·
AuthenticateAsGuest() logs in with the credentials of the MCMS guest
account. If guest account is enabled, the user will be issued a ticket that
gives access to portions of the site that are open to guests. ·
AuthenticateUsingWindowsToken()
logs in with the credentials of a Windows user. It
is usually used when impersonating a logon session with a Windows account. ·
AuthenticateAsUser() is the method used in the example above. With this
method, you specify the domain, username and password of the user. The format
of the user account is WinNT://Domain/Username. In all these methods, you can
choose to pass in optional parameters defining the client account name and
client account type. For example, we can specify our own custom values for the
user's client account name and client account type by calling: CmsFormsAuthentication.AuthenticateAsUser
(user,password,"MyAccountName1","MyAccountType1"); In this case, User.ClientAccountName has the value MyAccountName1. User.ClientAccountType holds the value MyAccountType1. This opens the possibility of having multiple users
using the same Windows account but mapped to different client accounts. What's in the Cookie
You can see the contents of the cookie by using the GetAuthCookie() method as shown below: HttpCookie cook =
CmsFormsAuthentication.GetAuthCookie(ticket,true); Response.Write(cook.Value.ToString()); You will get values such as
this: zne2e42rfjou3mlrhrmpad4opbt2xacv2rq5gm4bctx77slym24ntq5ii3f6ybohuoklqpwxf3g4j This does not tell you much because the contents have been encrypted. Be assured that the cookie only contains only information that is required for the RedirectFromLoginPage(), SetAuthCookie(), and GetAuthCookie() methods to work. It does not contain sensitive information like the Windows token and password, or other information that may compromise the security of the system. When Users Do Not Have Rights to
View the Requested Page
Should you visit a page you have no access to, you would
be returned to the login page. Unless you enter the credentials of a user who
has the rights to view the page, you will keep being returned to the login
page. To provide a way out, it is a good idea to add a hyperlink that leads
the visitor from the login page to the referring page or at least to a part
of the site that is accessible. With Login.aspx opened in Design view, drag and drop a Hyperlink control from the Web
Forms Toolbox onto the form as shown in the following figure: Give the Hyperlink the
following properties:
|