AD密码与Lotus密码实现同步
我们主要通过.net编写的程序来实现用户通过web方式更改密码及重置密码,之前是通过在线传输的方式来实现,AD密码与Domino同步,但是我们发现使用后有很多bug,比如+、&特殊字符传输后密码失效的问题,然后还有一个就是当用户在同一时间大量修改或者重置会出现遗漏的问题,比如50个用户同时修改,会有1-2个不生效,其实不生效的原因跟服务器的性能有一定关系,为了解决该问题,我们通过另外一个方式,实现AD密码与Domino密码的同步,我们在后台中添加程序,将用户修改、重置后的信息写入到数据库下(在此我用SQL),然后Domino再通过程序从sql下提取,完成密码重置工作,然后给sql一个回执,这样就实现了AD密码到Domino密码的同步了。
新建SQL服务器,然后创建数据库、表即可。
数据库名称为:PassworInfor
表:PassInfo
表:
简易查询:
然后通过程序设置,将用户在传输过程中的数据,写入到SQL数据库下
Passinfo using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration; namespace ChangePassword.Models.Mapping { public class PassInfoMap : EntityTypeConfiguration<PassInfo> { public PassInfoMap() { // Primary Key this.HasKey(t => t.Id); // Properties this.Property(t => t.Username) .HasMaxLength(50); this.Property(t => t.Password) .HasMaxLength(50); // Table & Column Mappings this.ToTable("PassInfo"); this.Property(t => t.Id).HasColumnName("Id"); this.Property(t => t.Username).HasColumnName("Username"); this.Property(t => t.Password).HasColumnName("Password"); this.Property(t => t.DateTime).HasColumnName("DateTime"); this.Property(t => t.Statue).HasColumnName("Statue"); } } }
using System; using System.Collections.Generic; namespace ChangePassword.Models { public partial class PassInfo { public int Id { get; set; } public string Username { get; set; } public string Password { get; set; } public Nullable<System.DateTime> DateTime { get; set; } public Nullable<int> Statue { get; set; } } }
using System.Data.Entity; using System.Data.Entity.Infrastructure; using ChangePassword.Models.Mapping; namespace ChangePassword.Models { public partial class PasswordInfoContext : DbContext { static PasswordInfoContext() { Database.SetInitializer<PasswordInfoContext>(null); } public PasswordInfoContext() : base("Name=PasswordInfoContext") { } public DbSet<PassInfo> PassInfoes { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new PassInfoMap()); } } }
/// 重置密码 /// </summary> public void SetPassword() { string Rs = ""; string sItCode = (string)Session["VerIDcode"]; Session.Remove("VerIDcode"); string sNewPwd = Request["sNewPwd"]; { ADOperator ao = new ADOperator(); int y = ao.IsUserExistsByAccount(sItCode); if (y == 1) { string username = ConfigurationManager.AppSettings["AutoRestAdminUser"].ToString(); ; string password = ConfigurationManager.AppSettings["AutoRestAdminPwd"].ToString(); ; DirectoryEntry de = ADOperator.GetDirectoryObject(username, password); //(DirectoryEntry)Session["admin"]; int z = ao.SetPasswordByAccount(de, sItCode, sNewPwd); if (z == 1) { try { PassInfo passInfo = new PassInfo(); passInfo.Password = sNewPwd; passInfo.Statue = 0; passInfo.Username = sItCode; passInfo.DateTime = DateTime.Now; psContext.PassInfoes.Add(passInfo); psContext.SaveChanges(); } catch (Exception ex) { //处理 数据库错误 } Rs = "CS"; //调用Domino密码修改 changeDominoPwd(sItCode, sNewPwd); HttpContext.Cache.Remove(sItCode + "_PrivateEmail"); HttpContext.Cache.Remove(sItCode + "_code"); HttpContext.Cache.Remove(sItCode + "_reset_uid"); HttpContext.Cache.Remove(sItCode + "_reset_uname"); optionLog log = new optionLog(); log.idCode = sItCode; log.opDateTime = DateTime.Now; log.opType = "密码重置"; log.opUser = username; dbContext.optionLogs.Add(log); dbContext.SaveChanges(); } else { Rs = "FA"; } } else { Rs = "NU"; } } Response.Write(Rs.ToString()); } #endregion /// <summary> /// 验证用户是否登录 /// </summary> /// <returns></returns> private bool VerifyIfSignedIn(string itcode) { if (itcode == null) { return false; } string ipAddress = this.GetIPAddress(); isValidate isValidateReq = new isValidate(new isValidateBody(itcode, ipAddress)); var isValidateResponse = soaService.isValidate(isValidateReq); if (isValidateResponse.Body.isValidateResult) { System.Web.HttpContext.Current.Session["itcode"] = itcode; } return isValidateResponse.Body.isValidateResult; } //服务器获取用户IP private string GetIPAddress() { string user_IP = string.Empty; if (System.Web.HttpContext.Current.Request.ServerVariables["HTTP_VIA"] != null) { if (System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null) { user_IP = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString(); } else { user_IP = System.Web.HttpContext.Current.Request.UserHostAddress; } } else { user_IP = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"].ToString(); } return user_IP; } // MD5 加密中文 public static string GetMD5(string str) { byte[] b = System.Text.Encoding.Default.GetBytes(str); b = new System.Security.Cryptography.MD5CryptoServiceProvider().ComputeHash(b); string ret = ""; for (int i = 0; i < b.Length; i++) { ret += b[i].ToString("x").PadLeft(2, '0'); } return ret; } public JsonResult CheckValidateCode(string idCode,string userNumber,string validateCode) { string tempValidateCode = (string)Session["ValidateCode"]; if (validateCode == tempValidateCode) { }
add name="PasswordInfoContext" connectionString="Data Source=10.1.1.88;Initial Catalog=PasswordInfo;Persist Security Info=True;User ID=sa;Password=password;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
接下来准备domino配置环境
新建空的数据库resetpw.nsf,然后我们用来跑试图及代理程序
首先是创建代理:我们需要创建4个代理,分别为:agtOenDataSource、agtSaveDataSource、resetpw、获取用户次数代理
首先是AgtOpenDatasource
%REM Agent agtOpenDataSource Created 2014-5-13 by admin/digioa Description: Comments for Agent %END REM OptionPublic OptionDeclare Sub Initialize Dim session AsNew NotesSession Dim db As NotesDatabase Dim view As NotesView Dim doc As NotesDocument OnErrorGoTo err_line Set note = session.Documentcontext Set view = db.Getview("vwDataSource") Set doc = view.Getfirstdocument() IfNot doc IsNothingThen note.strServerIP = doc.strServerIP(0) note.strUserName = doc.strUserName(0) note.strPassWord = doc.strPassWord(0) note.strDataSource = doc.strDataSource(0) note.strKeyCol = doc.strKeyCol(0) EndIf ExitSub err_line: MsgBox"agtOpenDataSource Initialize error :"+Error+" at line:"+CStr(Erl) EndSub
agtSaveDataSource
%REM Agent agtSaveDataSource Created 2014-5-13 by admin/digioa Description: Comments for Agent %END REM OptionPublic OptionDeclare Sub Initialize Dim session AsNew NotesSession Dim db As NotesDatabase Dim doc As NotesDocument Dim note As NotesDocument OnErrorGoTo err_line Set note = session.Documentcontext Set doc = view.Getfirstdocument() IfNot doc IsNothingThen doc.strServerIP = note.strServerIP(0) doc.strUserName = note.strUserName(0) doc.strPassWord = note.strPassWord(0) doc.strDataSource = note.strDataSource(0) doc.strTableName = note.strTableName(0) doc.strUserCol = note.strUserCol(0) Call doc.save(False,False) Else note.Form = "frmDataSource" Call note.save(False,False) EndIf Print|<script>| Print|alert("保存成功!");| Print|window.location.href = ExitSub err_line: MsgBox"agtSaveDataSource Initialize error :"+Error+" at line:"+CStr(Erl) EndSub
获取用户次数代理
%REM Agent 修改用户名密码 Created 2013-12-15 by administrator/iiosoft Description: Comments for Agent %END REM OptionPublic OptionDeclare Dim session As NotesSession Dim db As NotesDatabase Dim doc As NotesDocument Dim view As NotesView Dim strhtml AsString Sub Initialize OnErrorGoTo err_handle Dim i AsInteger Dim tmp AsInteger Set session = New NotesSession Set db = session.Currentdatabase Set doc = session.Documentcontext Set view = db.Getview("ListByPsnView") Set olddoc = view.Getfirstdocument() WhileNot namesdoc IsNothing tmp = 0 arr = namesdoc.Getitemvalue("ShortName") For i = 0ToUBound(arr) tmp = tmp + dc.Count EndIf Next strhtml = strhtml Set namesdoc = namesview.Getnextdocument(namesdoc) Wend strhtml = |<table><tr><td>用户名</td><td>次数</td></tr>|+strhtml+|</table>| Call doc.Replaceitemvalue("RTFTable", strhtml) ExitSub err_handle: MsgBox session.Currentdatabase.Filepath + session.Currentagent.name MsgBoxError MsgBoxErl EndSub
接下来是resetpwd,我们定义是java类
首先是SQLServer08.java
import java.sql.Connection; import java.sql.DriverManager; public class SQLServer08 { private static final String MYDBCN="com.microsoft.sqlserver.jdbc.SQLServerDriver"; private static SQLServer08 dbcn=null; Connection conn=null; private SQLServer08(){ try { Class.forName(this.MYDBCN); } public static SQLServer08 getSqlDBCN(){ if(dbcn==null) dbcn=new SQLServer08(); return dbcn; String SQLUSER = user; String SQLPASSWORD = password; conn=DriverManager.getConnection(SQLURL,SQLUSER,SQLPASSWORD); } catch (Exception e) { e.printStackTrace(); } return conn; } }
第二个是javaagent
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import lotus.domino.*; public static String sqltable = ""; public static String sqlusercol = ""; public static String sqlpwcol = ""; public static String sqltimecol = ""; public static String sqlkeycol = ""; public static String resetString = "密码已重置"; public void NotesMain() { Document doclog = null; Name nm = null; String userinfo = ""; String uname = ""; try { Session session = getSession(); dbname = session.getDatabase("","names.nsf"); if(dbname != null){ vw1 = dbname.getView("($VIMPeople)"); //按层次名称,如:dev01/local userinfo = GetUserAndPW(); if(!"".equals(userinfo)){ String[] arrinfo = userinfo.split("\\(###\\)"); boolean resetflag = false; boolean updsqlflag = false; boolean logflag = false; uname = user[0]; if(uname.indexOf("/")!=-1){ docname = vw1.getDocumentByKey(uname,true); }else{ docname = vw2.getDocumentByKey(uname,true); } if(docname!=null){ resetflag = resetUserpw(session,docname,user[1]); if(resetflag){ updsqlflag = updSqlState(user[3]); if(updsqlflag){ logflag = System.out.println("用户["+uname+"]重置密码操作失败!"); } }else{ System.out.println("重置密码:names库中未找到用户["+uname+"]的文档"); } } } } }else{ System.out.println("重置密码失败:SQL数据库配置不完整,请检查配置!"); } }else{ System.out.println("重置用户密码失败:无法获取/打开names库"); } } catch(Exception e) { e.printStackTrace(); } finally { try { if(dbname != null){ dbname.recycle(); } if(vw1 != null){ vw1.recycle(); } if(vw2 != null){ vw2.recycle(); } if(docname != null){ docname.recycle(); } if(nm != null){ nm.recycle() } if(doclog != null){ doclog.recycle(); } } catch(Exception e) { System.out.println(e); } } }
//获取SQL数据库配置 public boolean GetDataSourceConfig(Document docConfig){ boolean gflag = false; if(docConfig!=null){ try { sqlserver = docConfig.getItemValueString("strServerIP") sqlpassword = docConfig.getItemValueString("strPassWord"); sqldatabase = docConfig.getItemValueString("strDataSource"); sqltable = docConfig.getItemValueString("strTableName"); if("".equals(sqlserver) || "".equals(sqluser) || "".equals(sqlpassword) || "".equals(sqldatabase) || "".equals(sqltable) || "".equals(sqlusercol) || "".equals(sqlpwcol) || gflag = false; }else{ gflag = true; } } catch (NotesException e) { } } return gflag; } //连接SQL Server2008获取用户名、密码及时间 public String GetUserAndPW(){ String userinfo = ""; String sql = "select * from "+sqltable+" where "+sqlpwcol+"!=?"; Connection cn=null; PreparedStatement pst=null; ResultSet rs=null; try{ cn=DBCNfactory.getSqlDBCN().getConnection(sqlserver,sqldatabase,sqluser,sqlpassword); if("".equals(userinfo)){ userinfo = }else{ userinfo = userinfo+rowsp+rs.getString(sqlusercol)+colsp+rs.getString(sqlpwcol)+colsp+rs.getString(sqltimecol)+colsp+rs.getString(sqlkeycol); } } }catch(Exception e){ System.out.println(e); } return userinfo; } //重置密码 public boolean resetUserpw(Session session,Document docname,String pw){ boolean oflag = false; if(docname!=null){ String reset = "@Password('"+pw+"')"; try { docname.replaceItemValue("HTTPPassword",session.evaluate(reset)); docname.computeWithForm(false, false); docname.save(true,true); oflag = true; } catch (Exception e) { System.out.println(e); } } return oflag; } //将用户密码标识为已重置 public boolean updSqlState(String uid){ boolean oflag = false; try{ Connection cn=DBCNfactory.getSqlDBCN().getConnection(sqlserver,sqldatabase,sqluser,sqlpassword); String sql="update "+sqltable+" set "+sqlpwcol+"=? where "+sqlkeycol+"=?"; PreparedStatement pst=cn.prepareStatement(sql) oflag = true; }catch(Exception e){ e.printStackTrace(); } return oflag; } //记录日志 public boolean recordLog(Database db,Document doc,String un,String pw,String stime){ boolean oflag = false; try { doc = db.createDocument(); doc.replaceItemValue("Form","internetpwd"); doc.replaceItemValue("UserName",un); doc.replaceItemValue("UserPassword",pw); doc.replaceItemValue("Time",stime); doc.save(true,true); oflag = true; } catch (NotesException e) { System.out.println(e); } return oflag; } }
设置完后,我们将resetpwd代理程序定义计划任务:
接下来我们创建表单:
Internetpwd:
<div style="display:none"> 当前登录人: </div> <h1>邮箱密码重置系统<h1> <table border="1" cellspacing="0"> <tr> <td>用户名:</td> <td></td> </tr> <tr> <td>新密码:</td> <td></td> </tr> <tr> <td colspan="2"> </td> </tr> </table>
SQP数据库配置:
<div style="display:none"> </div> <table> <tr> <td>服务器IP:</td> <td></td> </tr> <tr> <td>登录名:</td> <td></td> </tr> <tr> <td>密码:</td> <td></td> </tr> <tr> <td>数据库名:</td> <td></td> </tr> <tr> <td>表名:</td> <td></td> </tr> <tr> <td>用户名列:</td> <td></td> </tr> <tr> <td>密码列:</td> <td></td> </tr> <tr> <td>时间列:</td> <td></td> </tr> <tr> <td>关键字列:</td> <td></td> </tr> <tr> <td colspan="2"> <input type="button" value="保存" onclick="mysubmit();"/> </td> </tr> </table>
接下来我们配置相关的显示试图:因为我要记录、统计用户修改密码的信息
SQL数据库配置试图
Listview试图
用户修改密码次数统计
创建完,访问效果为:
接下来我们部署配置文档:
1.从Web端打开SQL数据源配置文档,输入如下地址:
http://iio-mail01.iiosoft.com/resetpw.nsf/frmDataSource?openform
2.根据页面提示,填写SQL相关信息(字段均需填写),保存即可。
字段说明:
服务器IP:SQL Server2008 服务器IP地址
登录名:SQL Server2008数据库登录帐户名
密码:SQL Server2008数据库登录帐户的密码
数据库名:SQL Server2008数据库名
表名:SQL Server2008数据库存储用户及密码相关信息的表名
用户名列:表中存储用户名的列名
密码列:表中存储用户密码的列名
时间列:表中存储用户重置密码的时间列名
关键字列:表的关键字列名(标识列id)
接着我们将sqljdbc4.jar包部署至domino安装目录下,路径:Lotus\Domino\jvm\lib\ext
注:配置完后,我们需要重启domino程序,不然不生效。
首先我的AD、domino内分别注册username=user01的用户,第一次我们闲将密码重置为:123456,然后我们将密码修改为123123abc,看看domino的密码是否能同步:
接下来我们查看SQL下的日志
接下来就差domino运行代理了,运行代理后,domino下的user01的密码被重置为:123123abc后,数据库的密码列将标记为已重置。我们有两种方式让代理快速运行:
1:运行该链接http://iio-mail01.iiosoft.com/resetpw.nsf/%20resetpw?openAgent
2.在控制台敲打命令,其实命令跟url的一样
Tell amgr run “resetpw.nsf” ‘resetpw?apenAgent’
代理生效后,我们查看SQL下的密码状态:明文的密码已经标记其他字符
接着我们查看domino的试图记录log,然后user01是否有更改密码的记录
最后我们就得真实测试user01的密码是否正确了。
测试完全正常,最后我们发现SQL数据库配置试图,可以看见敏感信息,密码:我们可以见该列隐藏,这样就不降低危险性了。
我们来隐藏该列:
设置隐藏即可:应藏后,我们通过desinger.nsf还是可以看见的。
我们在通过notes客户端打开查看。我们发现密码那一项没了。
注:附件中的源代码下载后需要将扩展名更改为.7z格式然后解压即可使用。