Tomcat JAAS 身份验证和授权

Java 认证和授权服务(JAAS)是一种用于验证用户身份以确定安全等级的 Tomcat Realm ( org.apache.catalina.Realm) 的实现。

需求

Tomcat 7.0, MVC (推荐 Spring MVC)和数据库(推荐 Mysql)

1. 配置

appName

appName 属性的值将被传递给 LoginContext (javax.security.auth.login.LoginContext) 构造函数,以指定实现 LoginModule ( javax.security.auth.spi.LoginModule) 的实体名称。

LoginModule 是一个提供了特定类型的身份验证的可插拔接口。 LoginContext 通过读取配置(javax.security.auth.login.Configuration) 指定登录程序中的登录模块(S)。

一个登录配置包括以下信息 :

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

一个登录配置中可能包括不只一个的登录模块。

ModuleClass 是登录模块的完整相称类名。Flag 值 ( Required, Requisite, Sufficient, Optional ) 则控制身份验证的行为。

ModuleOptions则直接将值传递给底层登录模块,它的格式是一个用空格分割的列表。

将下列 Tomcat JAAS Realm 配置添加到 Tomcat server.xml 文件中:

<realm classname="org.apache.catalina.realm.JAASRealm" appname="jasslogin" userclassnames="com.test.secure.TestUserPrincipal" roleclassnames="com.test.secure.TestRolePrincipal">
 
</realm>

在 tomcat/conf 文件夹中创建 jass.config 文件:

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

在 tomcat/bin 文件夹中创建 setenv.bat 文件,并添加下列配置:

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

2. 登录模块

当 logincontext 读取配置时,登录模块将初始化,包括 Subject ( javax.security.auth.Subject),回叫处理( javax.security.auth.callback.CallBackHandler),共享登录模块以及 LoginModule-specific 选项。

  boolean login() throws LoginException;

第一个被 LoginContext 调用来实际处理身份验证的方法是 Login 方法,它将返回 true 或 false 。如果验证成功, commit 方法将被调用。

 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;
		
	}

你需要将 tomcat/lib 中的三个类都封装到 jar 中,一边在启动时加载。

3.登录处理器

在这个例子中我们使用的是 Spring MVC,但作为测试,你可使用任何其他的请求处理器,或者只是一个 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;
	}

}

4. 安全约束

在 web.xml 中增加一个安全约束 ( org.apache.catalina.deploy. SecurityConstraint) 和角色授权的资源访问:

 < 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>

然后添加登录和登录错误页

FORMjasslogin/join.do/joinerror.do

5. 用户密码和角色

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');

6.登录表单

<form action="<%= response.encodeURL(" j_security_check")="" %="">" method="post">
 
    <fieldset>
        <legend>Login </legend>
        <p><label for="name">Username</label> <input name="j_username" type="text"></p>
        <p><label for="e-mail">Password</label> <input name="j_password" type="password"><br></p>
        <p class="submit"><input value="Submit" type="submit"></p>
    </fieldset>
 
</form>


你可能感兴趣的:(java,Security,jaas)