Spring Security 3.x Cookbook
上QQ阅读APP看书,第一时间看更新

JAAS-based security authentication on JSPs

The deployment descriptor is the main configuration file of all the web applications. The container first looks out for the deployment descriptor before starting any application.

The deployment descriptor is an XML file, web.xml, inside the WEB-INF folder.

If you look at the XSD of the web.xml file, you can see the security-related schema.

The schema can be accessed using the following URL: http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd.

The following is the schema element available in the XSD:

<xsd:element name="security-constraint" type="j2ee:security-constraintType"/>
<xsd:element name="login-config" type="j2ee:login-configType"/>
<xsd:element name="security-role "type="j2ee:security-roleType"/>

Getting ready

You will need the following to demonstrate authentication and authorization:

  • JBoss 7
  • Eclipse Indigo 3.7
  • Create a dynamic web project and name it Security Demo
  • Create a package, com.servlets
  • Create an XML file in the WebContent folder, jboss-web.xml
  • Create two JSP pages, login.jsp and logoff.jsp

How to do it...

Perform the following steps to achieve JAAS-based security for JSPs:

  1. Edit the login.jsp file with the input fields j_username, j_password, and submit it to SecurityCheckerServlet:
    <%@ page contentType="text/html; charset=UTF-8" %>
    <%@ page language="java" %>
    <html >
      <HEAD>
        <TITLE>PACKT Login Form</TITLE>
        <SCRIPT>
          function submitForm() {
            var frm = document. myform;
            if( frm.j_username.value == "" ) {
              alert("please enter your username, its empty");
              frm.j_username.focus();
              return ;
            }
    
            if( frm.j_password.value == "" ) {
              alert("please enter the password,its empty");
              frm.j_password.focus();
              return ;
            }
            frm.submit();
          }
        </SCRIPT>
      </HEAD>
      <BODY>
        <FORM name="myform" action="SecurityCheckerServlet" METHOD=get>
        <TABLE width="100%" border="0" cellspacing="0" cellpadding="1" bgcolor="white">
        <TABLE width="100%" border="0" cellspacing="0" cellpadding="5">
        <TR align="center">
        <TD align="right" class="Prompt"></TD>
        <TD align="left">
          <INPUT type="text" name="j_username" maxlength=20>
        </TD>
        </TR>
        <TR align="center">
        <TD align="right" class="Prompt"> </TD>
        <TD align="left">
        <INPUT type="password"name="j_password" maxlength=20 >
        <BR>
        <TR align="center">
        <TD align="right" class="Prompt"> </TD>
        <TD align="left">
        <input type="submit" onclick="javascript:submitForm();" value="Login">
        </TD>
        </TR>
        </TABLE>
        </FORM>
      </BODY>
    </html>

    The j_username and j_password are the indicators of using form-based authentication.

  2. Let's modify the web.xml file to protect all the files that end with .jsp. If you are trying to access any JSP file, you would be given a login form, which in turn calls a SecurityCheckerServlet file to authenticate the user. You can also see role information is displayed. Update the web.xml file as shown in the following code snippet. We have used 2.5 xsd. The following code needs to be placed in between the webapp tag in the web.xml file:
    <display-name>jaas-jboss</display-name>
     <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
     </welcome-file-list>
    
     <security-constraint>
        <web-resource-collection>
         <web-resource-name>something</web-resource-name>
         <description>Declarative security tests</description>
         <url-pattern>*.jsp</url-pattern>
         <http-method>HEAD</http-method>
         <http-method>GET</http-method>
         <http-method>POST</http-method>
         <http-method>PUT</http-method>
         <http-method>DELETE</http-method>
        </web-resource-collection>
        <auth-constraint>
         <role-name>role1</role-name>
        </auth-constraint>
        <user-data-constraint>
         <description>no description</description>
         <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
     </security-constraint>
     <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
         <form-login-page>/login.jsp</form-login-page>
         <form-error-page>/logoff.jsp</form-error-page>
        </form-login-config>
     </login-config>
     <security-role>
        <description>some role</description>
        <role-name>role1</role-name>
     </security-role>
     <security-role>
        <description>packt managers</description>
        <role-name>manager</role-name>
     </security-role>
     <servlet>
        <description></description>
        <display-name>SecurityCheckerServlet</display-name>
        <servlet-name>SecurityCheckerServlet</servlet-name>
        <servlet-class>com.servlets.SecurityCheckerServlet</servlet-class>
     </servlet>
     <servlet-mapping>
        <servlet-name>SecurityCheckerServlet</servlet-name>
        <url-pattern>/SecurityCheckerServlet</url-pattern>
     </servlet-mapping>
  3. JAAS Security Checker and Credential Handler: Servlet is a security checker. Since we are using JAAS, the standard framework for authentication, in order to execute the following program you need to import org.jboss.security.SimplePrincipal and org.jboss.security.auth.callback.SecurityAssociationHandle and add all the necessary imports. In the following SecurityCheckerServlet, we are getting the input from the JSP file and passing it to the CallbackHandler.

    We are then passing the Handler object to the LoginContext class which has the login() method to do the authentication. On successful authentication, it will create Subject and Principal for the user, with user details. We are using iterator interface to iterate the LoginContext object to get the user details retrieved for authentication.

    In the SecurityCheckerServlet Class:

    package com.servlets;
    public class SecurityCheckerServlet extends HttpServlet {
      private static final long serialVersionUID = 1L;
         
        public SecurityCheckerServlet() {
          super();
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
           char[] password = null;
           PrintWriter out=response.getWriter();
           try
           {
              
             SecurityAssociationHandler handler = new SecurityAssociationHandler();
             SimplePrincipal user = new SimplePrincipal(request.getParameter("j_username"));
             password=request.getParameter("j_password").toCharArray();
             handler.setSecurityInfo(user, password);
             System.out.println("password"+password);
        
             CallbackHandler myHandler = new UserCredentialHandler(request.getParameter("j_username"),request.getParameter("j_password"));
             LoginContext lc = new LoginContext("other", handler);
             lc.login();
        
             Subject subject = lc.getSubject();
             Set principals = subject.getPrincipals();
            
             List l=new ArrayList();
             Iterator it = lc.getSubject().getPrincipals().iterator();
             while (it.hasNext()) {
               System.out.println("Authenticated: " + it.next().toString() + "<br>");
               out.println("<b><html><body><font color='green'>Authenticated: " + request.getParameter("j_username")+"<br/>"+it.next().toString() + "<br/></font></b></body></html>");
                  }
               it = lc.getSubject().getPublicCredentials(Properties.class).iterator();
               while (it.hasNext()) System.out.println(it.next().toString());
          
               lc.logout();
           }     catch (Exception e) {
                 out.println("<b><font color='red'>failed authenticatation.</font>-</b>"+e);
                
           }
        }
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       }
    
    }

    Create the UserCredentialHandler file:

    package com.servlets;
    class UserCredentialHandler implements CallbackHandler {
      private String user, pass;
    
      UserCredentialHandler(String user, String pass) {
        super();
        this.user = user;
        this.pass = pass;
      }
      @Override
      public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
          for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof NameCallback) {
              NameCallback nc = (NameCallback) callbacks[i];
              nc.setName(user);
            } else if (callbacks[i] instanceof PasswordCallback) {
              PasswordCallback pc = (PasswordCallback) callbacks[i];
              pc.setPassword(pass.toCharArray());
            } else {
            throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
          }
        }
      }
     }

    In the jboss-web.xml file:

    <?xml version="1.0" encoding="UTF-8"?>
    <jboss-web>
    <security-domain>java:/jaas/other</security-domain>
    </jboss-web>

    Other is the name of the application policy defined in the login-config.xml file.

    All these will be packed in as a .war file.

  4. Configuring the JBoss Application Server. Go to jboss-5.1.0.GA\server\default\conf\login-config.xml in JBoss. If you look at the file, you can see various configurations for database LDAP and a simple one using the properties file, which I have used in the following code snippet:
    <application-policy name="other">
      <!-- A simple server login module, which can be used when the number of users is relatively small. It uses two properties files:
      users.properties, which holds users (key) and their password (value).
      roles.properties, which holds users (key) and a comma-separated list of
      their roles (value).
      The unauthenticatedIdentity property defines the name of the principal
      that will be used when a null username and password are presented as is
      the case for an unauthenticated web client or MDB. If you want to allow such users to be authenticated add the property, e.g.,
        unauthenticatedIdentity="nobody"
      -->
      <authentication>
      <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
        flag="required"/>
        <module-option name="usersProperties">users.properties</module-option>
        <module-option name="rolesProperties">roles.properties</module-option>
        <module-option name="unauthenticatedIdentity">nobody</module-option> 
      </authentication>
    </application-policy>
  5. Create the users.properties file in the same folder. The following is the Users.properties file with username mapped with role.

    User.properties

    anjana=anjana123

    roles.properties

    anjana=role1
  6. Restart the server.

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com. If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.

How it works...

JAAS consists of a set of interfaces to handle the authentication process. They are:

  • The CallbackHandler and Callback interfaces
  • The LoginModule interface
  • LoginContext

The CallbackHandler interface gets the user credentials. It processes the credentials and passes them to LoginModule, which authenticates the user.

JAAS is container specific. Each container will have its own implementation, here we are using JBoss application server to demonstrate JAAS.

In my previous example, I have explicitly called JASS interfaces.

UserCredentialHandler implements the CallbackHandler interfaces.

So, CallbackHandlers are storage spaces for the user credentials and the LoginModule authenticates the user.

LoginContext bridges the CallbackHandler interface with LoginModule. It passes the user credentials to LoginModule interfaces for authentication:

CallbackHandler myHandler = new UserCredentialHandler(request.getParameter("j_username"),request.getParameter("j_password"));
  LoginContext lc = new LoginContext("other", handler);
  lc.login();

The web.xml file defines the security mechanisms and also points us to the protected resources in our application.

The following screenshot shows a failed authentication window:

How it works...

The following screenshot shows a successful authentication window:

How it works...

See also

  • The JAAS-based security authentication on servlet recipe
  • The Container-based basic authentication on servlet recipe
  • The Form-based authentication on servlet recipe
  • The Form-based authentication with open LDAP and servlet recipe
  • The Hashing/Digest Authentication on servlet recipe
  • The Basic authentication for JAX-WS and JAX-RS recipe
  • The Enabling and disabling the file listing recipe