sso

login_controller.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. package controllers
  2. import (
  3. "fmt"
  4. "net/url"
  5. "time"
  6. "SSO/enums"
  7. "SSO/models"
  8. "SSO/service"
  9. "SSO/utils"
  10. "github.com/astaxie/beego"
  11. )
  12. type LoginController struct {
  13. BaseController
  14. }
  15. // /login [get]
  16. // @param app_type:int
  17. // @param returnurl?:string
  18. // @param relogin?:bool 是否强制重新登录
  19. func (this *LoginController) PwdLogin() {
  20. token := this.Ctx.GetCookie("sso_token_cookie")
  21. returnURL := this.GetString("returnurl")
  22. appType, _ := this.GetInt("app_type")
  23. relogin, _ := this.GetBool("relogin", false)
  24. if relogin {
  25. token = ""
  26. }
  27. if url := service.GetAppURLWithAppType(appType); len(url) == 0 {
  28. appType = 1
  29. returnURL = ""
  30. }
  31. utils.TraceLog("login token: %v", token)
  32. if len(token) == 0 {
  33. this.Data["app_type"] = appType
  34. this.Data["return_url"] = returnURL
  35. this.TplName = "new_main/login.html"
  36. } else {
  37. if len(returnURL) == 0 {
  38. this.Data["app_type"] = appType
  39. this.Data["return_url"] = returnURL
  40. this.TplName = "new_main/login.html"
  41. } else {
  42. utils.TraceLog("跳过登录直接验证token")
  43. url := this.appendTokenParamToReturnURL(returnURL, token)
  44. this.Redirect(url, 302)
  45. }
  46. }
  47. }
  48. // /login/code [get]
  49. // @param app_type:int
  50. // @param returnurl?:string
  51. func (this *LoginController) CodeLogin() {
  52. token := this.Ctx.GetCookie("sso_token_cookie")
  53. returnURL := this.GetString("returnurl")
  54. appType, _ := this.GetInt("app_type")
  55. if url := service.GetAppURLWithAppType(appType); len(url) == 0 {
  56. appType = 1
  57. returnURL = ""
  58. }
  59. utils.TraceLog("login token: %v", token)
  60. if len(token) != 0 && len(returnURL) != 0 {
  61. utils.TraceLog("跳过登录直接验证token")
  62. url := this.appendTokenParamToReturnURL(returnURL, token)
  63. this.Redirect(url, 302)
  64. } else {
  65. redisClient := service.RedisClient()
  66. defer redisClient.Close()
  67. req := this.Ctx.Request
  68. addr := utils.GetIP(req)
  69. cur_time := time.Now().Format("2006-01-02")
  70. _, err := redisClient.Get("ip:host_" + cur_time + "_" + addr).Result()
  71. if err != nil {
  72. redisClient.Set("ip:host_"+cur_time+"_"+addr, 0, time.Second*24*60*60)
  73. }
  74. //将客户端的ip加密传给前端,作为短信验证的密钥,来验证短信发送的IP地址
  75. aespass := utils.AESEncrypt(addr)
  76. this.Data["aespass"] = aespass
  77. this.Data["app_type"] = appType
  78. this.Data["return_url"] = returnURL
  79. this.TplName = "new_main/login_by_code.html"
  80. }
  81. }
  82. // /login/submit [post]
  83. // @param app_type:int
  84. // @param mobile:string
  85. // @param password:string
  86. // @param returnurl?:string
  87. // 登录成功时,返回下一步跳转的链接:data.url
  88. func (this *LoginController) PwdLoginSubmit() {
  89. appType, _ := this.GetInt("app_type")
  90. if url := service.GetAppURLWithAppType(appType); len(url) == 0 {
  91. appType = 1
  92. }
  93. mobile := this.GetString("mobile")
  94. password := this.GetString("password")
  95. if len(mobile) == 0 || len(password) == 0 {
  96. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrPasswordWrong)
  97. this.ServeJSON()
  98. return
  99. }
  100. if service.IsPasswordRight(mobile, password) {
  101. token := utils.GenerateLoginToken(mobile)
  102. returnURL := this.GetString("returnurl")
  103. utils.TraceLog("login submit return url: %v", returnURL)
  104. url, err, admin := this.getRedirectURL(appType, mobile, returnURL, token)
  105. if err != nil {
  106. this.Data["json"] = enums.MakeFailResponseJSONWithSGJError(err)
  107. this.ServeJSON()
  108. } else {
  109. if admin != nil {
  110. this.SetSession("admin_user", admin)
  111. }
  112. // 保存登录令牌
  113. expiration, _ := beego.AppConfig.Int64("login_token_expiration_second")
  114. this.Ctx.SetCookie("sso_token_cookie", token, expiration, "/")
  115. redisClient := service.RedisClient()
  116. defer redisClient.Close()
  117. share_session_id := this.Ctx.Input.CruSession.SessionID()
  118. utils.TraceLog("share_session_id: %v", share_session_id)
  119. this.Ctx.SetCookie("s", share_session_id, expiration, "/", beego.AppConfig.String("cookie_rootdomain"))
  120. redisClient.Set(fmt.Sprintf("sso_token_%v", share_session_id), token, time.Duration(expiration)*time.Second)
  121. this.Data["json"] = enums.MakeSuccessResponseJSON(map[string]interface{}{
  122. "url": url,
  123. })
  124. this.ServeJSON()
  125. }
  126. } else {
  127. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrPasswordWrong)
  128. this.ServeJSON()
  129. }
  130. }
  131. // /login/code/submit [post]
  132. // @param app_type:int
  133. // @param mobile:string
  134. // @param code:string
  135. // @param returnurl?:string
  136. // 登录成功时,返回下一步跳转的链接:data.url
  137. func (this *LoginController) CodeLoginSubmit() {
  138. appType, _ := this.GetInt("app_type")
  139. if url := service.GetAppURLWithAppType(appType); len(url) == 0 {
  140. appType = 1
  141. }
  142. mobile := this.GetString("mobile")
  143. code := this.GetString("code")
  144. if len(mobile) == 0 || len(code) == 0 {
  145. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
  146. this.ServeJSON()
  147. return
  148. }
  149. if !service.IsMobileRegister(mobile) {
  150. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
  151. this.ServeJSON()
  152. return
  153. }
  154. redisClient := service.RedisClient()
  155. defer redisClient.Close()
  156. cachedCode, err := redisClient.Get("code_msg_" + mobile).Result()
  157. if err != nil {
  158. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
  159. this.ServeJSON()
  160. return
  161. } else {
  162. if code != cachedCode {
  163. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
  164. this.ServeJSON()
  165. return
  166. } else {
  167. token := utils.GenerateLoginToken(mobile)
  168. returnURL := this.GetString("returnurl")
  169. url, err, admin := this.getRedirectURL(appType, mobile, returnURL, token)
  170. if err != nil {
  171. this.Data["json"] = enums.MakeFailResponseJSONWithSGJError(err)
  172. this.ServeJSON()
  173. } else {
  174. if admin != nil {
  175. this.SetSession("admin_user", admin)
  176. }
  177. // 保存登录令牌
  178. expiration, _ := beego.AppConfig.Int64("login_token_expiration_second")
  179. this.Ctx.SetCookie("sso_token_cookie", token, expiration, "/")
  180. share_session_id := this.Ctx.Input.CruSession.SessionID()
  181. utils.TraceLog("share_session_id: %v", share_session_id)
  182. this.Ctx.SetCookie("s", share_session_id, expiration, "/", beego.AppConfig.String("cookie_rootdomain"))
  183. redisClient.Set(fmt.Sprintf("sso_token_%v", share_session_id), token, time.Duration(expiration)*time.Second)
  184. // 清除验证码
  185. redisClient.Del("code_msg_" + mobile)
  186. this.Data["json"] = enums.MakeSuccessResponseJSON(map[string]interface{}{
  187. "url": url,
  188. })
  189. this.ServeJSON()
  190. }
  191. }
  192. }
  193. }
  194. func (this *LoginController) appendTokenParamToReturnURL(returnURL string, token string) string {
  195. urlStr, _ := url.QueryUnescape(returnURL)
  196. u, _ := url.Parse(urlStr)
  197. query := u.Query()
  198. query.Del("token")
  199. query.Add("token", token)
  200. u.RawQuery = query.Encode()
  201. return u.String()
  202. }
  203. func (this *LoginController) getRedirectURL(app_type int, mobile string, returnURL string, token string) (string, *enums.SGJError, *models.AdminUser) {
  204. utils.TraceLog("returnURL: %v", returnURL)
  205. admin := service.GetAdminUserWithMobile(mobile)
  206. if admin != nil {
  207. if admin.IsSuperAdmin { // 如果是超级管理员用户
  208. // 那么需要检查是否创建了机构和应用,如未创建,则前往创建
  209. // 如果都创建了,那么和普通管理员用户一样,需要经过其他判断得出下一步跳转的链接
  210. if didCreateOrg, findOrgErr := service.DidAdminUserCreateOrg(admin.Id); findOrgErr != nil {
  211. utils.ErrorLog("数据错误:查找mobile = %v的用户创建的机构时错误:%v", mobile, findOrgErr)
  212. return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
  213. } else {
  214. if didCreateOrg {
  215. if didCreateApp, findAppErr := service.DidAdminUserOrgCreateApp(admin.Id, app_type); findAppErr != nil {
  216. utils.ErrorLog("数据错误:查找mobile = %v的用户创建的应用时错误:%v", mobile, findOrgErr)
  217. return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
  218. } else {
  219. if !didCreateApp {
  220. url := this.appendTokenParamToReturnURL(beego.URLFor("OrgController.CreateApp"), token)
  221. return url, nil, admin
  222. }
  223. }
  224. } else {
  225. url := this.appendTokenParamToReturnURL(beego.URLFor("OrgController.Create"), token) // 前往创建机构的 url
  226. return url, nil, admin
  227. }
  228. }
  229. }
  230. if len(returnURL) != 0 {
  231. url := this.appendTokenParamToReturnURL(returnURL, token)
  232. return url, nil, admin
  233. }
  234. // 普通管理员用户
  235. // 如果有登录记录,则前往最近登录记录中的应用
  236. lastLoginLog, getLastLoginLogErr := service.GetAdminUserLastLoginLog(admin.Id, app_type)
  237. if getLastLoginLogErr != nil {
  238. utils.ErrorLog("数据错误:查找mobile = %v的用户最近一次登录记录时错误:%v", mobile, getLastLoginLogErr)
  239. }
  240. if lastLoginLog != nil {
  241. utils.TraceLog("%+v", lastLoginLog)
  242. url := service.GetAppURLWithAppType(int(lastLoginLog.AppType))
  243. if len(url) > 0 {
  244. return this.appendTokenParamToReturnURL(url, token), nil, admin
  245. }
  246. }
  247. // 如果没有登录记录,则要前往其有权访问的、最高优先级的应用
  248. appType, getAppTypeErr := service.GetAdminUserPrioritizedAppType(admin.Id)
  249. if getAppTypeErr != nil {
  250. utils.ErrorLog("数据错误:查找mobile = %v的用户有权访问的应用的类型时错误:%v", mobile, getAppTypeErr)
  251. return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
  252. } else {
  253. if appType == 0 {
  254. utils.ErrorLog("数据错误:查找mobile = %v的用户有权访问的应用的类型时的结果错误:%v", mobile, "在超级管理员已创建应用,并且已分配角色的情况下,不可能存在查找不到相关应用")
  255. return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
  256. } else {
  257. url := this.appendTokenParamToReturnURL(service.GetAppURLWithAppType(int(appType)), token)
  258. return url, nil, admin
  259. }
  260. }
  261. } else {
  262. utils.ErrorLog("数据错误:查找不到mobile = %v的用户", mobile)
  263. return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
  264. }
  265. }
  266. // /logout [get]
  267. func (this *LoginController) Logout() {
  268. // 有 session.AdminUser 的话,销毁掉
  269. adminUserObj := this.GetSession("admin_user")
  270. redisClient := service.RedisClient()
  271. defer redisClient.Close()
  272. // 销毁 token
  273. this.Ctx.SetCookie("sso_token_cookie", "")
  274. redisClient.Del(fmt.Sprintf("sso_token_%v", this.Ctx.GetCookie("s")))
  275. if adminUserObj == nil {
  276. this.Redirect302(beego.URLFor("LoginController.Index"))
  277. return
  278. }
  279. adminUser := adminUserObj.(*models.AdminUser)
  280. this.DelSession("admin_user")
  281. redisClient.Del("sso_admin_user_info_" + adminUser.Mobile)
  282. loginLog := &models.AdminUserLoginLog{
  283. AdminUserId: adminUser.Id,
  284. OrgId: 0,
  285. AppId: 0,
  286. IP: utils.GetIP(this.Ctx.Request),
  287. OperateType: 2,
  288. AppType: 0,
  289. CreateTime: time.Now().Unix(),
  290. }
  291. if insertErr := service.InsertLoginLog(loginLog); insertErr != nil {
  292. utils.ErrorLog("为手机号为%v的用户插入一条登录记录失败:%v", adminUser.Mobile, insertErr)
  293. }
  294. // 通知其他子系统退出登录
  295. // 跳转首页
  296. this.Redirect302(beego.URLFor("LoginController.Index"))
  297. }