Tomcat JAAS Authentication & Authorization

Java Authentication and Authorization Service (JAAS)  is another implementation of Tomcat Realm ( org.apache.catalina.Realm) , used to authenticate   users  and identify  their security roles.

You will need

Tomcat 7.0, Spring MVC or any other MVC , MySql or any other database.

1. Configuration

appName

The value of appName    property  is passed to   the  LoginContext (javax.security.auth.login.LoginContext) constructor to specify  the name of implemented LoginModule ( javax.security.auth.spi.LoginModule). 

LoginModule is a pluggable interface used to  provide a particular type of authentication. LoginContext  reads the configuration (javax.security.auth.login.Configuration)  which specifies the login  module (s) used in the login application.

A  login configuration contains the following  information :

      Name {
             ModuleClass  Flag    ModuleOptions;
             ModuleClass  Flag    ModuleOptions;
             ModuleClass  Flag    ModuleOptions;
       };

There could be more the one login module  in one login configuration.

ModuleClass is the fully qualified class name of  the login module. The Flag
values ( Required, Requisite, Sufficient, Optional )  control  the behavior  of authentication.

ModuleOptions  is  a space separated list of  the login module specific values which are passed directly to  the underlying login modules.

 

Add the following Tomcat JAASRealm config to Tomcat server.xml :

	
			
			
			

Create a file named jass.config and place it in tomcat/conf directory:

jasslogin{
com.test.secure.TestLoginModule  required;
};

Create a file named setenv.bat, add the below config, and place it in tomcat/bin directory

set JAVA_OPTS=-Djava.security.auth.login.config==C:/tomcat/conf/jaas.config

2. LoginModule

When the configuration is read by the logincontext, the login module is initialized with Subject ( javax.security.auth.Subject) , a call back  handler ( javax.security.auth.callback.CallBackHandler) , shared login module state and and LoginModule-specific options.

 

 

  boolean login() throws LoginException;

Login mothod is the first one invoked by the LoginContext to perform the actual authentication  and the return either true or false. If authentication succeeds the method commit gets invoked. 

 
package com.test.secure;

import java.io.IOException;
import java.security.Principal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import org.apache.log4j.Logger;

public class TestLoginModule implements LoginModule {

	Logger logger = Logger.getLogger(TestLoginModule.class);
	public static String USER_QUERY = "select user_name from users where user_name=? and user_pass=?";
	public static String ROLE_QUERY = "select role_name from  user_roles where user_name=?";

	private Subject subject;
	private CallbackHandler callbackHandler;
	private Map sharedState;
	private Map options;

	// configurable option
	private boolean debug = false;

	// the authentication status
	private boolean succeeded = false;
	private boolean commitSucceeded = false;

	// user credentials
	private String username = null;
	private char[] password = null;
	// principals
	private TestUserPrincipal testUserPrincipal;
	private TestRolePrincipal testRolePrincipal;
	private TestPasswordPrincipal testPasswordPrincipal;

	@Override
	public void initialize(Subject subject, CallbackHandler callbackHandler,
			Map<String, ?> sharedState, Map<String, ?> options) {

		this.subject = subject;
		this.callbackHandler = callbackHandler;
		this.sharedState = sharedState;
		this.options = options;

	}

	@Override
	public boolean login() throws LoginException {

		if (callbackHandler == null) {
			throw new LoginException("call back handler is null");
		}

		Callback[] callbacks = new Callback[2];
		callbacks[0] = new NameCallback("username");
		callbacks[1] = new PasswordCallback("password: ", false);

		try {

			callbackHandler.handle(callbacks);
			
			username = ((NameCallback) callbacks[0]).getName();
			password = ((PasswordCallback) callbacks[1]).getPassword();

			if (username == null || password == null) {
				throw new LoginException(
						"Callback handler does not return login data properly");

			}

			logger.info(" username" + username);
			logger.info("password" + password);

			// authenticate

			if (isValidUser()) {
				succeeded = true;
				return true;
			}
			
		

		} catch (IOException e) {
			e.printStackTrace();
		} catch (UnsupportedCallbackException e) {
			e.printStackTrace();
		}

		return false;
	}

	@Override
	public boolean commit() throws LoginException {
		
		logger.info("committing...");

		if (succeeded == false) {
			return false;
		} else {
			testUserPrincipal = new TestUserPrincipal(username);
			
			
			if (!subject.getPrincipals().contains(testUserPrincipal)) {
				subject.getPrincipals().add(testUserPrincipal);
				
			}
			
			
		/*	testPasswordPrincipal = new TestPasswordPrincipal(new String(
					password));
			if (!subject.getPrincipals().contains(testPasswordPrincipal)) {
				subject.getPrincipals().add(testPasswordPrincipal);
				
			}
*/
			// populate subject with roles.
			
			
			// strings
			List roles = getRoles(testUserPrincipal);
			
			
			
			for (String role : roles) {
				
				
				
				testRolePrincipal = new TestRolePrincipal(role);
				
				if (!subject.getPrincipals().contains(testRolePrincipal)) {
					
					subject.getPrincipals().add(testRolePrincipal);
					
				}
					
					
				
			}
			
			
			commitSucceeded = true;

			logger.info("Login subject were successfully populated with principals and roles");
			logger.info("--------------principals");
			logger.info(subject.getPrincipals());
				
		   
			
			for(Principal p: subject.getPrincipals()){
				
				if(p instanceof TestRolePrincipal){
					
					logger.info(" ROLE: "+p.getName());
					
				}
				
				
			}
			
			
			

			return true;
		}
	}

	@Override
	public boolean abort() throws LoginException {

		if (succeeded == false) {
			return false;
		} else if (succeeded == true && commitSucceeded == false) {
			succeeded = false;
			username = null;
			if (password != null) {
				password = null;
			}
			testUserPrincipal = null;
		} else {
			logout();
		}
		return true;
	}

	@Override
	public boolean logout() throws LoginException {

		subject.getPrincipals().remove(testUserPrincipal);
		succeeded = false;
		succeeded = commitSucceeded;
		username = null;
		if (password != null) {
			for (int i = 0; i < password.length; i++) {
				password[i] = ' ';
				password = null;
			}
		}
		testUserPrincipal = null;
		return true;
	}

	private boolean isValidUser() throws LoginException {

		Connection connection = null;

		ResultSet rs = null;
		PreparedStatement stmt = null;

		try {

			connection = getConnection();

			stmt = connection.prepareStatement(USER_QUERY);
			stmt.setString(1, username);
			stmt.setString(2, new String(password));

			rs = stmt.executeQuery();

			if (rs.next()) { // User exist with the given user name and
								// password.
				return true;
			}
		} catch (Exception e) {
			logger.error("Error when loading user from the database " + e);
			e.printStackTrace();
		} finally {
			try {
				rs.close();
			} catch (SQLException e) {
				logger.error("Error when closing result set." + e);
			}
			try {
				stmt.close();
			} catch (SQLException e) {
				logger.error("Error when closing statement." + e);
			}
			try {
				connection.close();
			} catch (SQLException e) {
				logger.error("Error when closing connection." + e);
			}
		}
		return false;
	}

	private List getRoles(TestUserPrincipal user) {

		Connection connection = null;

		ResultSet rs = null;
		PreparedStatement stmt = null;

		List roleList = new ArrayList();

		try {

			connection = getConnection();

			stmt = connection.prepareStatement(ROLE_QUERY);
			stmt.setString(1, username);

			rs = stmt.executeQuery();

			while (rs.next()) {
				roleList.add(rs.getString("role_name"));
				user.addRole(new TestRolePrincipal((rs.getString("role_name"))));
			}
		} catch (Exception e) {
			logger.error("Error when loading user from the database " + e);
			e.printStackTrace();
		} finally {
			try {
				rs.close();
			} catch (SQLException e) {
				logger.error("Error when closing result set." + e);
			}
			try {
				stmt.close();
			} catch (SQLException e) {
				logger.error("Error when closing statement." + e);
			}
			try {
				connection.close();
			} catch (SQLException e) {
				logger.error("Error when closing connection." + e);
			}
		}
		
		return roleList;
	}

	private Connection getConnection() {

		try {
			Class.forName("com.mysql.jdbc.Driver");
			return DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/test", "root", "password");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

}
package com.test.secure;

import java.security.Principal;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.catalina.Group;
import org.apache.catalina.Role;
import org.apache.catalina.User;
import org.apache.catalina.UserDatabase;

public class TestUserPrincipal implements User {
	
	
	private String username;
	private Set roles = new HashSet();
	
	public TestUserPrincipal(String u){
		this.username=u;
	}

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return  username;
	}

	@Override
	public void addGroup(Group arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void addRole(Role role) {
	roles.add(role);
		
	}

	@Override
	public String getFullName() {
		// TODO Auto-generated method stub
		return username;
	}

	@Override
	public Iterator getGroups() {
		
		
		
		return null;
	}

	@Override
	public String getPassword() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Iterator getRoles() {

        return roles.iterator();
	}

	@Override
	public UserDatabase getUserDatabase() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getUsername() {
		// TODO Auto-generated method stub
		return username;
	}

	@Override
	public boolean isInGroup(Group arg0) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isInRole(Role role) {
		
		if(1==1){
			return true;
		}
		
		if(!roles.isEmpty()){
			Iterator it =roles.iterator();
			while(it.hasNext()){
			Role rol =(Role)it.next();
			if(rol.getName()!=null && rol.getName().equals(role.getName())){
				return true;
			}
			}
		}
		
		return false;
	}

	@Override
	public void removeGroup(Group arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void removeGroups() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void removeRole(Role arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void removeRoles() {
		roles.clear();
		
	}

	@Override
	public void setFullName(String arg0) {
		setUsername(username);
		
	}

	@Override
	public void setPassword(String arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setUsername(String arg0) {
	 this.username=arg0;
		
	}

	
	
}


package com.test.secure;

import java.io.Serializable;

import java.security.Principal;

import org.apache.catalina.Role;
import org.apache.catalina.UserDatabase;

public class TestRolePrincipal implements Role, Serializable {
	
	
	private String roleName;
	
	
	public TestRolePrincipal(String name){
		this.roleName=name;
	}

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return getRolename();
	}

	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return " some role";
	}

	@Override
	public String getRolename() {
		// TODO Auto-generated method stub
		return roleName;
	}

	@Override
	public UserDatabase getUserDatabase() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setDescription(String arg0) {
		
		
	}

	@Override
	public void setRolename(String arg0) {
		roleName =arg0;
		
	}


	

Make sure you jar these three classes in tomcat/lib so Tomcat loads them on startup.

Login Handler

In our case we are using SpringMVC, but for this test, you can use any other request handler or just a plain servlet.

package com.test.secure;

import java.security.Principal;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.catalina.Session;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

public class SecureController extends AbstractController {

	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest req,
			HttpServletResponse res) throws Exception {

 
		
   
		
	ModelAndView m = new ModelAndView("SecureView");
	return m;
	}

}

5. Security Constraint

Add a security constraint ( org.apache.catalina.deploy. SecurityConstraint) and the set of roles permitted to access the resources   to your application web.xml

 < security-constraint >
  
  < web-resource-collection >
  < web-resource-name>interdit< /web-resource-name >
  < url-pattern >/go/*< /url-pattern >
  < /web-resource-collection >
  
  < auth-constraint >
  < description>tomcat< /description >
  < role-name>tomcat< /role-name >
  < /auth-constraint>

 < /security-constraint>

And add login and login error pages



FORM
jasslogin


/join.do
/joinerror.do



6. User Password & Roles

CREATE TABLE `users` (
  `user_name` varchar(15) NOT NULL,
  `user_pass` varchar(15) NOT NULL,
  PRIMARY KEY (`user_name`);

INSERT INTO `users` VALUES ('admin','root'),('role1','root'),('tomcat','tomcat');


CREATE TABLE `user_roles` (
  `user_name` varchar(15) NOT NULL,
  `role_name` varchar(15) NOT NULL,
  PRIMARY KEY (`user_name`,`role_name`);


INSERT INTO `user_roles` VALUES ('tomcat','manager-gui'),('tomcat','manager-jmx'),('tomcat','manager-script'),('tomcat','manager-status'),('tomcat','tomcat');


Login Form


" method="post">
Login


Recommend this article

To display a formatted code ( PHP, Java, HTML, XML, etc.), surround the selected code with:

<pre class="brush :lang"> ...src code... </pre>

. Set lang to one of the following brush aliases: as3, bash, shell, coldfusion, c-sharp, c, css, delphi, pascal, patch, erlang, groovy, js, java, php, text, powershell, python, ruby, scala, sql, vb, xml, xhtml, xslt, html, and xhtml.
You may use the following HTML tags: H2, H3, H4, ul, ol, li, b, I, pre and p.

Create a new account or sign on to submit your comment