auth.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package api
  2. import (
  3. "gfast/app/common/api"
  4. comModel "gfast/app/common/model"
  5. commonService "gfast/app/common/service"
  6. "gfast/app/system/model"
  7. "gfast/app/system/service"
  8. "gfast/library"
  9. "github.com/goflyfox/gtoken/gtoken"
  10. "github.com/gogf/gf/crypto/gmd5"
  11. "github.com/gogf/gf/encoding/gjson"
  12. "github.com/gogf/gf/frame/g"
  13. "github.com/gogf/gf/net/ghttp"
  14. "github.com/gogf/gf/os/gcache"
  15. "github.com/gogf/gf/os/genv"
  16. "github.com/gogf/gf/os/gtime"
  17. "github.com/gogf/gf/util/gconv"
  18. "github.com/gogf/gf/util/gvalid"
  19. "github.com/mssola/user_agent"
  20. "strings"
  21. )
  22. type auth struct {
  23. api.CommonBase
  24. }
  25. var (
  26. Auth = new(auth)
  27. MultiLogin = g.Cfg().GetBool("gToken.system.MultiLogin")
  28. GfToken = &gtoken.GfToken{
  29. CacheMode: g.Cfg().GetInt8("gToken.system.CacheMode"),
  30. CacheKey: g.Cfg().GetString("gToken.system.CacheKey"),
  31. Timeout: g.Cfg().GetInt("gToken.system.Timeout"),
  32. MaxRefresh: g.Cfg().GetInt("gToken.system.MaxRefresh"),
  33. TokenDelimiter: g.Cfg().GetString("gToken.system.TokenDelimiter"),
  34. EncryptKey: g.Cfg().GetBytes("gToken.system.EncryptKey"),
  35. AuthFailMsg: g.Cfg().GetString("gToken.system.AuthFailMsg"),
  36. MultiLogin: MultiLogin,
  37. LoginPath: "/login",
  38. LoginBeforeFunc: Auth.login,
  39. LoginAfterFunc: Auth.loginAfter,
  40. LogoutPath: "/logout",
  41. AuthExcludePaths: g.SliceStr{"/login"},
  42. AuthAfterFunc: Auth.authAfterFunc,
  43. LogoutBeforeFunc: Auth.loginOut,
  44. }
  45. )
  46. //后台用户登陆验证
  47. func (c *auth) login(r *ghttp.Request) (string, interface{}) {
  48. var ctx = r.GetCtx()
  49. var apiReq *model.LoginParamsReq
  50. if err := r.Parse(&apiReq); err != nil {
  51. c.FailJsonExit(r, err.(gvalid.Error).Current().Error())
  52. }
  53. //判断验证码是否正确
  54. debug := genv.GetWithCmd("gf.debug")
  55. if debug.Int() != 1 {
  56. if !commonService.Captcha.VerifyString(apiReq.VerifyKey, apiReq.VerifyCode) {
  57. c.FailJson(true, r, "验证码输入错误")
  58. }
  59. }
  60. ip := library.GetClientIp(r)
  61. userAgent := r.Header.Get("User-Agent")
  62. if user, err := service.SysUser.GetAdminUserByUsernamePassword(ctx, apiReq); err != nil {
  63. //保存日志(异步)
  64. service.SysLoginLog.Invoke(&model.LoginLogParams{
  65. Status: 0,
  66. Username: apiReq.Username,
  67. Ip: ip,
  68. UserAgent: userAgent,
  69. Msg: err.Error(),
  70. Module: "系统后台",
  71. })
  72. c.FailJsonExit(r, err.Error())
  73. } else if user != nil {
  74. r.SetParam("userInfo", user)
  75. //更新用户登录记录 写入日志信息
  76. service.SysUser.UpdateLoginInfo(user.Id, apiReq.Username, ip, userAgent, "登录成功", "系统后台")
  77. var keys string
  78. if MultiLogin {
  79. keys = gconv.String(user.Id) + "-" + gmd5.MustEncryptString(user.UserName) + gmd5.MustEncryptString(user.UserPassword+ip)
  80. } else {
  81. keys = gconv.String(user.Id) + "-" + gmd5.MustEncryptString(user.UserName) + gmd5.MustEncryptString(user.UserPassword)
  82. }
  83. return keys, user
  84. }
  85. return "", nil
  86. }
  87. //登录成功返回
  88. func (c *auth) loginAfter(r *ghttp.Request, respData gtoken.Resp) {
  89. if !respData.Success() {
  90. r.Response.WriteJson(respData)
  91. } else {
  92. token := respData.GetString("token")
  93. uuid := respData.GetString("uuid")
  94. var userInfo *model.LoginUserRes
  95. r.GetParamVar("userInfo").Struct(&userInfo)
  96. //保存用户在线状态token到数据库
  97. userAgent := r.Header.Get("User-Agent")
  98. ua := user_agent.New(userAgent)
  99. os := ua.OS()
  100. explorer, _ := ua.Browser()
  101. onlineData := &model.SysUserOnline{
  102. Uuid: uuid,
  103. Token: token,
  104. CreateTime: gtime.Now(),
  105. UserName: userInfo.UserName,
  106. Ip: library.GetClientIp(r),
  107. Explorer: explorer,
  108. Os: os,
  109. }
  110. //保存用户在线状态(异步)
  111. service.Online.Invoke(onlineData)
  112. c.SusJsonExit(r, g.Map{
  113. "token": token,
  114. })
  115. }
  116. }
  117. //gToken验证后返回
  118. func (c *auth) authAfterFunc(r *ghttp.Request, respData gtoken.Resp) {
  119. if r.Method == "OPTIONS" || respData.Success() {
  120. r.Middleware.Next()
  121. } else {
  122. c.JsonExit(r, respData.Code, "用户信息验证失败")
  123. }
  124. }
  125. //后台退出登陆
  126. func (c *auth) loginOut(r *ghttp.Request) bool {
  127. //删除在线用户状态
  128. authHeader := r.Header.Get("Authorization")
  129. if authHeader != "" {
  130. parts := strings.SplitN(authHeader, " ", 2)
  131. if len(parts) == 2 && parts[0] == "Bearer" && parts[1] != "" {
  132. //删除在线用户状态操作
  133. service.Online.DeleteOnlineByToken(parts[1])
  134. }
  135. }
  136. authHeader = r.GetString("token")
  137. if authHeader != "" {
  138. //删除在线用户状态操作
  139. service.Online.DeleteOnlineByToken(authHeader)
  140. }
  141. return true
  142. }
  143. // CheckUserOnline 检查在线用户
  144. func (c *auth) CheckUserOnline() {
  145. param := &model.SysUserOnlineSearchReq{
  146. PageReq: comModel.PageReq{
  147. PageNum: 1,
  148. PageSize: 50,
  149. },
  150. }
  151. var total int
  152. for {
  153. var (
  154. list []*model.SysUserOnline
  155. err error
  156. )
  157. total, _, list, err = service.Online.GetOnlineListPage(param, true)
  158. if err != nil {
  159. g.Log().Error(err)
  160. break
  161. }
  162. if list == nil {
  163. break
  164. }
  165. for _, v := range list {
  166. if b := c.UserIsOnline(v.Token); !b {
  167. service.Online.DeleteOnlineByToken(v.Token)
  168. }
  169. }
  170. if param.PageNum*param.PageSize >= total {
  171. break
  172. }
  173. param.PageNum++
  174. }
  175. }
  176. // UserIsOnline 判断用户是否在线
  177. func (c *auth) UserIsOnline(token string) bool {
  178. uuid, userKey := c.GetUuidUserKeyByToken(token)
  179. cacheKey := GfToken.CacheKey + userKey
  180. switch GfToken.CacheMode {
  181. case gtoken.CacheModeCache:
  182. userCacheValue, _ := gcache.Get(cacheKey)
  183. if userCacheValue == nil {
  184. return false
  185. }
  186. return true
  187. case gtoken.CacheModeRedis:
  188. var userCache g.Map
  189. userCacheJson, err := g.Redis().Do("GET", cacheKey)
  190. if err != nil {
  191. g.Log().Error("[GToken]cache get error", err)
  192. return false
  193. }
  194. if userCacheJson == nil {
  195. return false
  196. }
  197. err = gjson.DecodeTo(userCacheJson, &userCache)
  198. if err != nil {
  199. g.Log().Error("[GToken]cache get json error", err)
  200. return false
  201. }
  202. if uuid != userCache["uuid"] {
  203. return false
  204. }
  205. return true
  206. }
  207. return false
  208. }
  209. // GetUuidUserKeyByToken 通过token获取uuid和userKey
  210. func (c *auth) GetUuidUserKeyByToken(token string) (uuid, userKey string) {
  211. decryptToken := GfToken.DecryptToken(token)
  212. if !decryptToken.Success() {
  213. return
  214. }
  215. userKey = decryptToken.GetString("userKey")
  216. uuid = decryptToken.GetString("uuid")
  217. return
  218. }