package controllers import ( "fmt" "net/url" "time" "SSO/enums" "SSO/models" "SSO/service" "SSO/utils" "github.com/astaxie/beego" ) type LoginController struct { BaseController } // /login [get] // @param app_type:int // @param returnurl?:string // @param relogin?:bool 是否强制重新登录 func (this *LoginController) PwdLogin() { token := this.Ctx.GetCookie("sso_token_cookie") returnURL := this.GetString("returnurl") appType, _ := this.GetInt("app_type") relogin, _ := this.GetBool("relogin", false) if relogin { token = "" } if url := service.GetAppURLWithAppType(appType); len(url) == 0 { appType = 0 returnURL = "" } utils.TraceLog("login token: %v", token) if len(token) == 0 { this.Data["app_type"] = appType this.Data["return_url"] = returnURL this.TplName = "new_main/login.html" } else { if len(returnURL) == 0 { this.Data["app_type"] = appType this.Data["return_url"] = returnURL this.TplName = "new_main/login.html" } else { utils.TraceLog("跳过登录直接验证token") url := this.appendTokenParamToReturnURL(returnURL, token) this.Redirect(url, 302) } } } // /login/code [get] // @param app_type:int // @param returnurl?:string func (this *LoginController) CodeLogin() { token := this.Ctx.GetCookie("sso_token_cookie") returnURL := this.GetString("returnurl") appType, _ := this.GetInt("app_type") if url := service.GetAppURLWithAppType(appType); len(url) == 0 { appType = 0 returnURL = "" } utils.TraceLog("login token: %v", token) if len(token) != 0 && len(returnURL) != 0 { utils.TraceLog("跳过登录直接验证token") url := this.appendTokenParamToReturnURL(returnURL, token) this.Redirect(url, 302) } else { redisClient := service.RedisClient() defer redisClient.Close() req := this.Ctx.Request addr := utils.GetIP(req) cur_time := time.Now().Format("2006-01-02") _, err := redisClient.Get("ip:host_" + cur_time + "_" + addr).Result() if err != nil { redisClient.Set("ip:host_"+cur_time+"_"+addr, 0, time.Second*24*60*60) } //将客户端的ip加密传给前端,作为短信验证的密钥,来验证短信发送的IP地址 aespass := utils.AESEncrypt(addr) this.Data["aespass"] = aespass this.Data["app_type"] = appType this.Data["return_url"] = returnURL this.TplName = "new_main/login_by_code.html" } } // /login/submit [post] // @param app_type:int // @param mobile:string // @param password:string // @param returnurl?:string // 登录成功时,返回下一步跳转的链接:data.url func (this *LoginController) PwdLoginSubmit() { appType, _ := this.GetInt("app_type") utils.TraceLog("login app_type=%v", appType) if url := service.GetAppURLWithAppType(appType); len(url) == 0 { utils.TraceLog("login app_type not exist") appType = 0 } mobile := this.GetString("mobile") password := this.GetString("password") if len(mobile) == 0 || len(password) == 0 { this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrPasswordWrong) this.ServeJSON() return } if service.IsPasswordRight(mobile, password) { token := utils.GenerateLoginToken(mobile) returnURL := this.GetString("returnurl") utils.TraceLog("login submit return url: %v", returnURL) url, err, admin := this.getRedirectURL(appType, mobile, returnURL, token) if err != nil { this.Data["json"] = enums.MakeFailResponseJSONWithSGJError(err) this.ServeJSON() } else { if admin != nil { this.SetSession("admin_user", admin) } // 保存登录令牌 expiration, _ := beego.AppConfig.Int64("login_token_expiration_second") this.Ctx.SetCookie("sso_token_cookie", token, expiration, "/") redisClient := service.RedisClient() defer redisClient.Close() share_session_id := this.Ctx.Input.CruSession.SessionID() utils.TraceLog("share_session_id: %v", share_session_id) this.Ctx.SetCookie("s", share_session_id, expiration, "/", beego.AppConfig.String("cookie_rootdomain")) redisClient.Set(fmt.Sprintf("sso_token_%v", share_session_id), token, time.Duration(expiration)*time.Second) this.Data["json"] = enums.MakeSuccessResponseJSON(map[string]interface{}{ "url": url, }) this.ServeJSON() } } else { this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrPasswordWrong) this.ServeJSON() } } // /login/code/submit [post] // @param app_type:int // @param mobile:string // @param code:string // @param returnurl?:string // 登录成功时,返回下一步跳转的链接:data.url func (this *LoginController) CodeLoginSubmit() { appType, _ := this.GetInt("app_type") if url := service.GetAppURLWithAppType(appType); len(url) == 0 { appType = 0 } mobile := this.GetString("mobile") code := this.GetString("code") if len(mobile) == 0 || len(code) == 0 { this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong) this.ServeJSON() return } if !service.IsMobileRegister(mobile) { this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong) this.ServeJSON() return } redisClient := service.RedisClient() defer redisClient.Close() cachedCode, err := redisClient.Get("code_msg_" + mobile).Result() if err != nil { this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong) this.ServeJSON() return } else { if code != cachedCode { this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong) this.ServeJSON() return } else { token := utils.GenerateLoginToken(mobile) returnURL := this.GetString("returnurl") url, err, admin := this.getRedirectURL(appType, mobile, returnURL, token) if err != nil { this.Data["json"] = enums.MakeFailResponseJSONWithSGJError(err) this.ServeJSON() } else { if admin != nil { this.SetSession("admin_user", admin) } // 保存登录令牌 expiration, _ := beego.AppConfig.Int64("login_token_expiration_second") this.Ctx.SetCookie("sso_token_cookie", token, expiration, "/") share_session_id := this.Ctx.Input.CruSession.SessionID() utils.TraceLog("share_session_id: %v", share_session_id) this.Ctx.SetCookie("s", share_session_id, expiration, "/", beego.AppConfig.String("cookie_rootdomain")) redisClient.Set(fmt.Sprintf("sso_token_%v", share_session_id), token, time.Duration(expiration)*time.Second) // 清除验证码 redisClient.Del("code_msg_" + mobile) this.Data["json"] = enums.MakeSuccessResponseJSON(map[string]interface{}{ "url": url, }) this.ServeJSON() } } } } func (this *LoginController) appendTokenParamToReturnURL(returnURL string, token string) string { urlStr, _ := url.QueryUnescape(returnURL) u, _ := url.Parse(urlStr) query := u.Query() query.Del("token") query.Add("token", token) u.RawQuery = query.Encode() return u.String() } func (this *LoginController) getRedirectURL(app_type int, mobile string, returnURL string, token string) (string, *enums.SGJError, *models.AdminUser) { utils.TraceLog("returnURL: %v", returnURL) admin := service.GetAdminUserWithMobile(mobile) if admin != nil { if admin.IsSuperAdmin { // 如果是超级管理员用户 // 那么需要检查是否创建了机构和应用,如未创建,则前往创建 // 如果都创建了,那么和普通管理员用户一样,需要经过其他判断得出下一步跳转的链接 if didCreateOrg, findOrgErr := service.DidAdminUserCreateOrg(admin.Id); findOrgErr != nil { utils.ErrorLog("数据错误:查找mobile = %v的用户创建的机构时错误:%v", mobile, findOrgErr) return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil } else { if didCreateOrg { if didCreateApp, findAppErr := service.DidAdminUserOrgCreateApp(admin.Id, app_type); findAppErr != nil { utils.ErrorLog("数据错误:查找mobile = %v的用户创建的应用时错误:%v", mobile, findOrgErr) return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil } else { if !didCreateApp { url := this.appendTokenParamToReturnURL(beego.URLFor("OrgController.CreateApp"), token) return url, nil, admin } } } else { url := this.appendTokenParamToReturnURL(beego.URLFor("OrgController.Create"), token) // 前往创建机构的 url return url, nil, admin } } } if len(returnURL) != 0 { url := this.appendTokenParamToReturnURL(returnURL, token) return url, nil, admin } utils.TraceLog("login redirect url: app_type=%v", app_type) // 普通管理员用户 // 如果有登录记录,则前往最近登录记录中的应用 lastLoginLog, getLastLoginLogErr := service.GetAdminUserLastLoginLog(admin.Id, app_type) if getLastLoginLogErr != nil { utils.ErrorLog("数据错误:查找mobile = %v的用户最近一次登录记录时错误:%v", mobile, getLastLoginLogErr) } if lastLoginLog != nil { utils.TraceLog("%+v", lastLoginLog) _, getAppRoleErr := service.GetAppRole(admin.Id, lastLoginLog.OrgId, lastLoginLog.AppId) if getAppRoleErr != nil { utils.ErrorLog("数据错误:查找mobile = %v的用户的应用角色时错误:%v", mobile, getAppRoleErr) } else { url := service.GetAppURLWithAppType(int(lastLoginLog.AppType)) if len(url) > 0 { return this.appendTokenParamToReturnURL(url, token), nil, admin } } } // 如果没有登录记录,则要前往其有权访问的、最高优先级的应用 appType, getAppTypeErr := service.GetAdminUserPrioritizedAppType(admin.Id) if getAppTypeErr != nil { utils.ErrorLog("数据错误:查找mobile = %v的用户有权访问的应用的类型时错误:%v", mobile, getAppTypeErr) return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil } else { if appType == 0 { utils.ErrorLog("数据错误:查找mobile = %v的用户有权访问的应用的类型时的结果错误:%v", mobile, "在超级管理员已创建应用,并且已分配角色的情况下,不可能存在查找不到相关应用") return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil } else { url := this.appendTokenParamToReturnURL(service.GetAppURLWithAppType(int(appType)), token) return url, nil, admin } } } else { utils.ErrorLog("数据错误:查找不到mobile = %v的用户", mobile) return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil } } // /logout [get] func (this *LoginController) Logout() { // 有 session.AdminUser 的话,销毁掉 adminUserObj := this.GetSession("admin_user") redisClient := service.RedisClient() defer redisClient.Close() // 销毁 token this.Ctx.SetCookie("sso_token_cookie", "") redisClient.Del(fmt.Sprintf("sso_token_%v", this.Ctx.GetCookie("s"))) if adminUserObj == nil { this.Redirect302(beego.URLFor("LoginController.Index")) return } adminUser := adminUserObj.(*models.AdminUser) this.DelSession("admin_user") redisClient.Del("sso_admin_user_info_" + adminUser.Mobile) loginLog := &models.AdminUserLoginLog{ AdminUserId: adminUser.Id, OrgId: 0, AppId: 0, IP: utils.GetIP(this.Ctx.Request), OperateType: 2, AppType: 0, CreateTime: time.Now().Unix(), } if insertErr := service.InsertLoginLog(loginLog); insertErr != nil { utils.ErrorLog("为手机号为%v的用户插入一条登录记录失败:%v", adminUser.Mobile, insertErr) } // 通知其他子系统退出登录 // 跳转首页 this.Redirect302(beego.URLFor("LoginController.Index")) }