浏览 4551 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-04-30
最后修改:2009-05-03
<login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login.jsp</form-login-page> <form-error-page>/failure.jsp</form-error-page> </form-login-config> <realm-name>Security Realm</realm-name> </login-config> <security-constraint> <display-name>Tomcat security constraint</display-name> <web-resource-collection> <web-resource-name>Protected Resources</web-resource-name> <url-pattern>/security/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>manager</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>manager</role-name> </security-role> 而使用CAS实现SSO之后,我们又不想容器保护去掉,该怎么办才能真正把这个CAS验证过的用户和角色传给Tomcat容器,而不至于重新登录,或者出现403错误呢? 需求描述2: 在有EJB资源的时候,我们通常都会用JAAS来实现保护EJB资源,以限制EJB资源的访问。在调用EJB的时候,需要输入用户名和密码并且此用户具有相应的角色才能调用EJB。而在实现SSO之后,如果真正实现与EJB容器进行SSO呢? 下面基于Tomcat容器实现需求1. 1. Tomcat窗口提供了一个叫Valve的接口,还提供一个基本实现ValveBase,我们解决方案就是基于实现一个自己的Valve, 在Valve里把经过CAS验证的用户传给Tomcat。以下就是自定义的Valve: package edu.extcas.valve; import java.io.*; import java.security.Principal; import java.util.ArrayList; import java.util.List; import javax.servlet.*; import javax.servlet.http.*; import org.apache.catalina.Container; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.realm.GenericPrincipal; import org.apache.catalina.valves.ValveBase; //import com.ttg.customagent.jboss.SimplePrincipal; public class ExtCASValve extends ValveBase { public final static String CAS_FILTER_USER = "edu.yale.its.tp.cas.client.filter.user"; public void setContainer(Container container) { super.setContainer(container); } public void invoke(Request request, Response response) throws IOException, ServletException { HttpSession session = ((HttpServletRequest) request).getSession(); if (session != null) { String username = (String) session.getAttribute(CAS_FILTER_USER); if (null != username) { List roleList = getRolesFromUserStore(username); Principal principal = new GenericPrincipal(request.getContext() .getRealm(), username, "", roleList); request.setUserPrincipal(principal); } //SecurityAssociation类是在登录EJB的时候使用的。 //SecurityAssociation.setPrincipal(new SimplePrincipal(username.trim())); //SecurityAssociation.setCredential("password".trim().toCharArray()); getNext().invoke(request, response); return; } else { getNext().invoke(request, response); return; } } //此方法是为在用户存储里取到相应的角色。自已根据实际情况实现 private List getRolesFromUserStore(String username) { List roleList = new ArrayList(); roleList.add("admin"); roleList.add("manager"); return roleList; } } 2. 实现好自己的Valve,在server.xml里配置一下。然后就可以测试了。 下面讲实现需求2实现的思路: 1. 如果是EJB容器是JBoss,而Web容器是Tomcat, 也可以在Valve里用SecurityAssociation类(或者其他的方法)把CAS验证过的用户传到EJB容器里。这里同时需要传递密码。 2. 要实现一个LoginModule, 类似以下代码: package edu.extcas.loginmodule; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.security.Principal; import java.security.cert.X509Certificate; 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.catalina.realm.GenericPrincipal; public class ExtCasLoginModule implements LoginModule { private CallbackHandler callbackHandler; private Principal unauthenticatedIdentity; private String userName = null; private String password = null; protected Subject subject; protected boolean loginOk; protected Principal identity; public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { this.subject = subject; this.callbackHandler = callbackHandler; try { String name = (String) options.get("unauthenticatedIdentity"); if (name != null) { unauthenticatedIdentity = createIdentity(name, "", null); } } catch (Exception e) { } } public boolean login() throws LoginException { loginOk = false; if (userName == null && password == null) { identity = unauthenticatedIdentity; } if ((identity == null) && (password != null)) { if ("password".equals(password)) { identity = createIdentity(userName, "", null); loginOk = true; return true; } } } public boolean abort() throws LoginException { subject.getPrincipals().clear(); return true; } public boolean logout() throws LoginException { Principal identity = getIdentity(); Set principals = subject.getPrincipals(); if (principals.contains(identity) == true) principals.remove(identity); return true; } private Principal getIdentity() { return identity; } private Principal createIdentity(String username) throws Exception { return new CustomPrincipal(username); } public boolean commit() throws LoginException { if (loginOk == false) return false; StringBuffer sb = new StringBuffer(); List roles = getRolesFromUserStore(userName); Set principals = subject.getPrincipals(); if (!principals.contains(identity)) principals.add(identity); Group jbossroles = new SimpleGroup("Roles"); for (Iterator i = roles.iterator(); i.hasNext();) { String roleStr = (String) i.next(); jbossroles.addMember(new SimplePrincipal(roleStr)); } principals.add(jbossroles); return true; } private List getRolesFromUserStore(String username) { List roleList = new ArrayList(); roleList.add("admin"); roleList.add("manager"); return roleList; } } 这里面最重要的就是commit方法了。由于这是个有点麻烦,只是在产品里的代码改了一下。不保证这个LoginModule能运行正确,不过思路和基本方法都是正确的。 3. 然后在JBoss里的配置使用这个LoginModule, 并且让它来保护EJB资源。这样就可以了。至于怎么设置这个,非常繁琐,这里就不讲了。 重要注意:这里为了演示,把密码写作“password”, 在实现应用中,用这么简单的密码不合适,这谁都知道。但是是否使用真正的密码,也是要考虑的。如果使用真正的密码,在loginmodule还要验证一次。如果使用一定逻辑来实现密码验证(如使用CAS的Ticket),同时也支持真正密码登录,这样适应性更强,性能也更好。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |