`
whitesock
  • 浏览: 478761 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

OpenEJB (4)

    博客分类:
  • EE
阅读更多

本文部分内容节选自Enterprise JavaBeans 3.0 by Bill Burke & Richard Monson-Haefel

 

5 Security
    EJB规范虽然规定了如何将安全信息从客户端传递到服务器,但是EJB规范并没有规定客户端如何取得安全信息,也没有规定如何进行验证。对于大多数应用服务器而言,JNDI验证是最为常见的一种方式。客户端在与JNDI InitialContext建立连接时进行验证,之后在调用远程EJB时,安全信息会传递到服务器并在服务器中进行传播。验证的执行过程是将一个或者多个角色与给定的用户进行关联。用户一旦通过了验证,应用服务器会对该用户进行授权。与验证不同,EJB规范对授权进行了明确的定义。

5.1 JAAS
    OpenEJB可以使用标准的JAAS机制来实现验证和授权。通常情况下,你需要通过设置java.security.auth.login.config系统属性来配置login modules的配置文件。如果没有指定这个系统属性,那么缺省会使用login.config作为文件名。以下是conf目录下一个login.config文件的例子:

PropertiesLogin {
    org.apache.openejb.core.security.jaas.PropertiesLoginModule required
        Debug=true
        UsersFile="users.properties"
        GroupsFile="groups.properties";
};

    以上文件中指定了org.apache.openejb.core.security.jaas.PropertiesLoginModule作为login module。同时指定了users.properties和groups.properties,其内容分别如下:
conf/users.properties
Kevin=password
WhiteSock=password

conf/groups.propertie
Manager=Kevin
Employee=Kevin,WhiteSock

    以下是修改后的MovieDaoJpaImpl.java:

@Stateful
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class MovieDaoJpaImpl implements MovieDaoJpaLocal, MovieDaoJpaRemote {
	//
	@PersistenceContext(unitName = "movie", type = PersistenceContextType.TRANSACTION)
	private EntityManager entityManager;
	
	@SuppressWarnings("unchecked")
	@PermitAll
	public List<Movie> getAllMovies() {
		Query query = entityManager.createQuery("SELECT m from Movie as m");
        return query.getResultList();
	}
	
	@RolesAllowed({"Employee", "Manager"})
	public void addMovies(List<Movie> movies) {
		for(Movie m : movies) {
			entityManager.persist(m);
		}
	}

	@RolesAllowed({"Manager"})
	public void deleteMovie(Movie movie) {
		Movie m = entityManager.merge(movie);
		entityManager.remove(m);
	}
}
public static void main(String args[]) throws Exception {
    //
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.RemoteInitialContextFactory");
    properties.setProperty(Context.PROVIDER_URL, "ejbd://localhost:4201");
    properties.setProperty(Context.SECURITY_PRINCIPAL, "Kevin");
    properties.setProperty(Context.SECURITY_CREDENTIALS, "password");
    InitialContext ctx = new InitialContext(properties);
    
    //
    List<Movie> movies = new ArrayList<Movie>();
    movies.add(new Movie("Dances with Wolves", "Kevin Costner", 1990));
    movies.add(new Movie("Legends of the Fall", "Edward Zwich", 1994));
    movies.add(new Movie("A Very Long Engagement", "Jean-Pierre Jeunet", 2004));

    //
    Object ref = ctx.lookup("MovieDaoJpaImplRemote");
    MovieDaoJpaRemote dao = (MovieDaoJpaRemote) PortableRemoteObject.narrow(ref, MovieDaoJpaRemote.class);
    dao.addMovies(movies);
    for(Movie m : dao.getAllMovies()) {
	    System.out.println("dao.getAllMovies(): " + m);
	    dao.deleteMovie(m);
    }
}

    以上代码中,如果尝试以"WhiteSock"作为Context.SECURITY_PRINCIPAL进行登陆,那么在执行到dao.deleteMovie(m)时,会抛出javax.ejb.EJBAccessException异常。


5.2 Plug Points
    OpenEJB的安全机制有多种扩展点,其中最复杂的方式是实现SecurityService接口。OpenEJB的缺省实现是SecurityServiceImpl,它继承自AbstractSecurityService。在conf/openejb.xml中通过以下方式进行配置:

<SecurityService id="My Default Security Service">
    className org.apache.openejb.core.security.SecurityServiceImpl
    realmName PropertiesLogin
</SecurityService>

    如果采用JAAS进行验证授权,那么有一种比较简单的扩展方式是定制LoginModule。以下是个简单的例子:

import java.io.IOException;
import java.security.Principal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.naming.InitialContext;
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 javax.sql.DataSource;

import org.apache.openejb.core.security.jaas.GroupPrincipal;
import org.apache.openejb.core.security.jaas.UserPrincipal;

public class JdbcLoginModule implements LoginModule {
	//
	private boolean debug;
	
	//
	private User user;
	private Subject subject;
    private CallbackHandler callbackHandler;
    private Set<Principal> principals = new HashSet<Principal>();

	public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
		//
		this.subject = subject;
        this.callbackHandler = callbackHandler;
        debug = "true".equalsIgnoreCase((String)options.get("Debug"));
        
        //
        if(debug) {
        	System.out.println("# in JdbcLoginModule.initialize()");
        }
	}

	public boolean login() throws LoginException {
		//
		if(debug) {
			System.out.println("# in JdbcLoginModule.login()");
		}
		
		//
		Callback[] callbacks = new Callback[2];
        callbacks[0] = new NameCallback("Username: ");
        callbacks[1] = new PasswordCallback("Password: ", false);
        try {
            callbackHandler.handle(callbacks);
        } catch (IOException ioe) {
            throw new LoginException(ioe.getMessage());
        } catch (UnsupportedCallbackException uce) {
            throw new LoginException(uce.getMessage() + " not available to obtain information from user");
        }

        //
        String name = ((NameCallback) callbacks[0]).getName();
        char[] password = ((PasswordCallback) callbacks[1]).getPassword();
        if (password == null) password = new char[0];
        
        //
        List<User> users;
		try {
			users = getUser(name, new String(password));
		} catch (Exception e) {
			throw new RuntimeException("failed to get user by name: " + name, e);
		}
        if(users != null && users.size() == 1) {
        	user = users.get(0);
        } else {
        	throw new LoginException("invalid user name: " + name);
        }
        
        //
		return true;
	}
	
	public boolean commit() throws LoginException {
		//
		if(debug) {
			System.out.println("# in JdbcLoginModule.commit()");
		}
		
		//
		principals.add(new UserPrincipal(user.getName()));
		for(Role r : user.getRoles()) {
			principals.add(new GroupPrincipal(r.getName()));
		}
		subject.getPrincipals().addAll(principals);
		return true;
	}

	public boolean logout() throws LoginException {
		//
		if(debug) {
			System.out.println("# in JdbcLoginModule.logout()");
		}
		
		//
		subject.getPrincipals().removeAll(principals);
        principals.clear();
        return true;
	}
	
	public boolean abort() throws LoginException {
		//
		if(debug) {
			System.out.println("# in JdbcLoginModule.abort()");
		}
		
		//
		subject.getPrincipals().removeAll(principals);
        principals.clear();
		return true;
	}
	
	private List<User> getUser(String name, String password) throws Exception {
		//
		InitialContext ctx = new InitialContext();
		DataSource ds = (DataSource) ctx.lookup("java:openejb/Resource/mysqlDataSource");
		
		//
		boolean succeed = false;
		Connection con = null;
		Map<Integer, User> users = new HashMap<Integer, User>();
		try {
			//
			con = ds.getConnection();
			PreparedStatement pstmt = con.prepareStatement("select " +
					"u.id as user_id, u.name as user_name, " +
					"r.id as role_id, r.name as role_name " +
					"from user u, role r, user_role ur " +
					"where u.id = ur.user_id and r.id = ur.roles_id " +
					"and u.name = ? and u.password = ?");
			pstmt.setString(1, name);
			pstmt.setString(2, password);
			
			//
			ResultSet rs = pstmt.executeQuery();
	        while ( rs.next() ) {
	        	//
	        	Integer userId = rs.getInt("user_id");
	        	String userName = rs.getString("user_name");
	        	User user = users.get(userId);
	        	if(user == null) {
	        		user = new User();
	        		user.setId(userId.intValue());
	        		user.setName(userName);
	        		users.put(userId, user);
	        	}
	        	if(user.getRoles() == null) {
	        		user.setRoles(new ArrayList<Role>());
	        	}
	        	
	        	//
	        	Role role = new Role();
	        	role.setId(rs.getInt("role_id"));
	        	role.setName(rs.getString("role_name"));
	        	user.getRoles().add(role);
	        }
	        rs.close();
	        pstmt.close();
	        
	        //
	        succeed = true;
		} finally {
			if(con != null) {
				try {
					con.close();
				} catch(Exception e) {
					if(succeed) {
						throw e;
					}
				}
			}
		}
		
		//
		List<User> r = new ArrayList<User>();
		r.addAll(users.values());
		return r;
	}
}

    在login.config中配置JdbcLogin realm,例如:
JdbcLogin {
    com.yourpackage.JdbcLoginModule required
        Debug=true;
};

    conf/openejb.xml中的相关配置如下:

<Resource id="mysqlDataSource" type="DataSource">
  JdbcDriver com.mysql.jdbc.Driver
  JdbcUrl jdbc:mysql://localhost:3306/ejb
  UserName root
  Password password
  JtaManaged true
</Resource>

<SecurityService id="My Default Security Service">
    className org.apache.openejb.core.security.SecurityServiceImpl
    realmName JdbcLogin
</SecurityService>

    数据库中的相关表如下:

mysql> select * from user;
+----+-----------+----------+
| id | name      | password |
+----+-----------+----------+
|  1 | Kevin     | password   |
|  2 | WhiteSock | password   |
+----+-----------+----------+

mysql> select * from role;
+----+----------+
| id | name     |
+----+----------+
|  1 | Manager  |
|  2 | Employee |
+----+----------+

mysql> select * from user_role;
+---------+----------+
| User_id | roles_id |
+---------+----------+
|       1 |        1 |
|       2 |        2 |
|       2 |        1 |
+---------+----------+
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics