Browse Source

Merge branch 'feature/nh_idap_0111'

hsun 1 year ago
parent
commit
c35f64bb66

+ 8 - 1
controllers/data_manage/chart_info.go

@@ -2271,7 +2271,14 @@ func (this *ChartInfoController) ChartInfoBase64Upload() {
 	select {
 	case <-time.After(30 * time.Second):
 		utils.FileLog.Info("执行超过30秒 杀死超时进程")
-		cmd.Process.Kill()
+		e := cmd.Process.Kill()
+		if e != nil {
+			fmt.Println("cmd kill err: ", e.Error())
+			utils.FileLog.Info(fmt.Sprintf("cmd kill err: %s", e.Error()))
+			br.Msg = "图片生成失败"
+			br.ErrMsg = "图片生成失败, 执行超时" + e.Error()
+			return
+		}
 		fmt.Println("timeout kill process")
 	case <-doneChannel:
 		fmt.Println("done")

+ 8 - 1
controllers/data_manage/future_good/future_good_chart_info.go

@@ -2419,7 +2419,14 @@ func (this *FutureGoodChartInfoController) ChartInfoBase64Upload() {
 	select {
 	case <-time.After(30 * time.Second):
 		utils.FileLog.Info("执行超过30秒 杀死超时进程")
-		cmd.Process.Kill()
+		e := cmd.Process.Kill()
+		if e != nil {
+			fmt.Println("cmd kill err: ", e.Error())
+			utils.FileLog.Info(fmt.Sprintf("cmd kill err: %s", e.Error()))
+			br.Msg = "图片生成失败"
+			br.ErrMsg = "图片生成失败, 执行超时" + e.Error()
+			return
+		}
 		fmt.Println("timeout kill process")
 	case <-doneChannel:
 		fmt.Println("done")

+ 32 - 15
controllers/sys_admin.go

@@ -442,21 +442,36 @@ func (this *SysAdminController) Add() {
 		}
 	}
 
-	pwdByte, err := base64.StdEncoding.DecodeString(req.Password)
-	if err != nil {
-		br.Msg = "解析数据失败"
-		br.ErrMsg = "解析数据失败,Err:" + err.Error()
-		return
+	var originPass, pass string
+	if req.Password != "" {
+		pwdByte, e := base64.StdEncoding.DecodeString(req.Password)
+		if e != nil {
+			br.Msg = "解析数据失败"
+			br.ErrMsg = "解析数据失败,Err:" + e.Error()
+			return
+		}
+		originPass = string(pwdByte)
 	}
-	pwdStr := string(pwdByte)
-	//pwdStr = strings.ToLower(pwdStr)
-	if pwdStr == "" {
-		br.Msg = "请输入密码"
-		return
+	// 系统用户-密码必填且需要校验密码格式
+	if req.IsLdap == 0 {
+		if originPass == "" {
+			br.Msg = "请输入密码"
+			return
+		}
+		if !utils.CheckPwd(originPass) {
+			br.Msg = "密码格式错误,请重新输入"
+			return
+		}
+		pass = utils.MD5(originPass)
 	}
-	if !utils.CheckPwd(pwdStr) {
-		br.Msg = "密码格式错误,请重新输入"
-		return
+	// 域用户-密码非必填(实际登录用不到这个密码)
+	if req.IsLdap == 1 {
+		if originPass != "" {
+			pass = utils.MD5(originPass)
+		} else {
+			// 未填写的话给个初始密码即可
+			pass = utils.MD5(utils.LdapInitPassword)
+		}
 	}
 
 	// 员工工号
@@ -465,7 +480,7 @@ func (this *SysAdminController) Add() {
 	admin := new(system.Admin)
 	admin.AdminName = req.AdminName
 	admin.RealName = req.RealName
-	admin.Password = utils.MD5(pwdStr)
+	admin.Password = pass
 	admin.LastUpdatedPasswordTime = time.Now().Format(utils.FormatDateTime)
 	admin.Enabled = 1
 	admin.LastLoginTime = time.Now().Format(utils.FormatDateTime)
@@ -519,6 +534,7 @@ func (this *SysAdminController) Add() {
 	admin.City = req.City
 	admin.CityCode = req.CityCode
 	admin.TelAreaCode = req.TelAreaCode
+	admin.IsLdap = req.IsLdap
 	err = system.AddAdmin(admin)
 	if err != nil {
 		br.Msg = "新增失败"
@@ -737,9 +753,10 @@ func (this *SysAdminController) Edit() {
 	adminInfo.EmployeeId = req.EmployeeId
 	adminInfo.Email = req.Email
 	adminInfo.TelAreaCode = req.TelAreaCode
+	adminInfo.IsLdap = req.IsLdap
 	cols := []string{
 		"AdminName", "RealName", "LastUpdatedTime", "Mobile", "RoleId", "RoleName", "Enabled", "Authority",
-		"Position", "RoleTypeCode", "Province", "ProvinceCode", "City", "CityCode", "EmployeeId", "Email", "TelAreaCode",
+		"Position", "RoleTypeCode", "Province", "ProvinceCode", "City", "CityCode", "EmployeeId", "Email", "TelAreaCode", "IsLdap",
 	}
 	if e := adminInfo.Update(cols); e != nil {
 		br.Msg = "编辑失败"

+ 131 - 47
controllers/user_login.go

@@ -144,7 +144,7 @@ func (this *UserLoginController) GetVerifyCode() {
 			br.Msg = "请输入手机号"
 			return
 		}
-		if req.TelAreaCode == "86" && !utils.ValidateMobileFormatat(req.Mobile) {
+		if req.TelAreaCode == utils.TelAreaCodeHome && !utils.ValidateMobileFormatat(req.Mobile) {
 			br.Msg = "您的手机号输入有误, 请检查"
 			return
 		}
@@ -251,13 +251,14 @@ func (this *UserLoginController) Login() {
 
 	// 入参
 	type UserLoginReq struct {
-		LoginType  int    `description:"登录方式: 1-账号; 2-手机号; 3-邮箱"`
-		Username   string `description:"账号"`
-		Password   string `description:"密码"`
-		Mobile     string `description:"手机号"`
-		Email      string `description:"邮箱"`
-		VerifyCode string `description:"验证码"`
-		ReqTime    string `description:"登录时间戳"`
+		LoginType   int    `description:"登录方式: 1-账号; 2-手机号; 3-邮箱"`
+		Username    string `description:"账号"`
+		Password    string `description:"密码"`
+		Mobile      string `description:"手机号"`
+		Email       string `description:"邮箱"`
+		VerifyCode  string `description:"验证码"`
+		ReqTime     string `description:"登录时间戳"`
+		TelAreaCode string `description:"区号"`
 	}
 	var req UserLoginReq
 	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
@@ -334,45 +335,71 @@ func (this *UserLoginController) Login() {
 			return
 		}
 
-		// 账号密码校验
-		dbPass := utils.MD5(fmt.Sprintf("%s%s%s", accountUser.Password, utils.UserLoginSalt, req.ReqTime))
-		if req.Password != dbPass {
-			br.Ret = models.BaseRespCodeLoginErr
-			br.Msg = "登录失败, 账号或密码错误"
-			// 错误密码计数, 超过6次标记异常
-			if !utils.Rc.IsExist(errPassKey) {
-				_ = utils.Rc.Put(errPassKey, 1, utils.GetTodayLastSecond())
+		// 系统用户-账号密码校验
+		if accountUser.IsLdap == 0 {
+			dbPass := utils.MD5(fmt.Sprintf("%s%s%s", accountUser.Password, utils.UserLoginSalt, req.ReqTime))
+			if req.Password != dbPass {
+				br.Ret = models.BaseRespCodeLoginErr
+				br.Msg = "登录失败, 账号或密码错误"
+				// 错误密码计数, 超过6次标记异常
+				if !utils.Rc.IsExist(errPassKey) {
+					_ = utils.Rc.Put(errPassKey, 1, utils.GetTodayLastSecond())
+					return
+				}
+				errNum, _ := utils.Rc.RedisInt(errPassKey)
+				errNum += 1
+				if errNum < 6 {
+					_ = utils.Rc.Put(errPassKey, errNum, utils.GetTodayLastSecond())
+					return
+				}
+				// 标记异常登录, 重置计数
+				br.Ret = models.BaseRespCodeAbnormalLogin
+				br.Msg = "账号异常, 请进行手机号/邮箱校验"
+				_ = utils.Rc.Put(abnormalKey, "true", utils.GetTodayLastSecond())
+				_ = utils.Rc.Delete(errPassKey)
 				return
 			}
-			errNum, _ := utils.Rc.RedisInt(errPassKey)
-			errNum += 1
-			if errNum < 6 {
-				_ = utils.Rc.Put(errPassKey, errNum, utils.GetTodayLastSecond())
+
+			if accountUser.Enabled == 0 {
+				br.Msg = "您的账号已被禁用, 如需登录, 请联系管理员"
+				br.ErrMsg = fmt.Sprintf("账号已被禁用, 登录账号: %s, 账户名称: %s", accountUser.AdminName, accountUser.RealName)
 				return
 			}
-			// 标记异常登录, 重置计数
-			br.Ret = models.BaseRespCodeAbnormalLogin
-			br.Msg = "账号异常, 请进行手机号/邮箱校验"
-			_ = utils.Rc.Put(abnormalKey, "true", utils.GetTodayLastSecond())
-			_ = utils.Rc.Delete(errPassKey)
-			return
-		}
 
-		if accountUser.Enabled == 0 {
-			br.Msg = "您的账号已被禁用, 如需登录, 请联系管理员"
-			br.ErrMsg = fmt.Sprintf("账号已被禁用, 登录账号: %s, 账户名称: %s", accountUser.AdminName, accountUser.RealName)
-			return
+			// 异常登录-是否登录间隔大于60天
+			if isAbnormal == "" {
+				abnormalTime := time.Now().AddDate(0, 0, -60)
+				lastLogin, _ := time.ParseInLocation(utils.FormatDateTime, accountUser.LastLoginTime, time.Local)
+				if !lastLogin.IsZero() && lastLogin.Before(abnormalTime) {
+					br.Msg = "请进行异常登录校验"
+					br.Ret = models.BaseRespCodeAbnormalLogin
+					// 标记异常登录
+					_ = utils.Rc.Put(abnormalKey, "true", utils.GetTodayLastSecond())
+					return
+				}
+			}
 		}
 
-		// 异常登录-是否登录间隔大于60天
-		if isAbnormal == "" {
-			abnormalTime := time.Now().AddDate(0, 0, -60)
-			lastLogin, _ := time.ParseInLocation(utils.FormatDateTime, accountUser.LastLoginTime, time.Local)
-			if !lastLogin.IsZero() && lastLogin.Before(abnormalTime) {
-				br.Msg = "请进行异常登录校验"
-				br.Ret = models.BaseRespCodeAbnormalLogin
-				// 标记异常登录
-				_ = utils.Rc.Put(abnormalKey, "true", utils.GetTodayLastSecond())
+		// 如果是域账号, 那么取出原始密码走AD域校验
+		if accountUser.IsLdap == 1 {
+			passDecode, e := base64.StdEncoding.DecodeString(req.Password)
+			if e != nil {
+				br.Ret = models.BaseRespCodeLoginErr
+				br.Msg = "登录失败, 账号或密码错误"
+				br.ErrMsg = "Pass Decode err: " + e.Error()
+				return
+			}
+			originPass := strings.Replace(string(passDecode), utils.UserLoginSalt, "", 1)
+			pass, e := services.LdapUserCheck(req.Username, originPass)
+			if e != nil {
+				br.Ret = models.BaseRespCodeLoginErr
+				br.Msg = "登录失败, 账号或密码错误"
+				br.ErrMsg = "LdapLogin err: " + e.Error()
+				return
+			}
+			if !pass {
+				br.Ret = models.BaseRespCodeLoginErr
+				br.Msg = "登录失败, 账号或密码错误"
 				return
 			}
 		}
@@ -386,7 +413,8 @@ func (this *UserLoginController) Login() {
 			br.Msg = "请输入手机号"
 			return
 		}
-		if !utils.ValidateMobileFormatat(req.Mobile) {
+		// 大陆校验区号
+		if req.TelAreaCode == utils.TelAreaCodeHome && !utils.ValidateMobileFormatat(req.Mobile) {
 			br.Msg = "您的手机号输入有误, 请检查"
 			return
 		}
@@ -675,11 +703,12 @@ func (this *UserLoginController) ForgetCodeVerify() {
 	}()
 
 	type ForgetCodeVerifyReq struct {
-		FindType   int    `description:"密码找回方式: 1-手机号; 2-邮箱"`
-		VerifyCode string `description:"验证码"`
-		UserName   string `description:"用户名"`
-		Mobile     string `description:"手机号"`
-		Email      string `description:"邮箱"`
+		FindType    int    `description:"密码找回方式: 1-手机号; 2-邮箱"`
+		VerifyCode  string `description:"验证码"`
+		UserName    string `description:"用户名"`
+		Mobile      string `description:"手机号"`
+		Email       string `description:"邮箱"`
+		TelAreaCode string `description:"区号"`
 	}
 	var req ForgetCodeVerifyReq
 	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
@@ -706,7 +735,7 @@ func (this *UserLoginController) ForgetCodeVerify() {
 			br.Msg = "请输入手机号"
 			return
 		}
-		if !utils.ValidateMobileFormatat(req.Mobile) {
+		if req.TelAreaCode == utils.TelAreaCodeHome && !utils.ValidateMobileFormatat(req.Mobile) {
 			br.Msg = "您的手机号输入有误, 请检查"
 			return
 		}
@@ -920,6 +949,61 @@ func (this *UserLoginController) AreaCodeList() {
 	br.Msg = "获取成功"
 }
 
+// CheckUserLdap
+// @Title 校验用户是否为域用户
+// @Description 校验用户是否为域用户
+// @Param	request	body CheckUserLdapReq true "type json string"
+// @Success 200 Ret=200 获取成功
+// @router /ldap/user_check [post]
+func (this *UserLoginController) CheckUserLdap() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	type CheckUserLdapReq struct {
+		UserName string `description:"用户名"`
+	}
+	var req CheckUserLdapReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.UserName = strings.TrimSpace(req.UserName)
+	if req.UserName == "" {
+		br.Msg = "请输入账号"
+		return
+	}
+
+	isLdap := false
+	accountUser, e := system.GetSysUserByAdminName(req.UserName)
+	if e != nil {
+		// 无该用户视作普通用户
+		if e.Error() == utils.ErrNoRow() {
+			br.Data = false
+			br.Success = true
+			br.Msg = "操作成功"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取用户信息失败, Err: " + e.Error()
+		return
+	}
+	if accountUser.IsLdap == 1 {
+		isLdap = true
+	}
+
+	br.Data = isLdap
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
 // ICPLicense
 // @Title icp备案信息
 // @Description icp备案信息

+ 2 - 0
go.mod

@@ -18,6 +18,7 @@ require (
 	github.com/beego/beego/v2 v2.0.7
 	github.com/beevik/etree v1.2.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
+	github.com/go-ldap/ldap v3.0.3+incompatible
 	github.com/go-sql-driver/mysql v1.7.0
 	github.com/go-xorm/xorm v0.7.9
 	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b
@@ -115,6 +116,7 @@ require (
 	golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
+	gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect

+ 4 - 0
go.sum

@@ -148,6 +148,8 @@ github.com/go-ego/gse v0.80.2 h1:3LRfkaBuwlsHsmkOZvnhTcsYPXUAhiP06Sqcid7mO1M=
 github.com/go-ego/gse v0.80.2/go.mod h1:kesekpZfcFQ/kwd9b27VZHUOH5dQUjaaQUZ4OGt4Hj4=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk=
+github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
@@ -622,6 +624,8 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
+gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
+gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

+ 35 - 11
models/business_conf.go

@@ -9,22 +9,46 @@ import (
 )
 
 const (
-	BusinessConfUseXf             = "UseXf"
-	BusinessConfXfAppid           = "XfAppid"
-	BusinessConfXfApiKey          = "XfApiKey"
-	BusinessConfXfApiSecret       = "XfApiSecret"
-	BusinessConfXfVcn             = "XfVcn"
-	BusinessConfEnPptCoverImgs    = "EnPptCoverImgs"
-	BusinessConfIsReportApprove   = "IsReportApprove"
-	BusinessConfReportApproveType = "ReportApproveType"
-	BusinessConfCompanyName       = "CompanyName"
-	BusinessConfCompanyWatermark  = "CompanyWatermark"
-	BusinessConfWatermarkChart    = "WatermarkChart"
+	BusinessConfUseXf                     = "UseXf"
+	BusinessConfXfAppid                   = "XfAppid"
+	BusinessConfXfApiKey                  = "XfApiKey"
+	BusinessConfXfApiSecret               = "XfApiSecret"
+	BusinessConfXfVcn                     = "XfVcn"
+	BusinessConfEnPptCoverImgs            = "EnPptCoverImgs"
+	BusinessConfIsReportApprove           = "IsReportApprove"
+	BusinessConfReportApproveType         = "ReportApproveType"
+	BusinessConfCompanyName               = "CompanyName"
+	BusinessConfCompanyWatermark          = "CompanyWatermark"
+	BusinessConfWatermarkChart            = "WatermarkChart"
+	BusinessConfLoginSmsTpId              = "LoginSmsTpId"
+	BusinessConfLoginSmsGjTpId            = "LoginSmsGjTpId"
+	BusinessConfSmsJhgnAppKey             = "SmsJhgnAppKey"
+	BusinessConfSmsJhgjAppKey             = "SmsJhgjAppKey"
+	BusinessConfLdapHost                  = "LdapHost"
+	BusinessConfLdapBase                  = "LdapBase"
+	BusinessConfLdapPort                  = "LdapPort"
+	BusinessConfEmailClient               = "EmailClient"
+	BusinessConfEmailServerHost           = "EmailServerHost"
+	BusinessConfEmailServerPort           = "EmailServerPort"
+	BusinessConfEmailSender               = "EmailSender"
+	BusinessConfEmailSenderUserName       = "EmailSenderUserName"
+	BusinessConfEmailSenderPassword       = "EmailSenderPassword"
+	BusinessConfSmsClient                 = "SmsClient"
+	BusinessConfNanHuaSmsAppKey           = "NanHuaSmsAppKey"
+	BusinessConfNanHuaSmsAppSecret        = "NanHuaSmsAppSecret"
+	BusinessConfNanHuaSmsApiHost          = "NanHuaSmsApiHost"
+	BusinessConfLoginSmsTplContent        = "LoginSmsTplContent"
+	BusinessConfLoginEmailTemplateSubject = "LoginEmailTemplateSubject"
+	BusinessConfLoginEmailTemplateContent = "LoginEmailTemplateContent"
+	BusinessConfLdapBindUserSuffix        = "LdapBindUserSuffix"
+	BusinessConfLdapUserFilter            = "LdapUserFilter"
 )
 
 const (
 	BusinessConfReportApproveTypeEta   = "eta"
 	BusinessConfReportApproveTypeOther = "other"
+	BusinessConfClientFlagNanHua       = "nhqh" // 南华标记
+	BusinessConfEmailClientSmtp        = "smtp" // 普通邮箱标记
 )
 
 // BusinessConf 商户配置表

+ 1 - 0
models/system/admin_verify_code_record.go

@@ -30,6 +30,7 @@ type AdminVerifyCodeRecord struct {
 	ExpiredTime time.Time `description:"验证码过期时间"`
 	SendResult  string    `description:"发送结果"`
 	SendStatus  int       `description:"发送状态:0-待发送;1-已发送;2-发送失败"`
+	RequestId   string    `description:"请求ID"`
 	CreateTime  time.Time `description:"创建时间"`
 	ModifyTime  time.Time `description:"更新时间"`
 }

+ 3 - 0
models/system/sys_admin.go

@@ -41,6 +41,7 @@ type AdminItem struct {
 	CityCode                string `description:"市编码"`
 	EmployeeId              string `description:"员工工号(钉钉/每刻报销)"`
 	TelAreaCode             string `description:"手机区号"`
+	IsLdap                  int    `description:"是否为域用户:0-系统账户;1-域用户"`
 }
 
 func GetSysuserList(condition string, pars []interface{}, startSize, pageSize int) (items []*AdminItem, err error) {
@@ -117,6 +118,7 @@ type SysuserAddReq struct {
 	EmployeeId       string `description:"员工工号(钉钉/每刻报销)"`
 	Email            string `description:"邮箱"`
 	TelAreaCode      string `description:"手机区号"`
+	IsLdap           int    `description:"是否为域用户:0-系统账户;1-域用户"`
 }
 
 func GetSysAdminCount(adminName string) (count int, err error) {
@@ -163,6 +165,7 @@ type SysuserEditReq struct {
 	EmployeeId       string `description:"员工工号(钉钉/每刻报销)"`
 	Email            string `description:"邮箱"`
 	TelAreaCode      string `description:"手机区号"`
+	IsLdap           int    `description:"是否为域用户:0-系统账户;1-域用户"`
 }
 
 type SysUserMoveReq struct {

+ 1 - 0
models/system/sys_user.go

@@ -63,6 +63,7 @@ type Admin struct {
 	CityCode                  string    `description:"市编码"`
 	EmployeeId                string    `description:"员工工号(钉钉/每刻报销)"`
 	TelAreaCode               string    `description:"手机区号"`
+	IsLdap                    int       `description:"是否为域用户:0-系统账户;1-域用户"`
 }
 
 // Update 更新用户基础信息

+ 9 - 0
routers/commentsRouter.go

@@ -8476,6 +8476,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "CheckUserLdap",
+            Router: `/ldap/user_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"],
         beego.ControllerComments{
             Method: "Login",

+ 62 - 0
services/email.go

@@ -1,9 +1,12 @@
 package services
 
 import (
+	"crypto/tls"
 	"eta/eta_api/models"
 	"eta/eta_api/utils"
 	"fmt"
+	"gopkg.in/gomail.v2"
+	"strconv"
 	"strings"
 )
 
@@ -223,3 +226,62 @@ func SendEmailToCompany() {
 		}
 	}
 }
+
+type SendEmailReq struct {
+	Title   string   `description:"标题"`
+	Content string   `description:"内容"`
+	ToUser  []string `description:"收信人邮箱"`
+}
+
+func SendEmail(req SendEmailReq) (success bool, err error) {
+	if req.Title == "" {
+		err = fmt.Errorf("邮件主题不可为空")
+		return
+	}
+	if req.Content == "" {
+		err = fmt.Errorf("邮件内容不可为空")
+		return
+	}
+	if len(req.ToUser) <= 0 {
+		err = fmt.Errorf("收信人不可为空")
+		return
+	}
+
+	// 邮箱配置
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	checkArr := []string{
+		models.BusinessConfEmailServerHost, models.BusinessConfEmailServerPort,
+		models.BusinessConfEmailSender, models.BusinessConfEmailSenderUserName,
+		models.BusinessConfEmailSenderPassword,
+	}
+	for _, v := range checkArr {
+		if confMap[v] == "" {
+			err = fmt.Errorf("%s配置有误", v)
+			return
+		}
+	}
+
+	port, _ := strconv.Atoi(confMap[models.BusinessConfEmailServerPort])
+	if port <= 0 {
+		port = 587 // 默认587端口
+	}
+	m := gomail.NewMessage()
+	m.SetHeader("From", confMap[models.BusinessConfEmailSender])
+	m.SetHeader("To", req.ToUser...)
+	m.SetHeader("Subject", req.Title)
+	m.SetBody("text/html", req.Content)
+	d := gomail.NewDialer(confMap[models.BusinessConfEmailServerHost], port, confMap[models.BusinessConfEmailSenderUserName], confMap[models.BusinessConfEmailSenderPassword])
+	// 解决x509报错的问题。证书不通过。跳过证书验证
+	config := &tls.Config{ServerName: confMap[models.BusinessConfEmailServerHost], InsecureSkipVerify: true}
+	d.TLSConfig = config
+	if e = d.DialAndSend(m); e != nil {
+		err = fmt.Errorf("邮件发送失败, Err: %s", e.Error())
+		return
+	}
+	success = true
+	return
+}

+ 131 - 0
services/nanhua_sms.go

@@ -0,0 +1,131 @@
+package services
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/utils"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strconv"
+	"strings"
+)
+
+// NanHuaSms 南华短信服务
+type NanHuaSms struct{}
+
+func (cli *NanHuaSms) SendUserLoginCode(req UserLoginSmsCodeReq) (result UserLoginSmsCodeResult, err error) {
+	// 短信内容
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	tpl := confMap[models.BusinessConfLoginSmsTplContent]
+	if tpl == "" {
+		err = fmt.Errorf("短信模板有误")
+		return
+	}
+	smsContent := strings.Replace(tpl, "{{VERIFY_CODE}}", req.VerifyCode, 1)
+	smsContent = strings.Replace(smsContent, "{{EXPIRED_MINUTE}}", strconv.Itoa(utils.VerifyCodeExpireMinute), 1)
+
+	var params NanHuaSmsApiParams
+	var mobileRequest NanHuaSmsApiRequest
+	mobileRequest.Mobile = req.Mobile
+	params.ApiHost = confMap[models.BusinessConfNanHuaSmsApiHost]
+	params.AppKey = confMap[models.BusinessConfNanHuaSmsAppKey]
+	params.AppSecret = confMap[models.BusinessConfNanHuaSmsAppSecret]
+	params.Content = smsContent
+	params.TypeCode = 1
+	params.Requests = append(params.Requests, mobileRequest)
+
+	// 请求接口
+	res, e := CurlNanHuaSmsApi(params)
+	if e != nil {
+		err = fmt.Errorf("CurlNanHuaSmsApi err: %s", e.Error())
+		return
+	}
+	result.Success = res.Success
+	result.Message = res.Message
+	if len(res.Data.SmsResponses) > 0 {
+		result.RequestId = res.Data.SmsResponses[0].RecId
+	}
+	return
+}
+
+// NanHuaSmsApiParams 南华短信接口请求参数
+type NanHuaSmsApiParams struct {
+	ApiHost   string                `json:"-" description:"api-host"`
+	AppKey    string                `json:"-" description:"app-key"`
+	AppSecret string                `json:"-" description:"app-secret"`
+	Content   string                `json:"content" description:"短信内容,必填"`
+	Username  string                `json:"username" description:"使用人南华域账号,非必填"`
+	TypeCode  int                   `json:"typeCode" description:"1-普通短信;2-营销短信"`
+	Requests  []NanHuaSmsApiRequest `json:"requests" description:"手机号、内容信息"`
+}
+
+// NanHuaSmsApiRequest 南华短信接口请求-手机号信息
+type NanHuaSmsApiRequest struct {
+	Content        string `json:"content" description:"短信内容,非必填,填写则该手机号使用此内容单独发送"`
+	Mobile         string `json:"mobile" description:"手机号,必填"`
+	OrgId          string `json:"orgId" description:"客户所属ctp机构ID,非必填,用来计费(建议填写)"`
+	OaDepartmentId string `json:"oaDepartmentId" description:"客户所属oa部门ID,非必填,用来计费"`
+}
+
+// NanHuaSmsApiResult 南华短信接口-响应体
+type NanHuaSmsApiResult struct {
+	ErrorCode int    `json:"errorcode" description:"状态码:0-成功;400-失败"`
+	Success   bool   `json:"success" description:"true-成功;false-失败"`
+	Message   string `json:"message" description:"请求失败提示"`
+	Data      struct {
+		SmsResponses []struct {
+			Mobile string `json:"mobile" description:"手机号"`
+			RecId  string `json:"recId" description:"短信id,用来回查发送状态"`
+		} `json:"smsResponses"`
+	} `json:"data"`
+}
+
+// CurlNanHuaSmsApi 请求南华短信接口
+func CurlNanHuaSmsApi(params NanHuaSmsApiParams) (result NanHuaSmsApiResult, err error) {
+	url := fmt.Sprint(params.ApiHost)
+	byteParams, e := json.Marshal(params)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(byteParams)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("App-Key", params.AppKey)
+	req.Header.Set("App-Secret", params.AppSecret)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	return
+}

+ 108 - 0
services/sms.go

@@ -188,3 +188,111 @@ func sendSmsGj(jhGjAppKey, mobile, code, areaNum, tplId string) (rs []byte, err
 	utils.FileLog.Info("sendSmsGj:result:" + string(body))
 	return body, err
 }
+
+type SmsClient interface {
+	SendUserLoginCode(UserLoginSmsCodeReq) (UserLoginSmsCodeResult, error)
+}
+
+func NewSmsClient() (cli SmsClient, err error) {
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	if confMap[models.BusinessConfSmsClient] == models.BusinessConfClientFlagNanHua {
+		return new(NanHuaSms), nil
+	}
+	return new(HzSms), nil
+}
+
+type HzSms struct{}
+
+type UserLoginSmsCodeReq struct {
+	TelAreaCode string `description:"区号"`
+	Mobile      string `description:"手机号"`
+	VerifyCode  string `description:"短信验证码"`
+}
+
+type UserLoginSmsCodeResult struct {
+	Success   bool   `description:"发送是否成功"`
+	Message   string `description:"提示信息"`
+	RequestId string `description:"发送ID,用于回查发送状态"`
+}
+
+// SendUserLoginCode 发送用户登录验证码
+func (cli *HzSms) SendUserLoginCode(req UserLoginSmsCodeReq) (result UserLoginSmsCodeResult, err error) {
+	if req.Mobile == "" || req.VerifyCode == "" {
+		err = fmt.Errorf("参数有误, Mobile: %s, VerifyCode: %s", req.Mobile, req.VerifyCode)
+		return
+	}
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+
+	// 国内短信
+	var netReturn map[string]interface{}
+	if req.TelAreaCode == utils.TelAreaCodeHome {
+		tplId := confMap[models.BusinessConfLoginSmsTpId]
+		if tplId == "" {
+			err = fmt.Errorf("请先配置短信模板")
+			return
+		}
+		appKey := confMap[models.BusinessConfSmsJhgnAppKey]
+		if appKey == "" {
+			err = fmt.Errorf("请先配置聚合短信AppKey")
+			return
+		}
+
+		smsRes, e := sendSms(appKey, req.Mobile, tplId, req.VerifyCode)
+		if e != nil {
+			err = fmt.Errorf("send sms err: %s", e.Error())
+			return
+		}
+		if e = json.Unmarshal(smsRes, &netReturn); e != nil {
+			err = fmt.Errorf("json unmarshal err: %s", e.Error())
+			return
+		}
+	}
+
+	// 国际短信
+	if req.TelAreaCode != utils.TelAreaCodeHome {
+		tplId := confMap[models.BusinessConfLoginSmsGjTpId]
+		if tplId == "" {
+			err = fmt.Errorf("请先配置短信模板")
+			return
+		}
+		appKey := confMap[models.BusinessConfSmsJhgjAppKey]
+		if appKey == "" {
+			err = fmt.Errorf("请先配置聚合短信AppKey")
+			return
+		}
+
+		smsRes, e := sendSmsGj(appKey, req.Mobile, req.VerifyCode, req.TelAreaCode, tplId)
+		if e != nil {
+			err = fmt.Errorf("send gj sms err: %s", e.Error())
+			return
+		}
+		if e = json.Unmarshal(smsRes, &netReturn); e != nil {
+			err = fmt.Errorf("json unmarshal err: %s", e.Error())
+			return
+		}
+	}
+
+	errCode, ok := netReturn["error_code"].(float64)
+	if !ok {
+		err = fmt.Errorf("result code err")
+		return
+	}
+	// 忽略错误的手机号码这种错误
+	if errCode != 0 && errCode != 205401 {
+		err = fmt.Errorf("err code %f", errCode)
+		return
+	}
+	// 发送成功
+	if errCode == 0 {
+		result.Success = true
+	}
+	return
+}

+ 161 - 95
services/user_login.go

@@ -7,6 +7,7 @@ import (
 	"eta/eta_api/models/system"
 	"eta/eta_api/utils"
 	"fmt"
+	"github.com/go-ldap/ldap"
 	"strconv"
 	"strings"
 	"time"
@@ -14,6 +15,19 @@ import (
 
 // SendAdminMobileVerifyCode 发送用户手机验证码
 func SendAdminMobileVerifyCode(source int, mobile, areaCode string) (ok bool, err error) {
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("SendAdminMobileVerifyCode ErrMsg: %s", err.Error())
+			utils.FileLog.Info(tips)
+			fmt.Println(tips)
+		}
+	}()
+	smsClient, e := NewSmsClient()
+	if e != nil {
+		err = fmt.Errorf("NewSmsClient err: %s", e.Error())
+		return
+	}
+
 	verifyCode := utils.GetRandDigit(6)
 	record := new(system.AdminVerifyCodeRecord)
 	record.VerifyType = system.AdminVerifyCodeRecordTypeMobile
@@ -27,49 +41,24 @@ func SendAdminMobileVerifyCode(source int, mobile, areaCode string) (ok bool, er
 		err = fmt.Errorf("新增验证码记录失败, Err: %s", e.Error())
 		return
 	}
-	// 获取配置好的短信模版
-	smsCond := ` AND conf_key in (?,?) `
-	smsPars := make([]interface{}, 0)
-	smsPars = append(smsPars, "LoginSmsTpId", "LoginSmsGjTpId")
-	conf := new(models.BusinessConf)
-	confList, e := conf.GetItemsByCondition(smsCond, smsPars, []string{"conf_key", "conf_val"}, "")
+
+	var smsReq UserLoginSmsCodeReq
+	smsReq.Mobile = mobile
+	smsReq.TelAreaCode = areaCode
+	smsReq.VerifyCode = verifyCode
+	smsResult, e := smsClient.SendUserLoginCode(smsReq)
 	if e != nil {
-		if e.Error() == utils.ErrNoRow() {
-			err = fmt.Errorf("请先配置短信模版")
-			return
-		}
-		err = fmt.Errorf("获取短信模版失败, Err: %s", e.Error())
+		err = fmt.Errorf("SendUserLoginCode err: %s", e.Error())
 		return
 	}
+	ok = smsResult.Success
 
-	tplId := ""
-	gjTplId := ""
-	for _, v := range confList {
-		if v.ConfKey == "LoginSmsTpId" {
-			tplId = v.ConfVal
-		} else if v.ConfKey == "LoginSmsGjTpId" {
-			gjTplId = v.ConfVal
-		}
-	}
-
-	if tplId == "" {
-		err = fmt.Errorf("请先配置短信模版")
-		return
-	}
-	if areaCode == "86" {
-		ok = SendSmsCode(mobile, verifyCode, tplId)
-	} else {
-		if gjTplId == "" {
-			err = fmt.Errorf("请先配置国际短信模版")
-			return
-		}
-		ok = SendSmsCodeGj(mobile, verifyCode, areaCode, gjTplId)
-	}
 	record.SendStatus = system.AdminVerifyCodeRecordStatusSuccess
 	if !ok {
 		record.SendStatus = system.AdminVerifyCodeRecordStatusFail
 	}
-	cols := []string{"SendStatus"}
+	record.RequestId = smsResult.RequestId
+	cols := []string{"SendStatus", "RequestId"}
 	if e := record.Update(cols); e != nil {
 		err = fmt.Errorf("更新验证码记录失败, Err: %s", e.Error())
 	}
@@ -78,7 +67,37 @@ func SendAdminMobileVerifyCode(source int, mobile, areaCode string) (ok bool, er
 
 // SendAdminEmailVerifyCode 发送用户邮箱验证码
 func SendAdminEmailVerifyCode(source int, email string) (ok bool, err error) {
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("SendAdminEmailVerifyCode ErrMsg: %s", err.Error())
+			utils.FileLog.Info(tips)
+			fmt.Println(tips)
+		}
+	}()
+	// 读取配置
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	subjectConf := confMap[models.BusinessConfLoginEmailTemplateSubject]
+	contentConf := confMap[models.BusinessConfLoginEmailTemplateContent]
+	if subjectConf == "" {
+		err = fmt.Errorf("请先配置邮件模版主题")
+		return
+	}
+	if contentConf == "" {
+		err = fmt.Errorf("请先配置邮件模版内容")
+		return
+	}
 	verifyCode := utils.GetRandDigit(6)
+	t := time.Now().Format("2006年01月02日")
+	emailContent := contentConf
+	emailContent = strings.Replace(emailContent, "{{VERIFY_CODE}}", verifyCode, 1)
+	emailContent = strings.Replace(emailContent, "{{EXPIRED_MINUTE}}", strconv.Itoa(utils.VerifyCodeExpireMinute), 1)
+	emailContent = strings.Replace(emailContent, "{{DATE_TIME}}", t, 1)
+
+	// 验证码记录
 	record := new(system.AdminVerifyCodeRecord)
 	record.VerifyType = system.AdminVerifyCodeRecordTypeEmail
 	record.Email = email
@@ -92,84 +111,131 @@ func SendAdminEmailVerifyCode(source int, email string) (ok bool, err error) {
 		return
 	}
 
-	// 获取邮件配置
-	authKey := "english_report_email_conf"
-	emailConf, e := company.GetConfigDetailByCode(authKey)
-	if e != nil {
-		err = fmt.Errorf("获取群发邮件权限失败, Err: %s", e.Error())
-		return
+	var result string
+	if confMap[models.BusinessConfEmailClient] == models.BusinessConfEmailClientSmtp {
+		// 普通邮箱
+		var emailReq SendEmailReq
+		emailReq.Title = subjectConf
+		emailReq.Content = emailContent
+		emailReq.ToUser = append(emailReq.ToUser, email)
+		ok, e = SendEmail(emailReq)
+		if e != nil {
+			err = fmt.Errorf("邮箱推送失败, Err: %s", e.Error())
+			return
+		}
+	} else {
+		// 默认阿里云邮箱
+		// 读取发信人昵称配置...后面可以优化一下
+		authKey := "english_report_email_conf"
+		emailConf, e := company.GetConfigDetailByCode(authKey)
+		if e != nil {
+			err = fmt.Errorf("获取群发邮件权限失败, Err: %s", e.Error())
+			return
+		}
+		if emailConf.ConfigValue == "" {
+			err = fmt.Errorf("邮件配置为空, 不可推送")
+			return
+		}
+		conf := new(models.EnglishReportEmailConf)
+		if e = json.Unmarshal([]byte(emailConf.ConfigValue), &conf); e != nil {
+			err = fmt.Errorf("邮件配置有误, 不可推送")
+			return
+		}
+
+		req := new(EnglishReportSendEmailRequest)
+		req.Subject = subjectConf
+		req.Email = email
+		req.FromAlias = conf.FromAlias // 发信人昵称
+		req.HtmlBody = emailContent
+
+		aliEmail := new(AliyunEmail)
+		o, r, e := aliEmail.SendEmail(req)
+		if e != nil {
+			err = fmt.Errorf("阿里云邮箱推送失败, Err: %s", e.Error())
+			return
+		}
+		ok = o
+		result = r
 	}
-	if emailConf.ConfigValue == "" {
-		err = fmt.Errorf("邮件配置为空, 不可推送")
-		return
+
+	record.SendStatus = system.AdminVerifyCodeRecordStatusSuccess
+	if !ok {
+		record.SendStatus = system.AdminVerifyCodeRecordStatusFail
 	}
-	conf := new(models.EnglishReportEmailConf)
-	if e = json.Unmarshal([]byte(emailConf.ConfigValue), &conf); e != nil {
-		err = fmt.Errorf("邮件配置有误, 不可推送")
-		return
+	record.SendResult = result
+	cols := []string{"SendStatus", "SendResult"}
+	if e = record.Update(cols); e != nil {
+		err = fmt.Errorf("更新验证码记录失败, Err: %s", e.Error())
 	}
+	return
+}
 
-	// 获取邮箱模板
-	// 获取配置好的短信模版
-	cond := ` AND (conf_key = ? OR conf_key = ?)`
-	pars := make([]interface{}, 0)
-	pars = append(pars, "LoginEmailTemplateSubject", "LoginEmailTemplateContent")
-	busiConf := new(models.BusinessConf)
-	emailConfList, e := busiConf.GetItemsByCondition(cond, pars, []string{"conf_key, conf_val"}, "")
-	if e != nil {
-		if e.Error() == utils.ErrNoRow() {
-			err = fmt.Errorf("请先配置邮件模版")
-			return
+// LdapUserCheck AD域用户校验
+func LdapUserCheck(userName, password string) (pass bool, err error) {
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("LdapUserCheck ErrMsg: %s", err.Error())
+			utils.FileLog.Info(tips)
+			fmt.Println(tips)
 		}
-		err = fmt.Errorf("获取邮件模版失败, Err: %s", e.Error())
+	}()
+	if userName == "" || password == "" {
+		err = fmt.Errorf("账号密码有误")
 		return
 	}
-	var emaiContent, emailSubject string
-	for _, v := range emailConfList {
-		if v.ConfKey == "LoginEmailTemplateContent" {
-			emaiContent = v.ConfVal
-		} else if v.ConfKey == "LoginEmailTemplateSubject" {
-			emailSubject = v.ConfVal
-		}
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
 	}
-	if emailSubject == "" {
-		err = fmt.Errorf("请先配置邮件模版主题")
+	if confMap[models.BusinessConfLdapHost] == "" || confMap[models.BusinessConfLdapBase] == "" {
+		err = fmt.Errorf("AD域配置有误")
 		return
 	}
-	if emaiContent == "" {
-		err = fmt.Errorf("请先配置邮件模版内容")
+	ldapPort, _ := strconv.Atoi(confMap[models.BusinessConfLdapPort])
+	if ldapPort <= 0 {
+		err = fmt.Errorf("AD域端口号有误, Port: %d", ldapPort)
 		return
 	}
 
-	req := new(EnglishReportSendEmailRequest)
-	req.Subject = emailSubject
-	req.Email = email
-	// todo 发信人昵称
-	req.FromAlias = conf.FromAlias
-	// 填充模板
-	t := time.Now().Format("2006年01月02日")
-	ct := emaiContent
-	ct = strings.Replace(ct, "{{VERIFY_CODE}}", verifyCode, 1)
-	ct = strings.Replace(ct, "{{EXPIRED_MINUTE}}", strconv.Itoa(utils.VerifyCodeExpireMinute), 1)
-	ct = strings.Replace(ct, "{{DATE_TIME}}", t, 1)
-	req.HtmlBody = ct
-
-	aliEmail := new(AliyunEmail)
-	o, result, e := aliEmail.SendEmail(req)
+	// 连接ldap
+	addr := fmt.Sprintf("%s:%d", confMap[models.BusinessConfLdapHost], ldapPort)
+	conn, e := ldap.Dial("tcp", addr)
 	if e != nil {
-		err = fmt.Errorf("邮箱推送失败, Err: %s", e.Error())
+		err = fmt.Errorf("ldap Dial err: %s", e.Error())
 		return
 	}
-	ok = o
+	defer conn.Close()
 
-	record.SendStatus = system.AdminVerifyCodeRecordStatusSuccess
-	if !ok {
-		record.SendStatus = system.AdminVerifyCodeRecordStatusFail
+	// 绑定用户
+	bindUserName := fmt.Sprintf("%s%s", userName, confMap[models.BusinessConfLdapBindUserSuffix])
+	if e = conn.Bind(bindUserName, password); e != nil {
+		err = fmt.Errorf("ldap Bind err: %s", e.Error())
+		return
 	}
-	record.SendResult = result
-	cols := []string{"SendStatus", "SendResult"}
-	if e = record.Update(cols); e != nil {
-		err = fmt.Errorf("更新验证码记录失败, Err: %s", e.Error())
+
+	// 鉴权操作
+	searchRequest := ldap.NewSearchRequest(
+		confMap[models.BusinessConfLdapBase],
+		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
+		fmt.Sprintf(confMap[models.BusinessConfLdapUserFilter], userName),
+		[]string{"dn"},
+		nil,
+	)
+	//b, _ := json.Marshal(searchRequest)
+	//fmt.Println("searchRequest: ", string(b))
+
+	sr, e := conn.Search(searchRequest)
+	if e != nil {
+		err = fmt.Errorf("ldap Search err: %s", e.Error())
+		return
+	}
+
+	// 验证结果
+	if len(sr.Entries) != 1 {
+		utils.FileLog.Info("ldap check fail: user does not exist or too many entries returned")
+		return
 	}
+	pass = true
 	return
 }

+ 8 - 0
utils/constants.go

@@ -405,3 +405,11 @@ var DataSourceEnMap = map[int]string{
 	DATA_SOURCE_MYSTEEL_CHEMICAL: "Horizon Insights",
 	DATA_SOURCE_FUBAO:            "FuBao",
 }
+
+const (
+	TelAreaCodeHome = "86" // 大陆区号
+)
+
+const (
+	LdapInitPassword = "123456a" // 域用户初始密码
+)