sso

login_controller.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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 = 0
  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 = 0
  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. utils.TraceLog("login app_type=%v", appType)
  91. if url := service.GetAppURLWithAppType(appType); len(url) == 0 {
  92. utils.TraceLog("login app_type not exist")
  93. appType = 0
  94. }
  95. mobile := this.GetString("mobile")
  96. password := this.GetString("password")
  97. if len(mobile) == 0 || len(password) == 0 {
  98. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrPasswordWrong)
  99. this.ServeJSON()
  100. return
  101. }
  102. if service.IsPasswordRight(mobile, password) {
  103. token := utils.GenerateLoginToken(mobile)
  104. returnURL := this.GetString("returnurl")
  105. utils.TraceLog("login submit return url: %v", returnURL)
  106. url, err, admin := this.getRedirectURL(appType, mobile, returnURL, token)
  107. if err != nil {
  108. this.Data["json"] = enums.MakeFailResponseJSONWithSGJError(err)
  109. this.ServeJSON()
  110. } else {
  111. if admin != nil {
  112. this.SetSession("admin_user", admin)
  113. }
  114. // 保存登录令牌
  115. expiration, _ := beego.AppConfig.Int64("login_token_expiration_second")
  116. this.Ctx.SetCookie("sso_token_cookie", token, expiration, "/")
  117. redisClient := service.RedisClient()
  118. defer redisClient.Close()
  119. share_session_id := this.Ctx.Input.CruSession.SessionID()
  120. utils.TraceLog("share_session_id: %v", share_session_id)
  121. this.Ctx.SetCookie("s", share_session_id, expiration, "/", beego.AppConfig.String("cookie_rootdomain"))
  122. redisClient.Set(fmt.Sprintf("sso_token_%v", share_session_id), token, time.Duration(expiration)*time.Second)
  123. this.Data["json"] = enums.MakeSuccessResponseJSON(map[string]interface{}{
  124. "url": url,
  125. })
  126. this.ServeJSON()
  127. }
  128. } else {
  129. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrPasswordWrong)
  130. this.ServeJSON()
  131. }
  132. }
  133. // /login/code/submit [post]
  134. // @param app_type:int
  135. // @param mobile:string
  136. // @param code:string
  137. // @param returnurl?:string
  138. // 登录成功时,返回下一步跳转的链接:data.url
  139. func (this *LoginController) CodeLoginSubmit() {
  140. appType, _ := this.GetInt("app_type")
  141. if url := service.GetAppURLWithAppType(appType); len(url) == 0 {
  142. appType = 0
  143. }
  144. mobile := this.GetString("mobile")
  145. code := this.GetString("code")
  146. if len(mobile) == 0 || len(code) == 0 {
  147. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
  148. this.ServeJSON()
  149. return
  150. }
  151. if !service.IsMobileRegister(mobile) {
  152. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
  153. this.ServeJSON()
  154. return
  155. }
  156. redisClient := service.RedisClient()
  157. defer redisClient.Close()
  158. cachedCode, err := redisClient.Get("code_msg_" + mobile).Result()
  159. if err != nil {
  160. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
  161. this.ServeJSON()
  162. return
  163. } else {
  164. if code != cachedCode {
  165. this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
  166. this.ServeJSON()
  167. return
  168. } else {
  169. token := utils.GenerateLoginToken(mobile)
  170. returnURL := this.GetString("returnurl")
  171. url, err, admin := this.getRedirectURL(appType, mobile, returnURL, token)
  172. if err != nil {
  173. this.Data["json"] = enums.MakeFailResponseJSONWithSGJError(err)
  174. this.ServeJSON()
  175. } else {
  176. if admin != nil {
  177. this.SetSession("admin_user", admin)
  178. }
  179. // 保存登录令牌
  180. expiration, _ := beego.AppConfig.Int64("login_token_expiration_second")
  181. this.Ctx.SetCookie("sso_token_cookie", token, expiration, "/")
  182. share_session_id := this.Ctx.Input.CruSession.SessionID()
  183. utils.TraceLog("share_session_id: %v", share_session_id)
  184. this.Ctx.SetCookie("s", share_session_id, expiration, "/", beego.AppConfig.String("cookie_rootdomain"))
  185. redisClient.Set(fmt.Sprintf("sso_token_%v", share_session_id), token, time.Duration(expiration)*time.Second)
  186. // 清除验证码
  187. redisClient.Del("code_msg_" + mobile)
  188. this.Data["json"] = enums.MakeSuccessResponseJSON(map[string]interface{}{
  189. "url": url,
  190. })
  191. this.ServeJSON()
  192. }
  193. }
  194. }
  195. }
  196. func (this *LoginController) appendTokenParamToReturnURL(returnURL string, token string) string {
  197. urlStr, _ := url.QueryUnescape(returnURL)
  198. u, _ := url.Parse(urlStr)
  199. query := u.Query()
  200. query.Del("token")
  201. query.Add("token", token)
  202. u.RawQuery = query.Encode()
  203. return u.String()
  204. }
  205. func (this *LoginController) getRedirectURL(app_type int, mobile string, returnURL string, token string) (string, *enums.SGJError, *models.AdminUser) {
  206. utils.TraceLog("returnURL: %v", returnURL)
  207. admin := service.GetAdminUserWithMobile(mobile)
  208. if admin != nil {
  209. if admin.IsSuperAdmin { // 如果是超级管理员用户
  210. // 那么需要检查是否创建了机构和应用,如未创建,则前往创建
  211. // 如果都创建了,那么和普通管理员用户一样,需要经过其他判断得出下一步跳转的链接
  212. if didCreateOrg, findOrgErr := service.DidAdminUserCreateOrg(admin.Id); findOrgErr != nil {
  213. utils.ErrorLog("数据错误:查找mobile = %v的用户创建的机构时错误:%v", mobile, findOrgErr)
  214. return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
  215. } else {
  216. if didCreateOrg {
  217. if didCreateApp, findAppErr := service.DidAdminUserOrgCreateApp(admin.Id, app_type); findAppErr != nil {
  218. utils.ErrorLog("数据错误:查找mobile = %v的用户创建的应用时错误:%v", mobile, findOrgErr)
  219. return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
  220. } else {
  221. if !didCreateApp {
  222. url := this.appendTokenParamToReturnURL(beego.URLFor("OrgController.CreateApp"), token)
  223. return url, nil, admin
  224. }
  225. }
  226. } else {
  227. url := this.appendTokenParamToReturnURL(beego.URLFor("OrgController.Create"), token) // 前往创建机构的 url
  228. return url, nil, admin
  229. }
  230. }
  231. }
  232. if len(returnURL) != 0 {
  233. url := this.appendTokenParamToReturnURL(returnURL, token)
  234. return url, nil, admin
  235. }
  236. utils.TraceLog("login redirect url: app_type=%v", app_type)
  237. // 普通管理员用户
  238. // 如果有登录记录,则前往最近登录记录中的应用
  239. lastLoginLog, getLastLoginLogErr := service.GetAdminUserLastLoginLog(admin.Id, app_type)
  240. if getLastLoginLogErr != nil {
  241. utils.ErrorLog("数据错误:查找mobile = %v的用户最近一次登录记录时错误:%v", mobile, getLastLoginLogErr)
  242. }
  243. if lastLoginLog != nil {
  244. utils.TraceLog("%+v", lastLoginLog)
  245. _, getAppRoleErr := service.GetAppRole(admin.Id, lastLoginLog.OrgId, lastLoginLog.AppId)
  246. if getAppRoleErr != nil {
  247. utils.ErrorLog("数据错误:查找mobile = %v的用户的应用角色时错误:%v", mobile, getAppRoleErr)
  248. } else {
  249. url := service.GetAppURLWithAppType(int(lastLoginLog.AppType))
  250. if len(url) > 0 {
  251. return this.appendTokenParamToReturnURL(url, token), nil, admin
  252. }
  253. }
  254. }
  255. // 如果没有登录记录,则要前往其有权访问的、最高优先级的应用
  256. appType, getAppTypeErr := service.GetAdminUserPrioritizedAppType(admin.Id)
  257. if getAppTypeErr != nil {
  258. utils.ErrorLog("数据错误:查找mobile = %v的用户有权访问的应用的类型时错误:%v", mobile, getAppTypeErr)
  259. return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
  260. } else {
  261. if appType == 0 {
  262. utils.ErrorLog("数据错误:查找mobile = %v的用户有权访问的应用的类型时的结果错误:%v", mobile, "在超级管理员已创建应用,并且已分配角色的情况下,不可能存在查找不到相关应用")
  263. return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
  264. } else {
  265. url := this.appendTokenParamToReturnURL(service.GetAppURLWithAppType(int(appType)), token)
  266. return url, nil, admin
  267. }
  268. }
  269. } else {
  270. utils.ErrorLog("数据错误:查找不到mobile = %v的用户", mobile)
  271. return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
  272. }
  273. }
  274. // /logout [get]
  275. func (this *LoginController) Logout() {
  276. // 有 session.AdminUser 的话,销毁掉
  277. adminUserObj := this.GetSession("admin_user")
  278. redisClient := service.RedisClient()
  279. defer redisClient.Close()
  280. // 销毁 token
  281. this.Ctx.SetCookie("sso_token_cookie", "")
  282. redisClient.Del(fmt.Sprintf("sso_token_%v", this.Ctx.GetCookie("s")))
  283. if adminUserObj == nil {
  284. this.Redirect302(beego.URLFor("LoginController.Index"))
  285. return
  286. }
  287. adminUser := adminUserObj.(*models.AdminUser)
  288. this.DelSession("admin_user")
  289. redisClient.Del("sso_admin_user_info_" + adminUser.Mobile)
  290. loginLog := &models.AdminUserLoginLog{
  291. AdminUserId: adminUser.Id,
  292. OrgId: 0,
  293. AppId: 0,
  294. IP: utils.GetIP(this.Ctx.Request),
  295. OperateType: 2,
  296. AppType: 0,
  297. CreateTime: time.Now().Unix(),
  298. }
  299. if insertErr := service.InsertLoginLog(loginLog); insertErr != nil {
  300. utils.ErrorLog("为手机号为%v的用户插入一条登录记录失败:%v", adminUser.Mobile, insertErr)
  301. }
  302. // 通知其他子系统退出登录
  303. // 跳转首页
  304. this.Redirect302(beego.URLFor("LoginController.Index"))
  305. }