gtoken.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. package gtoken
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "github.com/gogf/gf/v2/crypto/gaes"
  7. "github.com/gogf/gf/v2/crypto/gmd5"
  8. "github.com/gogf/gf/v2/encoding/gbase64"
  9. "github.com/gogf/gf/v2/frame/g"
  10. "github.com/gogf/gf/v2/net/ghttp"
  11. "github.com/gogf/gf/v2/os/gtime"
  12. "github.com/gogf/gf/v2/text/gstr"
  13. "github.com/gogf/gf/v2/util/gconv"
  14. "github.com/gogf/gf/v2/util/grand"
  15. "net/http"
  16. "strings"
  17. )
  18. var GFToken *GfToken
  19. // GfToken gtoken结构体
  20. type GfToken struct {
  21. // GoFrame server name
  22. ServerName string
  23. // 缓存模式 1 gcache 2 gredis 默认1
  24. CacheMode int8
  25. // 缓存key
  26. CacheKey string
  27. // 超时时间 默认10天(毫秒)
  28. Timeout int
  29. // 缓存刷新时间 默认为超时时间的一半(毫秒)
  30. MaxRefresh int
  31. // Token分隔符
  32. TokenDelimiter string
  33. // Token加密key
  34. EncryptKey []byte
  35. // 认证失败中文提示
  36. AuthFailMsg string
  37. // 是否支持多端登录,默认false
  38. MultiLogin bool
  39. // 是否是全局认证,兼容历史版本,已废弃
  40. GlobalMiddleware bool
  41. // 中间件类型 1 GroupMiddleware 2 BindMiddleware 3 GlobalMiddleware
  42. MiddlewareType uint
  43. // 登录路径
  44. LoginPath string
  45. // 登录验证方法 return userKey 用户标识 如果userKey为空,结束执行
  46. LoginBeforeFunc func(r *ghttp.Request) (string, interface{})
  47. // 登录返回方法
  48. LoginAfterFunc func(r *ghttp.Request, respData Resp)
  49. // 登出地址
  50. LogoutPath string
  51. // 登出验证方法 return true 继续执行,否则结束执行
  52. LogoutBeforeFunc func(r *ghttp.Request) bool
  53. // 登出返回方法
  54. LogoutAfterFunc func(r *ghttp.Request, respData Resp)
  55. // 拦截地址
  56. AuthPaths g.SliceStr
  57. // 拦截排除地址
  58. AuthExcludePaths g.SliceStr
  59. // 认证验证方法 return true 继续执行,否则结束执行
  60. AuthBeforeFunc func(r *ghttp.Request) bool
  61. // 认证返回方法
  62. AuthAfterFunc func(r *ghttp.Request, respData Resp)
  63. }
  64. // Login 登录
  65. func (m *GfToken) Login(r *ghttp.Request) {
  66. userKey, data := m.LoginBeforeFunc(r)
  67. if userKey == "" {
  68. g.Log().Error(r.Context(), msgLog(MsgErrUserKeyEmpty))
  69. return
  70. }
  71. if m.MultiLogin {
  72. // 支持多端重复登录,返回相同token
  73. userCacheResp := m.getToken(r.Context(), userKey)
  74. if userCacheResp.Success() {
  75. respToken := m.EncryptToken(r.Context(), userKey, userCacheResp.GetString(KeyUuid))
  76. m.LoginAfterFunc(r, respToken)
  77. return
  78. }
  79. }
  80. // 生成token
  81. respToken := m.genToken(r.Context(), userKey, data)
  82. m.LoginAfterFunc(r, respToken)
  83. }
  84. // Logout 登出
  85. func (m *GfToken) Logout(r *ghttp.Request) {
  86. if !m.LogoutBeforeFunc(r) {
  87. return
  88. }
  89. // 获取请求token
  90. respData := m.getRequestToken(r)
  91. if respData.Success() {
  92. // 删除token
  93. m.RemoveToken(r.Context(), respData.DataString())
  94. }
  95. m.LogoutAfterFunc(r, respData)
  96. }
  97. // AuthMiddleware 认证拦截
  98. func (m *GfToken) authMiddleware(r *ghttp.Request) {
  99. urlPath := r.URL.Path
  100. if !m.AuthPath(r.Context(), urlPath) {
  101. // 如果不需要认证,继续
  102. r.Middleware.Next()
  103. return
  104. }
  105. // 不需要认证,直接下一步
  106. if !m.AuthBeforeFunc(r) {
  107. r.Middleware.Next()
  108. return
  109. }
  110. // 获取请求token
  111. tokenResp := m.getRequestToken(r)
  112. if tokenResp.Success() {
  113. // 验证token
  114. tokenResp = m.validToken(r.Context(), tokenResp.DataString())
  115. }
  116. m.AuthAfterFunc(r, tokenResp)
  117. }
  118. // GetTokenData 通过token获取对象
  119. func (m *GfToken) GetTokenData(r *ghttp.Request) Resp {
  120. respData := m.getRequestToken(r)
  121. if respData.Success() {
  122. // 验证token
  123. respData = m.validToken(r.Context(), respData.DataString())
  124. }
  125. return respData
  126. }
  127. // AuthPath 判断路径是否需要进行认证拦截
  128. // return true 需要认证
  129. func (m *GfToken) AuthPath(ctx context.Context, urlPath string) bool {
  130. // 去除后斜杠
  131. if strings.HasSuffix(urlPath, "/") {
  132. urlPath = gstr.SubStr(urlPath, 0, len(urlPath)-1)
  133. }
  134. // 分组拦截,登录接口不拦截
  135. if m.MiddlewareType == MiddlewareTypeGroup {
  136. if (m.LoginPath != "" && gstr.HasSuffix(urlPath, m.LoginPath)) ||
  137. (m.LogoutPath != "" && gstr.HasSuffix(urlPath, m.LogoutPath)) {
  138. return false
  139. }
  140. }
  141. // 全局处理,认证路径拦截处理
  142. if m.MiddlewareType == MiddlewareTypeGlobal {
  143. var authFlag bool
  144. for _, authPath := range m.AuthPaths {
  145. tmpPath := authPath
  146. if strings.HasSuffix(tmpPath, "/*") {
  147. tmpPath = gstr.SubStr(tmpPath, 0, len(tmpPath)-2)
  148. }
  149. if gstr.HasPrefix(urlPath, tmpPath) {
  150. authFlag = true
  151. break
  152. }
  153. }
  154. if !authFlag {
  155. // 拦截路径不匹配
  156. return false
  157. }
  158. }
  159. // 排除路径处理,到这里nextFlag为true
  160. for _, excludePath := range m.AuthExcludePaths {
  161. tmpPath := excludePath
  162. // 前缀匹配
  163. if strings.HasSuffix(tmpPath, "/*") {
  164. tmpPath = gstr.SubStr(tmpPath, 0, len(tmpPath)-2)
  165. if gstr.HasPrefix(urlPath, tmpPath) {
  166. // 前缀匹配不拦截
  167. return false
  168. }
  169. } else {
  170. // 全路径匹配
  171. if strings.HasSuffix(tmpPath, "/") {
  172. tmpPath = gstr.SubStr(tmpPath, 0, len(tmpPath)-1)
  173. }
  174. if urlPath == tmpPath {
  175. // 全路径匹配不拦截
  176. return false
  177. }
  178. }
  179. }
  180. return true
  181. }
  182. // getRequestToken 返回请求Token
  183. func (m *GfToken) getRequestToken(r *ghttp.Request) Resp {
  184. authHeader := r.Header.Get("Authorization")
  185. if authHeader != "" {
  186. parts := strings.SplitN(authHeader, " ", 2)
  187. if !(len(parts) == 2 && parts[0] == "Bearer") {
  188. g.Log().Warning(r.Context(), msgLog(MsgErrAuthHeader, authHeader))
  189. return Unauthorized(fmt.Sprintf(MsgErrAuthHeader, authHeader), "")
  190. } else if parts[1] == "" {
  191. g.Log().Warning(r.Context(), msgLog(MsgErrAuthHeader, authHeader))
  192. return Unauthorized(fmt.Sprintf(MsgErrAuthHeader, authHeader), "")
  193. }
  194. return Succ(parts[1])
  195. }
  196. authHeader = r.Get(KeyToken).String()
  197. if authHeader == "" {
  198. return Unauthorized(MsgErrTokenEmpty, "")
  199. }
  200. return Succ(authHeader)
  201. }
  202. // genToken 生成Token
  203. func (m *GfToken) genToken(ctx context.Context, userKey string, data interface{}) Resp {
  204. token := m.EncryptToken(ctx, userKey, "")
  205. if !token.Success() {
  206. return token
  207. }
  208. cacheKey := m.CacheKey + userKey
  209. userCache := g.Map{
  210. KeyUserKey: userKey,
  211. KeyUuid: token.GetString(KeyUuid),
  212. KeyData: data,
  213. KeyCreateTime: gtime.Now().TimestampMilli(),
  214. KeyRefreshTime: gtime.Now().TimestampMilli() + gconv.Int64(m.MaxRefresh),
  215. }
  216. cacheResp := m.setCache(ctx, cacheKey, userCache)
  217. if !cacheResp.Success() {
  218. return cacheResp
  219. }
  220. return token
  221. }
  222. // validToken 验证Token
  223. func (m *GfToken) validToken(ctx context.Context, token string) Resp {
  224. if token == "" {
  225. return Unauthorized(MsgErrTokenEmpty, "")
  226. }
  227. decryptToken := m.DecryptToken(ctx, token)
  228. if !decryptToken.Success() {
  229. return decryptToken
  230. }
  231. userKey := decryptToken.GetString(KeyUserKey)
  232. uuid := decryptToken.GetString(KeyUuid)
  233. userCacheResp := m.getToken(ctx, userKey)
  234. if !userCacheResp.Success() {
  235. return userCacheResp
  236. }
  237. if uuid != userCacheResp.GetString(KeyUuid) {
  238. g.Log().Debug(ctx, msgLog(MsgErrAuthUuid)+", decryptToken:"+decryptToken.Json()+" cacheValue:"+gconv.String(userCacheResp.Data))
  239. return Unauthorized(MsgErrAuthUuid, "")
  240. }
  241. return userCacheResp
  242. }
  243. // getToken 通过userKey获取Token
  244. func (m *GfToken) getToken(ctx context.Context, userKey string) Resp {
  245. cacheKey := m.CacheKey + userKey
  246. userCacheResp := m.getCache(ctx, cacheKey)
  247. if !userCacheResp.Success() {
  248. return userCacheResp
  249. }
  250. userCache := gconv.Map(userCacheResp.Data)
  251. nowTime := gtime.Now().TimestampMilli()
  252. refreshTime := userCache[KeyRefreshTime]
  253. // 需要进行缓存超时时间刷新
  254. if gconv.Int64(refreshTime) == 0 || nowTime > gconv.Int64(refreshTime) {
  255. userCache[KeyCreateTime] = gtime.Now().TimestampMilli()
  256. userCache[KeyRefreshTime] = gtime.Now().TimestampMilli() + gconv.Int64(m.MaxRefresh)
  257. return m.setCache(ctx, cacheKey, userCache)
  258. }
  259. return Succ(userCache)
  260. }
  261. // RemoveToken 删除Token
  262. func (m *GfToken) RemoveToken(ctx context.Context, token string) Resp {
  263. decryptToken := m.DecryptToken(ctx, token)
  264. if !decryptToken.Success() {
  265. return decryptToken
  266. }
  267. cacheKey := m.CacheKey + decryptToken.GetString(KeyUserKey)
  268. return m.removeCache(ctx, cacheKey)
  269. }
  270. // EncryptToken token加密方法
  271. func (m *GfToken) EncryptToken(ctx context.Context, userKey string, uuid string) Resp {
  272. if userKey == "" {
  273. return Fail(MsgErrUserKeyEmpty)
  274. }
  275. if uuid == "" {
  276. // 重新生成uuid
  277. newUuid, err := gmd5.Encrypt(grand.Letters(10))
  278. if err != nil {
  279. g.Log().Error(ctx, msgLog(MsgErrAuthUuid), err)
  280. return Error(MsgErrAuthUuid)
  281. }
  282. uuid = newUuid
  283. }
  284. tokenStr := userKey + m.TokenDelimiter + uuid
  285. token, err := gaes.Encrypt([]byte(tokenStr), m.EncryptKey)
  286. if err != nil {
  287. g.Log().Error(ctx, msgLog(MsgErrTokenEncrypt), tokenStr, err)
  288. return Error(MsgErrTokenEncrypt)
  289. }
  290. return Succ(g.Map{
  291. KeyUserKey: userKey,
  292. KeyUuid: uuid,
  293. KeyToken: gbase64.EncodeToString(token),
  294. })
  295. }
  296. // DecryptToken token解密方法
  297. func (m *GfToken) DecryptToken(ctx context.Context, token string) Resp {
  298. if token == "" {
  299. return Fail(MsgErrTokenEmpty)
  300. }
  301. token64, err := gbase64.Decode([]byte(token))
  302. if err != nil {
  303. g.Log().Error(ctx, msgLog(MsgErrTokenDecode), token, err)
  304. return Error(MsgErrTokenDecode)
  305. }
  306. decryptToken, err2 := gaes.Decrypt(token64, m.EncryptKey)
  307. if err2 != nil {
  308. g.Log().Error(ctx, msgLog(MsgErrTokenEncrypt), token, err2)
  309. return Error(MsgErrTokenEncrypt)
  310. }
  311. tokenArray := gstr.Split(string(decryptToken), m.TokenDelimiter)
  312. if len(tokenArray) < 2 {
  313. g.Log().Error(ctx, msgLog(MsgErrTokenLen), token)
  314. return Error(MsgErrTokenLen)
  315. }
  316. return Succ(g.Map{
  317. KeyUserKey: tokenArray[0],
  318. KeyUuid: tokenArray[1],
  319. })
  320. }
  321. // InitConfig 初始化配置信息
  322. func (m *GfToken) InitConfig() bool {
  323. if m.CacheMode == 0 {
  324. m.CacheMode = CacheModeCache
  325. }
  326. if m.CacheKey == "" {
  327. m.CacheKey = DefaultCacheKey
  328. }
  329. if m.Timeout == 0 {
  330. m.Timeout = DefaultTimeout
  331. }
  332. if m.MaxRefresh == 0 {
  333. m.MaxRefresh = m.Timeout / 2
  334. }
  335. if m.TokenDelimiter == "" {
  336. m.TokenDelimiter = DefaultTokenDelimiter
  337. }
  338. if len(m.EncryptKey) == 0 {
  339. m.EncryptKey = []byte(DefaultEncryptKey)
  340. }
  341. if m.AuthFailMsg == "" {
  342. m.AuthFailMsg = DefaultAuthFailMsg
  343. }
  344. // 设置中间件模式,未设置说明历史版本,通过GlobalMiddleware兼容
  345. if m.MiddlewareType == 0 {
  346. if m.GlobalMiddleware {
  347. m.MiddlewareType = MiddlewareTypeGlobal
  348. } else {
  349. m.MiddlewareType = MiddlewareTypeBind
  350. }
  351. }
  352. if m.LoginAfterFunc == nil {
  353. m.LoginAfterFunc = func(r *ghttp.Request, respData Resp) {
  354. if !respData.Success() {
  355. r.Response.WriteJson(respData)
  356. } else {
  357. r.Response.WriteJson(Succ(g.Map{
  358. KeyToken: respData.GetString(KeyToken),
  359. }))
  360. }
  361. }
  362. }
  363. if m.LogoutBeforeFunc == nil {
  364. m.LogoutBeforeFunc = func(r *ghttp.Request) bool {
  365. return true
  366. }
  367. }
  368. if m.LogoutAfterFunc == nil {
  369. m.LogoutAfterFunc = func(r *ghttp.Request, respData Resp) {
  370. if respData.Success() {
  371. r.Response.WriteJson(Succ(MsgLogoutSucc))
  372. } else {
  373. r.Response.WriteJson(respData)
  374. }
  375. }
  376. }
  377. if m.AuthBeforeFunc == nil {
  378. m.AuthBeforeFunc = func(r *ghttp.Request) bool {
  379. // 静态页面不拦截
  380. if r.IsFileRequest() {
  381. return false
  382. }
  383. return true
  384. }
  385. }
  386. if m.AuthAfterFunc == nil {
  387. m.AuthAfterFunc = func(r *ghttp.Request, respData Resp) {
  388. if respData.Success() {
  389. r.Middleware.Next()
  390. } else {
  391. var params map[string]interface{}
  392. if r.Method == http.MethodGet {
  393. params = r.GetMap()
  394. } else if r.Method == http.MethodPost {
  395. params = r.GetMap()
  396. } else {
  397. r.Response.Writeln(MsgErrReqMethod)
  398. return
  399. }
  400. no := gconv.String(gtime.TimestampMilli())
  401. g.Log().Warning(r.Context(), fmt.Sprintf("[AUTH_%s][url:%s][params:%s][data:%s]",
  402. no, r.URL.Path, params, respData.Json()))
  403. respData.Msg = m.AuthFailMsg
  404. r.Response.WriteJson(respData)
  405. r.ExitAll()
  406. }
  407. }
  408. }
  409. return true
  410. }
  411. // Start 启动
  412. func (m *GfToken) Start() error {
  413. if !m.InitConfig() {
  414. return errors.New(MsgErrInitFail)
  415. }
  416. ctx := context.Background()
  417. g.Log().Info(ctx, msgLog("[params:"+m.String()+"]start... "))
  418. s := g.Server(m.ServerName)
  419. // 缓存模式
  420. if m.CacheMode > CacheModeFile {
  421. g.Log().Error(ctx, msgLog(MsgErrNotSet, "CacheMode"))
  422. return errors.New(fmt.Sprintf(MsgErrNotSet, "CacheMode"))
  423. }
  424. // 初始化文件缓存
  425. if m.CacheMode == 3 {
  426. m.initFileCache(ctx)
  427. }
  428. // 认证拦截器
  429. if m.AuthPaths == nil {
  430. g.Log().Error(ctx, msgLog(MsgErrNotSet, "AuthPaths"))
  431. return errors.New(fmt.Sprintf(MsgErrNotSet, "AuthPaths"))
  432. }
  433. // 是否是全局拦截
  434. if m.MiddlewareType == MiddlewareTypeGlobal {
  435. s.BindMiddlewareDefault(m.authMiddleware)
  436. } else {
  437. for _, authPath := range m.AuthPaths {
  438. tmpPath := authPath
  439. if !strings.HasSuffix(authPath, "/*") {
  440. tmpPath += "/*"
  441. }
  442. s.BindMiddleware(tmpPath, m.authMiddleware)
  443. }
  444. }
  445. // 登录
  446. if m.LoginPath == "" {
  447. g.Log().Error(ctx, msgLog(MsgErrNotSet, "LoginPath"))
  448. return errors.New(fmt.Sprintf(MsgErrNotSet, "LoginPath"))
  449. }
  450. if m.LoginBeforeFunc == nil {
  451. g.Log().Error(ctx, msgLog(MsgErrNotSet, "LoginBeforeFunc"))
  452. return errors.New(fmt.Sprintf(MsgErrNotSet, "LoginBeforeFunc"))
  453. }
  454. s.BindHandler(m.LoginPath, m.Login)
  455. // 登出
  456. if m.LogoutPath == "" {
  457. g.Log().Error(ctx, msgLog(MsgErrNotSet, "LogoutPath"))
  458. return errors.New(fmt.Sprintf(MsgErrNotSet, "LogoutPath"))
  459. }
  460. s.BindHandler(m.LogoutPath, m.Logout)
  461. return nil
  462. }
  463. // Stop 结束
  464. func (m *GfToken) Stop(ctx context.Context) error {
  465. g.Log().Info(ctx, "[GToken]stop. ")
  466. return nil
  467. }
  468. // String token解密方法
  469. func (m *GfToken) String() string {
  470. return gconv.String(g.Map{
  471. // 缓存模式 1 gcache 2 gredis 默认1
  472. "CacheMode": m.CacheMode,
  473. "CacheKey": m.CacheKey,
  474. "Timeout": m.Timeout,
  475. "TokenDelimiter": m.TokenDelimiter,
  476. "AuthFailMsg": m.AuthFailMsg,
  477. "MultiLogin": m.MultiLogin,
  478. "MiddlewareType": m.MiddlewareType,
  479. "LoginPath": m.LoginPath,
  480. "LogoutPath": m.LogoutPath,
  481. "AuthPaths": gconv.String(m.AuthPaths),
  482. "AuthExcludePaths": gconv.String(m.AuthExcludePaths),
  483. })
  484. }
  485. // ValidToken 验证Token
  486. func (m *GfToken) ValidToken(ctx context.Context, token string) Resp {
  487. if token == "" {
  488. return Unauthorized(MsgErrTokenEmpty, "")
  489. }
  490. decryptToken := m.DecryptToken(ctx, token)
  491. if !decryptToken.Success() {
  492. return decryptToken
  493. }
  494. userKey := decryptToken.GetString(KeyUserKey)
  495. uuid := decryptToken.GetString(KeyUuid)
  496. userCacheResp := m.getToken(ctx, userKey)
  497. if !userCacheResp.Success() {
  498. return userCacheResp
  499. }
  500. if uuid != userCacheResp.GetString(KeyUuid) {
  501. g.Log().Debug(ctx, msgLog(MsgErrAuthUuid)+", decryptToken:"+decryptToken.Json()+" cacheValue:"+gconv.String(userCacheResp.Data))
  502. return Unauthorized(MsgErrAuthUuid, "")
  503. }
  504. return userCacheResp
  505. }