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

JCIFS支持多域的SSO

阅读更多

原谅我一开篇就骂人,折腾坏了。

====================================

介绍下背景:JCIFS是现在大多数主流的域登陆SSO采用的开源软件,包含如spring-acegi、liferay等都采用作为SSO的一部分。

 

可怜的悲剧来了。

JCIFS在单域环境下的确适用,但是在多个域的情况下,俺看了他所有的mailList,就只得到这么一句话,你需要在两个域之间建立信任关系,这样即使多个域情况下,也是能够SSO的。看来老外就是老外,在中国,修改配置是责任问题,这个你们懂的。

 

还好有个AJ的哥们说了,他改了源码,现在beta版,能支持多域了,俺看见曙光了。

=====================================

 

先初始说配置:

//jcifs.smb.client.soTimeout 这个参数默认30000,意思是你只要登陆,切换第二个用户就甭想进去了。
// JE上有个哥们提了同样的问题,说是单域下不能切换用户,还自己回答了,唉。。咋不说的更清楚些呢。
//jcifs.smb.client.soTimeout 不能太大了,否则切换不了用户,太小了,又登不进去。这个配置是关键
Config.setProperty( "jcifs.smb.client.soTimeout", "100" );//100
Config.setProperty( "jcifs.netbios.cachePolicy", "1200" );
Config.setProperty( "jcifs.smb.lmCompatibility", "0" );
//jcifs.smb.lmCompatibility的值大于3下面的值为true
Config.setProperty( "jcifs.smb.client.useExtendedSecurity", "false" );

 

 多域思路:

NTLM SSO的实现原理:http://www.cnblogs.com/adylee/articles/975213.html

NTLM 实现域用户名和密码 的核心 代码可以参考 jcifs.http.NtlmHttpFilter.negotiate(req,res)
改造如下:

  /**
       * 通过cifs获得登陆的用户名,用于自动登录,选择进行域验证
     * @param req
       * @param resp
        * @param domainController 域的域控制地址,通常为IP地址
     * @param skipDomainValidate 跳过域验证
     * @return
       * @throws Exception
     */
    protected NtlmPasswordAuthentication negotiate( HttpServletRequest req,
                HttpServletResponse resp,String domainController,
                boolean skipDomainValidate) throws Exception {
		NtlmPasswordAuthentication ntlm = null;
		UniAddress dc = null;
        String msg = req.getHeader( "Authorization" );
        boolean offerBasic = enableBasic && (insecureBasic || req.isSecure());
        if( msg != null && (msg.startsWith( "NTLM " ) || (offerBasic && msg.startsWith("Basic ")))) {
			if (msg.startsWith("NTLM ")) {
				dc = UniAddress.getByName( domainController,true );
                byte[] challenge = SmbSession.getChallenge( dc );
                if(( ntlm = NtlmSsp.authenticate( req, resp, challenge )) == null ) {
                	return null;
                }
	        }
			if ( skipDomainValidate ){
				return ntlm;
			}
	        try {
                SmbSession.logon( dc, ntlm );
                return ntlm;
            } catch( Exception e ) {
            	System.out.println(e.getClass().getName()+":"+e.getMessage());
            	resp.setHeader( "WWW-Authenticate", "NTLM" );
                resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
                resp.setContentLength(0); 
                resp.flushBuffer();
                return null;
            }
        }else{
        	resp.setHeader( "WWW-Authenticate", "NTLM" );
            resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
            resp.setContentLength(0);
            resp.flushBuffer();
            return null;
        }
    }

 


多域SSO分两部分:1、自动登录部分;2、切换用户部分。

1、自动登录(需要将地址添加到信任站点)

说思路:自动登陆时,比如调用链接:accout.do?method=autoLogin,这时候调用我们修改后的negotiate方法,传入参数(req,res,任意的域IP,true)。我们此时跳过域用户验证部分,只是为了获得客户端提交的信息,这时候,我们就能通过ntlm.getDomain()获得用户需要登陆的域,比如域ds2,现在需要将ds2转会为IP地址(可以定义一个MAP,保存域与IP的对应关系);将获得的IP地址保存在session内,跳转到我们真正进行验证的action的ntlmLogin方法,执行negotiate(req,res,域对应的IP,false),这时候进行验证(防止山寨登陆),这样自动登录就完成了;出错处理,跳转到手工登陆页面。

	public ModelAndView autoLogin(HttpServletRequest request,
			HttpServletResponse response){
		StringBuffer scripts = new StringBuffer();
		String ctx = request.getContextPath();
		try {
			String randomDC = getRandomDomainController();
			NtlmPasswordAuthentication ntlm = negotiate(request, response,randomDC,true);
			if ( ntlm == null )return null;
			
			//获得域对应的IP地址
			String domainController = getDomainController(ntlm.getDomain());
			if (logger.isDebugEnabled()) {
				logger.debug("客户端信息="+ntlm.getDomain() + ":"+ntlm.getUsername()+"<"+domainController+">"); 
			}
			//跳转到真正的登陆方法
			request.getSession().setAttribute(SESSION_DOMAINCONTROLLER, domainController);
			scripts.append("location.href='"+request.getContextPath()+"/accout.do?method=ntlmLogin';");
			super.writeScripts(response, scripts.toString());
			return null;
		} catch (Exception e) {
			e.printStackTrace();
                                                 //出错,跳转到 用户手工登陆页面
			scripts.append("alert(\"系统错误,请通知管理员\");");
                                                 //执行该句话前,小心你的cookie。
			scripts.append("document.execCommand('ClearAuthenticationCache');");
			scripts.append("location.href='"+ctx + "/accout.do?method=switchLogin';");
			super.writeScripts(response, scripts.toString());
			return null;
		}
	}
	
	public ModelAndView ntlmLogin(HttpServletRequest request,
			HttpServletResponse response){
		String ctx = request.getContextPath();
		StringBuffer scripts = new StringBuffer();
		try {
			String domainController =(String)request.getSession().getAttribute(SESSION_DOMAINCONTROLLER);
			if (logger.isDebugEnabled()) {
				logger.debug("domainController="+domainController); 
			}			
			//用户登陆,开始验证密码
			NtlmPasswordAuthentication ntlm = negotiate(request, response,domainController,false);
			if ( ntlm == null )return null;
			
			//验证用户部分
			//验证用户结束
			
			//自动登陆
			// 登录成功跳转到子系统首页
		} catch (Exception e) {
			e.printStackTrace();
                                                //出错,跳转到 用户手工登陆页面
			scripts.append("alert(\"系统错误,请通知管理员\");");
			scripts.append("document.execCommand('ClearAuthenticationCache');");
			scripts.append("location.href='"+ctx + "/accout.do?method=switchLogin';");
			super.writeScripts(response, scripts.toString());
			return null;
		}
	}
	

 2、切换用户

需要解决的两个问题:1、当能自动登录时,表示当服务器向客户端发出401时,IE自动会用当前用户和密码进行提交,不会弹出window的用户登陆提示框(这是用户要求的,非要用window那个登陆提示框),如何控制登陆提示框是否出来是个问题;

2、客户端凭证无法删除,切换用户后,后台还是第一个用户?

 

解决:

1、控制登陆提示框弹出说明有开关。

2、切换用户或者注销用户后,客户端必须执行document.execCommand('ClearAuthenticationCache');

 

思路:

切换用户,基本没有采用ntlm了,但是用户名和密码验证还是用它的。代码如下(realm随便瞎写什么都行)

	/**
	 * 切换用户
	 */
	public ModelAndView switchLogin(HttpServletRequest request,
			HttpServletResponse response){
		String ctx = request.getContextPath();
		try {
			String auth = request.getHeader("Authorization");
			if ( StringUtils.isBlank(auth)){
				//弹出验证框
				response.setStatus(response.SC_UNAUTHORIZED);   
				response.setHeader("WWW-Authenticate", "Basic realm=\""+realm+"\"");   
				response.flushBuffer();   
				return null;   
			}
			if(auth.startsWith("Basic ")){
				//用户名和密码解密
				String username_pw = new String(Base64.decode(auth.substring(6)));
				String[] array = username_pw.split(":");
				String userDomain = array[0];
                                                                //明文密码
				String password = array[1];
				String[] user_domain = userDomain.split(\\\\);
                                                                //用户需要登陆的域
				String domain = user_domain[0];
                                                                //用户名
				String princal = user_domain[1];
                                                                //获得域对应的IP
				String domainController = getDomainController(domain);
				//去域验证用户名和密码
                                                               boolean validate = validateDomainUser(domainController,princal, password);
				//用户验证成功 ,通过用户名登陆
				// 登录成功跳转到子系统首页
				return null;
			}else{
				response.setStatus(response.SC_UNAUTHORIZED);   
				response.setHeader("WWW-Authenticate", "Basic realm=\""+realm+"\"");   
				response.flushBuffer();   
				return null;   
			}
		} catch (Exception e) {
			e.printStackTrace();
			scripts.append("alert(\"系统错误,请通知管理员\");");
			scripts.append("document.execCommand('ClearAuthenticationCache');");
			scripts.append("location.href='"+ctx + "/accout.do?method=switchLogin';");
			super.writeScripts(response, scripts.toString());
			return null;
		}
	}

 

/**
	 * 验证域用户名和密码
	 */
	private boolean validateDomainUser(String domainController,String username,String password){
		try {
			UniAddress mydomaincontroller = UniAddress.getByName( domainController );
			NtlmPasswordAuthentication mycreds1 = new NtlmPasswordAuthentication( "", username, password );
			SmbSession.logon( mydomaincontroller, mycreds1 );
			return true;
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("validateDomainUser(String, String, String) - Exception e=" + e); //$NON-NLS-1$
			}
			
		} 
		return false;
	}

 

1
0
分享到:
评论
8 楼 弦月001 2014-09-25  
楼主目前对jcifs还记得吗?我目前在做jcifs sso研究开发,我想请问一下楼主,控制登陆提示框弹出说明有开关,这个开关是在域服务器控制吗?
不胜感激。
7 楼 jusescn 2013-01-04  
effort.mjb 写道
博主好,请问怎么样才能直接获取到当前客户端的用户的域信息呢?(注:我用js试过,用active控件试过,可以读取,但是IE总是会弹出安全验证信息,这个客户是不能接受的。),希望博主能给点详细的建议,最好能给个示例代码,非常感谢

effort.mjb 写道
博主好,请问怎么样才能直接获取到当前客户端的用户的域信息呢?(注:我用js试过,用active控件试过,可以读取,但是IE总是会弹出安全验证信息,这个客户是不能接受的。),希望博主能给点详细的建议,最好能给个示例代码,非常感谢

NtlmPasswordAuthentication ntlm = null; 
UniAddress dc = null; 
      String msg = req.getHeader( "Authorization" ); 
      boolean offerBasic = enableBasic && (insecureBasic || req.isSecure()); 
      if( msg != null && (msg.startsWith( "NTLM " ) || (offerBasic && msg.startsWith("Basic ")))) { 
    if (msg.startsWith("NTLM ")) { 
        dc = UniAddress.getByName( domainController,true ); 
              byte[] challenge = SmbSession.getChallenge( dc ); 
              if(( ntlm = NtlmSsp.authenticate( req, resp, challenge )) == null ) { 
                return null; 
              } 
       } 
 
if (ntlm != null){
   ntlm.getSome();//这里可以调用获得用户的域信息
}
6 楼 effort.mjb 2012-12-23  
博主好,请问怎么样才能直接获取到当前客户端的用户的域信息呢?(注:我用js试过,用active控件试过,可以读取,但是IE总是会弹出安全验证信息,这个客户是不能接受的。),希望博主能给点详细的建议,最好能给个示例代码,非常感谢
5 楼 jusescn 2012-12-21  
yingzhor 写道
博主,您好。 如果,我想扩展一下,如果客户端不再域内,直接把请求重定向到我指定的页面。 而不弹出密码输入框,我应该如何做。

望指点一二。


直接获得用户的域信息,如果没有就跳转下。easy.
4 楼 yingzhor 2012-12-10  
博主,您好。 如果,我想扩展一下,如果客户端不再域内,直接把请求重定向到我指定的页面。 而不弹出密码输入框,我应该如何做。

望指点一二。
3 楼 yuleiye 2012-12-10  
我最近在研究Jcifs用在域验证的问题,碰到不少问题,想请教您,方便的话加我QQ:38768264
2 楼 tevez0014 2012-09-17  
今天遇到个问题,jcifs貌似不支持 post提交方式。请问有遇到类似的问题么?
1 楼 zay1007 2011-12-14  
初学者向你请假域验证的问题 请加我q 5173202

相关推荐

    同父域sso单点登录源码SSO_SameFather

    通过研究和理解SSO_SameFather源码,你可以学习如何在同父域环境中实施SSO,包括如何处理Cookie、如何设计安全的票据验证流程以及如何在多应用环境中集成SSO。这将有助于你提升在Web身份验证和安全领域的专业技能。

    同域sso单点登录源码

    同域SSO特指在相同域名下的多个子系统之间实现单点登录,这对于大型网站或者企业内部应用集群尤为实用。下面将详细阐述同域SSO单点登录的核心概念、实现原理以及关键步骤。 1. **核心概念** - **票据(Ticket)**...

    jcifs-ext-0.9.4.jar

    jcifs-ext是jcifs的扩展版本,增加了更多的功能和改进,以更好地支持与Windows环境的交互,如AD域服务的集成。 Active Directory是微软提供的一种目录服务,用于管理和存储有关网络对象(如用户、计算机、组等)的...

    jcifs-1.3.15.jar

    jcifs实现SSO,SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是...

    jcifs包 源代码

    3. **安全机制**:jcifs支持NTLM和LAN Manager身份验证协议,这些都是Windows网络中常见的安全认证方式。研究这部分代码,你可以学习到如何在Java中实现这些安全协议。 4. **线程与并发**:在处理网络请求时,jcifs...

    jcifs实现集成登录

    5. **兼容性**:jcifs不仅适用于Java SE环境,还支持Java EE环境,因此可以在Web应用、企业级服务器和桌面应用等多种场景下实现SSO。 6. **扩展性**:jcifs的开源性质使其具备良好的可扩展性,开发者可以根据实际...

    SSO_same_domain

    6. **Spring Security SSO**:对于Java开发者,Spring Security提供了一套强大的安全框架,包括对SSO的支持。在同域中,可以通过Spring Security配置实现各个应用之间的SSO。 7. **安全性考虑**:同域SSO虽然方便,...

    jcifs-ext-0.9.4.jar/jcifs-1.3.3.jar

    描述中提到的"单点登陆(Single Sign-On,简称SSO)"是一种身份验证机制,允许用户在一次登录后访问多个相互信任的应用系统,无需为每个系统分别输入凭证。CAS(Central Authentication Service)是一个广泛使用的...

    jcifs-1.3.18.jar

    jcifs-1.3.18.jar包,SSO单点登录

    sso单点登陆1

    在SSO系统中,通常分为同域SSO和跨域SSO两种类型: 1. 同域SSO:所有需要SSO的应用系统共享同一个顶级域名,如web1.x.com、web2.x.com、web3.x.com都在x.com域名下。在这种情况下,可以通过设置全局的Cookie来实现...

    单点登录(SSO)-同域名、不同域名

    1. **Cookie域的设置**:在SSO系统中登录后,可以通过设置Cookie的域为顶级域(如`.example.com`),使得所有子域都能访问到该Cookie。这解决了Cookie跨域的问题。 2. **Session共享**:虽然各应用系统间默认不会...

    C#单点登陆组件源码SSO

    单点登录(Single Sign-On,简称SSO)是一种网络身份验证机制,允许用户在一个系统上登录后,无需再次验证即可访问多个相互关联的系统。在IT行业中,SSO技术广泛应用于企业级应用,提高用户体验,简化管理并增强安全...

    jcifs的拓展包ext

    CAS是一个开放源代码项目,旨在提供单一登录(Single Sign-On, SSO)服务,用于管理多个应用系统的用户认证。在CAS 4.2.1版本中,可能依赖jcifs库来实现对Windows网络共享资源的访问,例如读取或写入网络上的文件。...

    sso 同域之下简单模拟

    SSO(Single Sign-On)单点登录是一种身份验证机制,允许用户在多个应用系统中只需登录一次,即可访问所有相互信任的应用系统,无需再次进行身份验证。在这个“sso 同域之下简单模拟”中,我们将探讨如何在同一个...

    SSO单点登陆解决方案

    SSO(Single Sign-On)单点登录解决方案旨在提供一个统一的身份验证入口,满足集团多个成员网站的身份验证需求。该方案的主要目标是实现单点登录,提高用户体验,降低成员网站的登录负载,并体现集团大平台、大渠道...

    SSO之返回多个值

    2. **Keycloak**:是一个开源的身份和访问管理解决方案,支持SSO和多因素认证,提供图形化的管理界面。 3. **Okta**:是一个云身份管理平台,提供SSO服务,支持多种应用和协议。 综上所述,SSO处理返回多个值的关键...

    sharepoint sso配置和使用

    3. ** Federation Services**:如Active Directory Federation Services (AD FS) 可用于在不同信任域之间建立SSO,使得用户可以在多个组织的应用之间进行无感知的切换。 4. **配置步骤**:配置SharePoint SSO通常...

    spring boot 实现SSO单点登陆

    spring boot整合spring security 实现SSO单点登陆 完整DEMO. ...2、先后启动SsoServer、sso-resource、sso-client1、sso-client2 3、访问http://sso-taobao:8083/client1/ 或 http://sso-tmall:8084/client2/

    推荐给大家 sso介绍

    2. **SSO-enable的应用系统**:为了支持SSO功能,现有的应用系统需要进行改造,以支持SSO API。这意味着应用系统需要放弃原有的认证机制,转而依赖SSO Entry提供的认证服务。 3. **统一的认证与权限信息库**:为了...

Global site tag (gtag.js) - Google Analytics