You have implemented your own login system.
Unfortunately, it isn't particularly secure. A user could simply guess "welcome.xhtml" to bypass the password.
You could improve the security by adding additional checks on each JSF page (e.g., using a <f:viewAction>
to authenticate the user on each request).
A better approach is to use container-managed security.
Container-managed security is a feature of the application server.
It looks after logins and ensures that secure pages can only be accessed by authenticated users.
A JDBCRealm can be used to do user authentication via a database.
Configuration is a tedious process but not difficult.
Set Up Views
First, the JDBCRealm needs to use two database tables:
- A user table, with a username column and a password column. The table is used to look up the user's password by username.
- A group table, with a username column and a group name column. The table is used to find all the groups for a given username.
If you already have two tables that contain this information, you can use them directly.
For greater flexibility, you can also define database views for the JDBCRealm.
To create views, edit your create_db.sql file and create two new views:
create view jdbcrealm_user (username, password) as
select username, password
from account;
create view jdbcrealm_group (username, groupname) as
select username, 'Users'
from account;
A database view acts like a table but the rows of the table are retrieved according to the underlying tables.
You can test a views by running an SQL queries against it:
select * from jdbcrealm_user
The jdbcrealm_group is a simple view that associates every username with a group called "Users".
Configure JDBC Realm
The JDBC Realm is configured in the GlassFish Server Administration Console.
Navigate to the "Configurations/server-config/Security/Realms" section of the menu.
Create a new realm.
Name the realm "aipRealm" (without the quotes) and use the class name com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm. Then scroll down.
Configure the security using the settings as depicted in the two images below:
Use the JDBC Realm
Next, you need to configure your Week5 project to use the JDBC Realm.
First, add a new file of type "GlassFish Descriptor" from the category "GlassFish".
Use the default name and file location.
If an error occurs while creating the file, you can ignore it.
Double click on the file to edit it.
At the top of the editor panel, there is the option to switch to different views.
Click on XML to switch to the XML view.
Add the following tag to the XML document:
<security-role-mapping>
<role-name>Users</role-name>
<group-name>Users</group-name>
</security-role-mapping>
This tag tells GlassFish to map the group of users named "Users" into a role named "Users".
Your complete sun-web.xml file should now look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD GlassFish Application Server 3.0 Servlet 3.0//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_3_0-0.dtd">
<sun-web-app error-url="">
<security-role-mapping>
<role-name>Users</role-name>
<group-name>Users</group-name>
</security-role-mapping>
<class-loader delegate="true"/>
<jsp-config>
<property name="keepgenerated" value="true">
<description>Keep a copy of the generated servlet class' java code.</description>
</property>
</jsp-config>
</sun-web-app>
Now, edit the source of your web.xml file. Add the following configuration elements to the file:
<login-config>
<auth-method>FORM</auth-method>
<realm-name>aipRealm</realm-name>
<form-login-config>
<form-login-page>/faces/login.xhtml</form-login-page>
<form-error-page>/faces/login.xhtml</form-error-page>
</form-login-config>
</login-config>
<security-constraint>
<display-name>Logged in pages</display-name>
<web-resource-collection>
<web-resource-name>secure</web-resource-name>
<url-pattern>/faces/welcome.xhtml</url-pattern>
<url-pattern>/faces/secret/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>Users</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<description>Logged in Users</description>
<role-name>Users</role-name>
</security-role>
The first tag, <login-config>
, tells GlassFish that login should use a HTML form (rather than browser-based authentication).
It also configures the page that users are redirected to if they need to log in.
The <security-constraint>
tag is used to tell GlassFish which resources can be used by which users (roles).
We have secured welcome.xhtml and everything in the /secret/ directory.
The <security-role>
tag declares the names of the roles that will be used by the application.
After modifying the source of the XML file, save the file.
Then you can switch to the Security tab on the XML editor.
The Security tab provides a visual way of configuring the same settings (but be careful, sometimes it can be buggy so it always pays to double check the generated XML).
Now, run your application once again.
Attempt to access the welcome.xhtml page directly.
You should be redirected back to the login.xhtml page.
Integrating with Container Managed
You can now use the container-based authentication to replace your hand-created login code.
You can get the container to do authentication by calling login on the HttpServletRequest.
To log in:
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getRequest();
try {
request.login(username, password);
} catch (ServletException e) {
// (you could also log the exception to the server log)
context.addMessage(null, new FacesMessage(e.getMessage()));
return null;
}
return "welcome";
}
To log out:
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getRequest();
try {
request.logout();
} catch (ServletException e) {
// (you could also log the exception to the server log)
context.addMessage(null, new FacesMessage(e.getMessage()));
}
}
Modify your login code to use container-based authentication.
When you first test your code, it will find that it does not accept any passwords.
This is a security feature of GlassFish.
GlassFish does not allow you to store passwords in plain text in the database.
In fact, it is generally considered bad practice to store passwords in plain text in any application.
Instead, passwords should be encrypted.
We have configured GlassFish to use SHA-256 encryption.
You can use this website to manually encrypt a password:
http://www.movable-type.co.uk/scripts/sha256.html
To get container-managed encryption to work, you should update your database tables.
You should manually encrypt each password and save it in the account database table.
For example, the password "qwerty" is encrypted to "65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5".