原谅我一开篇就骂人,折腾坏了。
====================================
介绍下背景: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;
}
分享到:
相关推荐
同父域sso单点登录源码SSO_SameFather
同域sso单点登录源码
jcifs实现SSO,SSO英文全称Single Sign On,单点登录。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/
jcifs-ext-0.9.4.jar,cas集成AD域用到的拓展包
jcifs-1.3.18.jar包,SSO单点登录
2. Loong SSO支持 客户端 跨服务器、跨域名、跨应用、跨开发语言 3. SSO客户端调用, 支持各种 能用于WEB开发的 语言(比如 perl、PHP、Ruby、Python、Java)等等语言 4. SSO客户端开发简单,修改量非常小。 只要修改...
SSO的简单实现SSO的简单实现SSO的简单实现
单点登录(SSO)-同域名、不同域名
一个实现SSO方案的构想SSO单点登陆解决方案
sso单点登录sso单点登录sso单点登录sso单点登录
所有应用系统能够识别和提取Token信息要实现SSO的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对Token进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过...
3. 支持MySQL8+ 4. 修改PHPCMS目录为CMS 5. 验证码修改 6. 支持H5上传,移除Flash上传 7. 修改后台缩略图裁切图片,移除Flash裁切改为H5裁切 8. 后台附件上传修改为H5上传,会员头像上传修改为H5上传 9. 修改后台...
SSO开源项目,基于https的,大家可以在网上搜索研究一下!
Laravel开发-sso Laravel 4的SSO包。
自己动手写sso java 简单实现 使用 目前流行的 springboot 做基础
sso各子系统在集成sso后可通过sso client 端提供的一下接口获取sso数据
sso单点登录ppt.ppt
用户SSO接口API源代码,适用于各终端联合登陆,支持用户名密码,短信,crc... |__SSO.Lib |__SSO.BLL |__SSO.DAL |__SSO.Domain |__SSO.ManageFacade |__SSO.Management |__SSO.Open |__SSO.SDK |__SSO.UTILS
浏览器的单点登录,基于session的或者是基于app的基于token的,app类似SPA方式,但是有个不同点,就是在多个app或者多个SPA下怎么做单点登录。一开始以为很容易。...在涉及到多个域之间换取token还是有一些问题。