How to Use Basic Unified Login
Draft 0.3
Dec 22, 2000
Ted Lee (tlee@startups.com)
1. Applicability
Read this document when any of the following are true:
-
You want to make sure a Startups.com user has logged in before accessing
a page in the site.
-
You want to make sure a Startups.com user has permission to access a page
in the site.
If this document doesn't meet your needs, you may want to look at:
Background
There are certain sections of our site which require a user to log in prior
to accessing the page. In order to log in, a user must provide a
username and a password. Users may obtain usernames and passwords
in several areas on the site. For instance, it is possible to sign
up as a registered client to obtain a username and password, or one can
sign up as a service provider to obtain a username and password.
It is undesirable to require a user to obtain separate usernames and passwords
to access each section of our site, so instead we introduce the concept
of roles.
A user role is added to a user when the user signs up for a particular
section of the site. For example, a new user signing up as a client
will obtain a username, a password, and internally will be assigned a client
role. Roles may be added to existing username/password pairs if the
user signs up for other sections of the site. For example, a user
who is already a client may sign up as a service provider; in this case,
the user would have a single username and password but would have two roles
assigned to her--client role and service provider role.
Certain sections of the site require the user have a particular role
in addition to being logged in. Some pages require not only the presence
of a particular role but also the presence of certain permissions within
that role.
The desired effect of using unified login is that when a user tries
to access a page which requires login, a login screen will appear.
If the login is successful and the user has the appropriate roles and permissions
required for that page, the user will be able to access the page.
A flowchart of the desired behavior is shown below:
2. Steps for Using Unified Login
In order to require login to access a page, you need to override one or
more methods in the Model
object. If you don't know what a Model object is, please
see How To Create Web Pages With
Forms (Java Developer). Model defines a number of methods
that can be usefully overridden to change access protection behavior:
These methods will be explained in more detail below.
2.1 Specify what role is required to access a given page
Model defines a method called getRequiredRole() which
returns a Class object. By default it returns null.
Override this method to return the Class object for the role that
is required to access the page. For example, if JobBoardRole
is required to access the page, you would implement the method like this:
public Class getRequiredRole() {
return JobBoardRole.class;
}
Note that in order for the role to be checked, the user must be logged
in. In effect, specifying a required role always specifies that the
user must be logged in.
2.2 Specify the Login Page Text and Title
By default, a generic login page will appear if the framework determines
that the user does not have the required role. If you wish the login
page to say something specific, like "In order to access the Job Connection,
please log in", you must override the getLoginPageMessage() method.
You may also specify the title of the page by overriding getLoginPageTitle():
public String getLoginPageMessage() {
return "In order to access the Second Page you
must first log in.";
}
public String getLoginPageTitle() {
return "Log in to Second Page";
}
2.3 Specify the success URL
If the user successfully logs in and has the required role, by default
the user is sent to the page they were originally trying to access.
If this behavior is adequate, go on to the next step. In some cases,
it may be desirable to send the user to a specific page rather than the
page they tried to access. For example, if the user tries to enter
step 3 of a 5 page application by typing in the URL for page 3 in their
browser, we might want to send them to step 1 regardless of the fact that
they were trying to go to step 3. For example,
public String getAccessSuccessfulURL() {
return "/jobboard/index.jsp";
}
2.4 Specify the permission denied URL
It is possible to have a user log in successfully but not have the role
required to access a page, or if they have the role they may not have the
required permission. In this situation, the user should be sent to
a different page. Typically it would be a signup page. For
example:
public String getPermissionDeniedURL() {
return "/jobboard/freeposting/SingleFreePosting.jsp";
}
By default, the behavior is to show a page the just lists the roles
that the user has:
In most situations this is not the desired behavior, so it will usually
be the case that the getPermissionDeniedURL() should be overridden.
Tip:
If you have many pages with the same access protection, create an abstract
subclass of Model, override the methods discussed above, and extend
the subclass instead of Model for individual pages. For
example:
2.5 Add an Access Check Into the JSP View
In the jsp file corresponding to the Model that you just modified, add
a little jsp scriptlet just after the usebean declaration. For example:
<jsp:useBean id="thirdPageModel" class="com.startups.testunifiedlogin.ThirdPageModel"
scope="session" create="yes"/>
<%
thirdPageModel.checkAccess(request, response);
%>
This scriptlet turns on all of the access checking that you added to
the Model for the page.
3. Design Discussion
The goal of the unified login framework is to support the following behavior:
-
Support login at a framework level so we don't have to code many different
login pages.
-
Be able to protect areas of the site so that if a user types in the URL
of a protected page they will be forced to log in before being able to
access it.
-
Be able to specify which roles are required to access a page on a page-by-page
basis.
-
Be able to specify the URL to send the user to upon successful login.
This may not be the page that the user attempted to access (for example,
if they tried to jump into the middle of a 3-page form it should send them
to the first page).
-
Be able to specify the URL to send the user to if a required role is missing
but the username and password match. This might be a signup page, for instance.
-
Be able to specify a message that will appear on the login page, so that
instructions or explanation can be given to the user (e.g. "In order to
access job connection, you need to first log in").
-
Not require the implementor to be aware of any http session user variables
-
Check for the presence of the Startups.com premium content cookie on each
login, and add the cookie if not present
We have chosen to make the implementors of Model objects (from the MVC
pattern) responsible for adding access permissions to the page models.
We could have made a centralized lookup table of permissions and pages,
but someone would have to maintain it and we would have needed a separate
maintenance application. It seems reasonable that the implementor
of a page and its logic should know what permissions are necessary to access
that page. The drawback is that changing the permissions for a page
requires a recompile.
Role object granularity
Multiple roles vs multiple permissions in a single role. Most of
the time it seems as though multiple permissions in a role is overkill
and confusing--when do you decide to make a permission vs another role?
However, one clear case where it seems that permissions would be useful
is in admin roles. We could go crazy with permissions hierarchies
but we chose to go with simple string permissions. We've decided
to not initially offer individual permissions in roles but we've left the
door open for extension by allowing people to override isAccessibleTo()
with a version that checks additional things. More information on
this is available in the document "How
to Specify And Use Role Permissions".
Known Problems
-
Nothing exists to integrate this stuff with Vivid's login code. One
place where this makes a difference is in the login/logout button at the
upper right of our current web page. Making this work properly requires
an understanding of Vivid's login code, which is somewhat involved
to say the least. It will not only involve code, but probably some
data conversion. For instance, we'll have to add ServiceProviderRoles
to all of our existing customers who have this role in the Vivid code if
we don't want them getting permissions problems on new pages in service
provider sections of our site.
-
Overriding getPermissonDeniedURL() doesn't allow you to specify
a specific model to use with the jsp. There is a default PermissionDeniedModel
used with the default page which displays the user's roles, but there is
no way to have the system use a different model. So for now, the
jsps must be static if this method is overridden.
-
No way to specify failed login URL yet. Sometimes you might want
to specify a page to go to if the user fails login, rather than just going
back to the login form with validation again.
-
The HTML for the login pages need work. It looks ugly. The
example could be a bit more consistent (Log-in, Login, and Log In are all
on the image).
4 Version History
0.3, Dec 22, 2000. Added required jsp additions, removed need for
use of overloaded enterState() method. Added getLoginFailedURL().
0.2, Dec 15, 2000. Broke
off Role Permissions into separate document. Added isAccessibleTo()
method to Model.
0.1 First Version