Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]
.pdf
798 C H A P T E R 2 3 ■ A U T H O R I Z AT I O N A N D R O L E S
The application defines application-specific roles that are typical for a collaborative portal solution. You can assign both Windows users and Windows groups to these roles. SharePoint by default includes the roles Administrator, Web Author, Designer, and Reader. All of these roles are optimized for performing authorization within the portal. For example, while a Web Author automatically gets permission to create new workspaces for meetings and structure contents displayed on the portal, a Reader is just able to view information on the portal. Every Windows user assigned to one of these roles, or every Windows user who is a member of a Windows group assigned to one of these roles, automatically gets the appropriate permissions. Therefore, SharePoint is independent of the network structure deployed in the Windows network where it is used. You will learn more details about implementing such concepts in your own application in Chapter 26 where you will learn details about custom Membership and Roles providers.
Protecting Non-Web-Page Resources
All the authorization and authentication systems you’ve about learned so far have one limitation— they work only on file types that ASP.NET handles. In other words, if a user requests a GIF file or HTML page from the same virtual directory, that user will completely bypass your authentication and authorization mechanisms. Depending on your security needs, this may be completely acceptable, simply irrelevant, or potentially dangerous.
This behavior is a result of the way IIS uses file mappings, which were first covered in Chapter 18. By default, ASP.NET is registered to deal with a small set of relevant files. These include files it needs to execute, such as web pages and web services, and files it wants to protect, such as source code files, configuration files, and project files. If you want requests for other file types to filter through the ASP.NET request processing architecture and security models, you have to map those file types to the ASP.NET ISAPI filter.
Mapping additional file types to ASP.NET gives you some extra features. For example, it gives you the ability to deny anonymous user requests for image files. However, it can also add overhead, because files that would normally be served directly now require ASP.NET to perform some work. This overhead is fairly minimal if you aren’t expecting users to request non-ASP.NET file types and if you’re simply using this technique to provide a higher level of security.
Furthermore, if you map a file type with the ASP.NET runtime, you have to tell the runtime how it should process this file type. You can do this by creating your own HTTP handler, as you will learn later in this chapter.
■Note If you’re using Windows authentication, it is technically possible to force IIS to authenticate all requests, even those that are for non-ASP.NET files. To do so, you simply need to remove the Anonymous access option for the virtual directory. However, this option isn’t as useful as it seems, because it doesn’t allow you to enforce authentication for specific files or file types. As a result, you may find it more useful to allow anonymous access but use the techniques described in the following sections to protect specific resources.
Adding a File Type Mapping
To add a file type mapping to IIS 5, follow these steps:
1.Launch IIS Manager, and browse to the virtual directory in the tree.
2.Right-click the virtual directory, and select Properties.
3.Choose the Virtual Directory tab. Then click the Configuration button in the Application section of the Virtual Directory tab. The Application Configuration dialog box will appear, as shown in Figure 23-7.
C H A P T E R 2 3 ■ A U T H O R I Z AT I O N A N D R O L E S |
799 |
Figure 23-7. Application mappings
4.You need to add a new mapping for each file type you want to protect with forms authentication. This mapping will route requests for that file type to the ASP.NET ISAPI DLL. Click the Add button to create a new mapping. You’ll see the dialog box shown in Figure 23-8.
Figure 23-8. Adding an application mapping
5.The executable you want to use is aspnet_isapi.dll. The exact directory depends on the version of ASP.NET you have installed. In .NET 2.0, it’s c:\[WinDir]\Microsoft.Net\Framework\ v2.0.50215\aspnet_isapi.dll. You also need to enter the file extension you want to map.
Finally, you should also specify that you want to perform this mapping for all verbs (a verb is a method for requesting the file from the server over HTTP, such as GET or POST).
6.Once you have taken these steps, click OK to add the extension.
800 C H A P T E R 2 3 ■ A U T H O R I Z AT I O N A N D R O L E S
Writing a Custom HTTP Handler
Every resource processed by ASP.NET is processed by an actor called an HTTP handler. For example, web pages with the extension .aspx are processed by a page handler, while web services with the extension .asmx are processed by a SOAP handler. All these classes are implementations of the IHttpHandler interface.
When you associate your custom file type with the ASP.NET runtime, as shown in the previous section, you have to tell ASP.NET how to process this resource. The way to do this is to write a custom HTTP handler class that implements this interface. A custom handler processing any type of binary file looks like this:
namespace RolesDemo.Handlers
{
public class GenericHandler : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
byte[] ret = null;
// Open the file specified in the context
string PhysicalPath = context.Server.MapPath(context.Request.Path); using (FileStream fs = new FileStream(PhysicalPath, FileMode.Open))
{
ret = new byte[fs.Length]; fs.Read(ret, 0, (int)fs.Length);
}
// If it is not null, return the byte array if (ret != null)
{
context.Response.BinaryWrite(ret);
}
}
#endregion
}
}
This handler simply determines the local physical path of the resource requested by calling Server.MapPath. Afterward it uses a FileStream for opening the resource and returning the bytes included for this resource. You have to configure this HTTP handler as well. For this purpose, you just add a <httpHandlers> section within the <system.web> section of your web.config application configuration, as follows:
<httpHandlers>
<add verb="GET,POST" path="*.txt"
type="RolesDemo.Handlers.GenericHandler"/>
</httpHandlers>
C H A P T E R 2 3 ■ A U T H O R I Z AT I O N A N D R O L E S |
801 |
PROBLEMS WITH SOME FILE TYPES
Developers have reported some problems when using forms authentication to protect Adobe Acrobat (PDF) files. It’s possible that similar problems could affect other file types, especially if they require a web browser plug-in to be displayed.
With PDF files, the problems are caused by a combination of the ActiveX component that allows Internet Explorer to view Acrobat files and IIS. The problem is that PDF files are sent from the server to the client in chunks so that the user does not have to wait until the whole file has downloaded to start viewing it. For some reason, this system is adversely affected by redirections (for example, using the Response.Redirect() method). Redirecting to a PDF file causes the file to be reported as corrupted. This creates problems if you try to use forms authentication to protect PDF files. After the user logs in, the redirection back to the original PDF causes the file to be reported as corrupted or simply not displayed.
To solve this problem, you need to avoid using FormsAuthentication.RedirectFromLoginPage() or Response.Redirect() to send the user back to the PDF file. Fortunately, you simply need to write an HTML page that instructs the browser to redirect itself using the Response.AppendHeader() method. This header has the name refresh and takes the form 0;url=[originalUrl]. This causes the browser to immediately load the target URL (the 0 indicates a delay of 0 seconds).
Here’s the code statement you need to use instead of the RedirectFromLoginPage() method:
Response.AppendHeader("refresh","0;url=" + url);
Remember that because you aren’t using the RedirectFromLoginPage() method, you’ll also need to create and attach the cookie before you perform the redirect.
The type attribute includes the full namespace and class name of the IHttpHandler implementation, and optionally if it is placed in a different assembly, you have to specify the name of the assembly in the format “namespace.typename, assembly” within it. The additional attributes specify the HTTP verb (GET, PUT, POST, or * for all) as well as the path and file types for which the handler will be used.
Summary
Authorization provides an effective way to control access to resources. In this chapter, you learned how to safeguard different pages, directories, and code routines in your web application using authorization. You also saw how to use the Roles Service for managing and associating users with roles for simpler authorization.
In the next chapter, you’ll take a look at a few advanced security techniques that you can use to extend ASP.NET authentication and authorization.
804C H A P T E R 2 4 ■ P R O F I L E S
Unfortunately, many features that seem convenient suffer from poor performance or scalability. This is particularly a concern with profiles, because they involve database access, and database access can easily become a scalability bottleneck for any distributed application.
So, do profiles suffer from scalability problems? This question has no simple answer. It all depends on how much data you need to store and how often you plan to access it. To make an informed decision, you need to know a little more about how profiles work.
Profiles plug into the page life cycle in two ways:
•The first time you access the Profile object in your code, ASP.NET retrieves the complete profile data for the current user from the database. From this point onward, you can read the profile information in your code without any database work.
•If you change any profile data, the update is deferred until the page processing is complete. At that point (after the PreRender, PreRenderComplete, and Unload events have fired for the page), the profile is written back to the database. This way, multiple changes are batched into one operation. If you don’t change the profile data, no extra database work is incurred.
■Note Profile reading and saving is implemented by a dedicated ProfileModule, which runs during each request. Chapter 5 discusses HTTP modules in more detail.
Overall, the profiles feature could result in two extra database trips for each request (in a readwrite scenario) or one extra database trip (if you are simply reading profile data). The profiles feature doesn’t integrate with caching, so every request that uses profile data requires a database connection.
From a performance standpoint, profiles work best when the following is true:
•You have a relatively small number of pages that access the profile data.
•You are storing small amounts of data.
They tend to work less well when the following is true:
•You have a large number of pages that need to use profile information.
•You are storing large amounts of data. This is particularly inefficient if you need to use only some of that data in a given request (because the profile model always retrieves the full block of profile data).
Of course, you can combine profiles with another type of state management. For example, imagine your website includes an order wizard that walks the user through several steps. At the beginning of this process, you could retrieve the profile information and store it in session state.
You could then use the Session collection for the remainder of the process. Assuming you’re using the in-process or out-of-process state server to maintain session data, this approach is more efficient because it saves you from needing to connect to the database repeatedly.
How Profiles Store Data
The most significant limitation with profiles doesn’t have anything to do with performance— instead, it’s a limitation of how the profiles are serialized. The default Profiles provider included with ASP.NET serializes profile information into a block of data that’s inserted into a single field in a database record. For example, if you serialize address information, you’ll end up with something like this:
Marty Soren315 Southpart DriveLompocCalifornia93436U.S.A.
806 C H A P T E R 2 4 ■ P R O F I L E S
Profiles vs. Custom Data Components
Profiles are a natural competitor with custom data components of the kind you saw in Chapter 8. Clearly, data components are far more flexible. They allow you not only to maintain user-specific information but also to store other types of information and perform more complex business tasks.
For example, an e-commerce website could realistically use profiles to maintain customer address information (with the limitations discussed in the previous section). However, you wouldn’t use a profile to store information about previous orders. Not only is it far too much information to store efficiently, it’s also awkward to manipulate.
■Tip As a rule of thumb, use a profile to store only the same sort of information you’d place in the user table. Don’t use it to store related data that you’d place in separate tables.
The standard Profiles provider that’s included with ASP.NET (named SqlProfileProvider) doesn’t provide many additional features. The following list includes some features that you can easily add through a custom database component but aren’t available if you’re using the SqlProfileProvider.
If you need any of these features, you’ll need to abandon profiles and create your own data access component, or you’ll need to design a custom Profiles provider.
Encryption: Profile data can be serialized into a string, XML, or a binary representation. But no matter what you choose, you’ll always end up storing the raw text. If you have sensitive information, your only option is to encrypt it manually before you store it, which has the undesirable result of putting encryption logic in your user interface code.
Validation: You can’t restrict the type of information that can be placed in a profile. You need to use other tools (such as validator controls and custom data classes) to prevent invalid data.
Caching: If profile information is used in a page, it’s always retrieved from the database. You can’t keep profile information around in memory. Although you can copy profile information into the cache, it becomes more difficult to track this information.
Auditing: When you design a custom database component, you have the ability to add any logging or tracing code you want. You can use this to diagnose unexpected errors or monitor the performance of your web application. However, if you want these features with profiles, you’ll need to build a custom Profiles provider that has the logging code.
Now that you know the ins and outs of profiles, you’re ready to try them.
Using the SqlProfileProvider
The SqlProfileProvider allows you to store profile information in a SQL Server 7.0 or later database. You can choose to create the profile tables in any database. However, you can’t change any of the other database schema details, which means you’re locked into specific table names, column names, and serialization formats.
From start to finish, you need to perform the following steps to use profiles:
1.Create the profile tables.
2.Configure the provider.
3.Define some profile properties.
4.Enable authentication for a portion of your website.
5.Use the profile properties in your web-page code.
C H A P T E R 2 4 ■ P R O F I L E S |
807 |
You’ll tackle these steps in the following sections.
Creating the Profile Tables
To create the profile tables, you use the aspnet_regsql.exe command-line utility, the same tool that allows you to generate databases for other ASP.NET features, such as SQL Server–based session state, membership, roles, database cache dependencies, and web parts personalization. You can find the aspnet_regsql.exe tool in the c:\[WinDir]\Microsoft.NET\Framework\[Version] folder.
To create the tables, views, and stored procedures required for profiles, you use the -A p command-line option. The only other detail you need to supply is the server location (-S), database name (-d), and authentication information for connecting to the database (use -U and -P to supply a password and user name, or use -E to use the current Windows account). If you leave the other server location and database name, aspnet_regsql.exe uses the default instance on the current computer and creates a database named aspnetdb.
Here’s an example that creates the aspnetdb database with the default name on the current computer by logging into the database using the current Windows account:
aspnet_regsql.exe –A p –E
Table 24-1 shows the tables that aspnet_regsql.exe creates. (The rather unexciting views aren’t included.)
Table 24-1. Database Tables Used for Profiles
Table Name |
Description |
aspnet_Applications |
Lists all the web applications that have records in this database. It’s |
|
possible for several ASP.NET applications to use the same aspnetdb |
|
database. In this case, you have the option of separating the profile |
|
information so that it’s distinct for each application (by giving each |
|
application a different application name when you register the |
|
Profiles provider) or of sharing it (by giving each application the |
|
same application name). |
aspnet_Profile |
Stores the user-specific profile information. Each record contains the |
|
complete profile information for a single user. The PropertyNames |
|
field lists the property names, and the PropertyValuesString and |
|
PropertyValuesBinary fields list all the property data, although you’ll |
|
need to go to some work if you want to parse this information for use |
|
in other non-ASP.NET programs. Each record also includes the last |
|
update date and time (LastUpdatedDate). |
aspnet_SchemaVersions |
Lists the supported schemas for storing profile information. In the |
|
future, this could allow new versions of ASP.NET to provide new ways |
|
of storing profile information without breaking support for old profile |
|
databases that are still in use. |
aspnet_Users |
Lists user names and maps them to one of the applications in |
|
aspnet_Applications. Also records the last request date and time |
|
(LastActivityDate) and whether the record was generated |
|
automatically for an anonymous user (IsAnonymous). Anonymous |
|
user support is discussed later in this chapter (in the section |
|
“Anonymous Profiles”). |
|
|
