`
grandboy
  • 浏览: 125212 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

基于CAS 实现与容器(Tomcat 或者 JBoss)的SSO

阅读更多
需求描述1:大家知道J2EE应用程序都可以用类型以下形式进行保护:
   <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),同时也支持真正密码登录,这样适应性更强,性能也更好。


分享到:
评论

相关推荐

    CAS-Server-Client单点登录demo

    CAS(Central Authentication Service)是基于Java的开源身份验证框架,用于实现单一登录(Single Sign-On,简称SSO)。SSO允许用户通过一次登录过程访问多个应用系统,无需为每个系统分别进行认证。在这个"CAS-...

    集成框架中使用CAS实现单点登录技术方案.pdf

    - 支持的应用服务器包括JBoss和Apache Tomcat。 - CAS的Java客户端过滤器是edu.yale.its.tp.cas.client.filter.CASFilter。 ### CAS与其他技术的整合 - CAS能够与多种编程语言和框架整合,如Java, .Net, ISAPI, ...

    基于Java EE的单点登录技术研究与实现.zip

    基于Java EE的SSO技术研究与实现是Java企业级开发中的一项重要技术,它极大地提高了用户体验,同时也简化了企业级应用的管理。 首先,要理解SSO的工作原理。SSO的核心是中央认证服务(Central Authentication ...

    CAS实战4.0

    【CAS实战4.0】是耶鲁大学推出的一套基于CAS(Central Authentication Service,中央认证服务)的开源单点登录(Single Sign-On, SSO)系统实战教程,旨在提供简单易懂的技术指导。该教程主要涵盖了如何在实际环境中...

    lifery6.1+cas初始化环境搭建及门户解决方案

    - **CAS Client**:部署在各个应用系统中,用于与CAS Server交互以实现单点登录。 **2.2 数字签名** **2.2.1 概念** 数字签名是一种基于公钥加密技术的安全机制,用于验证消息的真实性和完整性。在CAS中,数字...

    单点登录分析报告.pdf

    2. 100% Java实现,基于Spring框架,易于集成和扩展,并且可以与acegi(Spring权限控制)配合使用。 3. CAS Server作为一个独立部署的Web应用,负责用户认证。 4. CAS Client支持多种客户端,包括Java、.Net、PHP、...

    SSO CAS 单点登录实例SSOAuth+SSOWebDemo1+SSOWebDemo2.zip

    样例部 署和运行的环境有一定的要求,需要符合Servlet2.3以上标准的J2EE容器才能运行(例如Tomcat5,Sun Application Server 8, Jboss 4等)。另外,身份认证服务需要JDK1.5的运行环境。之所以要用JDK1.5是因为笔者...

    acegi-security-1.0.5.zip

    通过此组件,Acegi Security可以与CAS服务器交互,支持单点登录(Single Sign-On, SSO)功能,使得用户只需一次登录就能访问多个应用系统。 3. acegi-security-jboss-1.0.5.jar:这是针对JBoss应用服务器的适配器,...

    liferayPortal

    - Single Sign-On (SSO):通过Yale CAS和Siteminder等提供单点登录功能。 - Hierarchical and Extensible系统:支持社区和组织的层次结构和扩展。 - Mule or ServiceMix ESB:支持企业服务总线集成。 #### 八、性能...

    knowage 7.0 使用说明.docx

    应用服务器如Tomcat或JBoss是部署Knowage服务的关键组件。正确配置应用服务器的内存分配、线程池等参数,可以提升系统处理大量请求的能力。 **5. 元数据的数据库架构** 元数据数据库用于存储关于数据源、用户、权限...

Global site tag (gtag.js) - Google Analytics