搜索
查看: 69|回复: 0

[网站] 基于Golang开发一个轻量级登录库/框架

[复制链接]
发表于 2023-5-8 21:45:58 | 显示全部楼层 |阅读模式
几乎每个项目都会有登录,退出等用户功能,而登录又不单仅仅是登录,我们要考虑很多东西。所以本文就来用Golang开发一个轻量级登录库/框架吧4 ~, e+ K- v+ E* `5 Q

, r! q5 ~4 E( }% ]& |

" Z4 s% W4 y1 H
+
目录
" D' Y" _% P. C1 q+ n4 J* h* k

几乎每个项目都会有登录,退出等用户功能,而登录又不单仅仅是登录,我们要考虑很多东西。

token该怎么生成?生成什么样的?

是在Cookie存token还是请求头存token?读取的时候怎么读取?

允许同一个账号被多次登录吗?多次登录他们的token是一样的?还是不一样的?

登录也有可能分成管理员登录,用户登录等多种登录类型

我们要做的就是把这些东西封装到一起,然后能更方便的使用

而完成这些最难的就是如何设计架构了,其实要简单的封装一下并不难,本篇要讲的就是如何进行架构的设计了。

源码:weloe/token-go: a light login library (github.com)

/ |0 U" v( B# M
1.Enforcer

我们可以抽象出一个供外部调用的执行器,它包括以下几个部分

token-go/enforcer.go at master · weloe/token-go (github.com)

  1. type Enforcer struct {: y3 C7 D4 M, t0 O% M% Z
  2.     // 从配置文件读取配置需要! a, N+ J! @$ @
  3.     conf         string/ K) A: q9 X* E
  4.     // 登录类型
    ( g8 J, A; @: l( L! G
  5.     loginType    string$ @# Y  f, }$ v
  6.     config       config.TokenConfig
    : K) @$ y. P- Y; a" y: \" v
  7.     // 生成token的函数
    4 Z5 J! Z& r0 w' |$ |
  8.     generateFunc model.GenerateTokenFunc
    5 k7 _( }" o0 I- H* ]
  9.     // 用于存储数据
    6 N3 M3 v9 X! e! k/ ]1 n$ V
  10.     adapter      persist.Adapter% X. m2 L3 m2 M6 ]0 @
  11.     // 监听器% M0 Q. m7 i6 L) u( w( k. A8 \1 Z' O2 K
  12.     watcher      persist.Watcher" @# z  g7 k3 b! p5 W
  13.     // 用于记录日志
    6 R; ?$ [7 L8 }7 c
  14.     logger       log.Logger
    4 q+ Z9 Q% D- W0 u$ ]  L
  15. }
复制代码
9 w& I! y4 Y: A) P7 b

执行器的接口,包含供外部调用的方法

token-go/enforcer_interface.go at master · weloe/token-go · GitHub

  1. var _ IEnforcer = &Enforcer{}
    / v- n; n1 n* K& \" [) \6 U

  2. ( Z- m9 @( {6 y2 @1 K
  3. type IEnforcer interface {5 T) F9 G* S0 f  w
  4.      
    9 p! I7 }7 ?& M! o6 G; O& Q- s" ~
  5.     Login(id string) (string, error)  f$ a) s. R3 q0 S1 i) B
  6.     LoginByModel(id string, loginModel *model.Login) (string, error)
    & p+ N9 k! j: G- l7 ]. D! E0 I
  7.     Logout() error( a3 G& _& f* V* k8 T
  8.     IsLogin() (bool, error)
    : X) N& Z4 }' U& w
  9.     IsLoginById(id string) (bool, error)3 t  g9 `: ?0 D- ?- Q
  10.     GetLoginId() (string, error)
    . b4 O5 F$ N  {! z5 p
  11. " J6 L4 `3 z- A5 j- c
  12.     Replaced(id string, device string) error6 d4 q( |' F" B" A) E8 }. w, m
  13.     Kickout(id string, device string) error7 K5 e! t- }2 v$ R

  14. 6 ^5 f$ M3 K2 b  m+ m) K5 }; O
  15.     GetRequestToken() string
    # J# H! B, U6 E( U( ^

  16. ! L1 ~3 |  [& k' c( N+ p
  17.     SetType(t string)
    4 l' i8 ~* o9 ~9 R9 O! ^
  18.     GetType() string
    $ H& b# c5 Y/ o) v* Z: Q  q
  19.     SetContext(ctx ctx.Context)
    7 c2 A# G& t# q% g6 |
  20.     GetAdapter() persist.Adapter7 R- b; Z8 Q) b  L# ]4 t# ?& P
  21.     SetAdapter(adapter persist.Adapter)
    9 S+ u% M& Q& \0 f2 s1 [( y# d0 {* g
  22.     SetWatcher(watcher persist.Watcher)2 _9 N# ]: k8 l+ w. C
  23.     SetLogger(logger log.Logger)
    + K: H" V; D6 t# w$ I% ]
  24.     EnableLog()
    + W, W; |# j5 P0 ?* S# m
  25.     IsLogEnable() bool
    # k" }* ?- O$ A, S' e
  26.     GetSession(id string) *model.Session
    ( y# E- j  t8 _. l$ {9 @  o
  27.     SetSession(id string, session *model.Session, timeout int64) error' m2 R6 y" L  O8 s
  28. }
复制代码

6 n1 D5 p: y" d$ Q2.Config

首先就是根据需求抽象出配置信息

一个是cookie的配置

token-go/cookie.go at master · weloe/token-go · GitHub

  1. type CookieConfig struct {
    3 W$ |5 d; {5 u$ a4 l7 ~' J7 K: x# U
  2.     Domain   string
    ) |( @7 V  Q7 B, F5 |
  3.     Path     string
    ! W/ D( O7 V2 T
  4.     Secure   bool
    8 {  C; J* z7 Q
  5.     HttpOnly bool! E8 f+ x! w2 W4 S
  6.     SameSite string
    3 c( t& P, M& w  }' d  C; ]
  7. }
复制代码
( A: g% ]7 }6 V0 b- B

一个是token的配置

token-go/token.go at master · weloe/token-go · GitHub

! @3 ]; S, W" `' I* ~1 s" T
                               
登录/注册后可看大图


+ V) }# @$ \8 T: Z5 a[size=1em]
  1. type TokenConfig struct {
    ! G- f4 u, k* D
  2.    // TokenStyle
      h4 V4 G  f3 _2 B
  3.    // uuid | uuid-simple | random-string32 | random-string64 | random-string128
    5 B9 B; ?& L4 X/ y, u2 p  a
  4.    TokenStyle string: X) Q3 b/ Z7 I4 k* `; X3 L( ?
  5.      
    & @1 B3 e# ?1 l6 ^! }# y$ ^
  6.    TokenName   string  ~1 k+ i7 w) _% @
  7. : Y+ P6 t# m3 i5 A
  8.    Timeout int64
    % _( Q/ \# I- J: z$ ^; D( B

  9. 8 h+ m  q2 Q- N6 x
  10.    // 允许多次登录% E5 c% I: }$ I* r$ n! a1 ~9 g7 x( L4 l
  11.    IsConcurrent bool9 d# I* O) O0 Y, S3 s
  12.    // 多次登录共享一个token
    4 {. j9 @- E: O& C
  13.    IsShare bool# J. W! ^: Y* i: o5 G
  14.    // If (IsConcurrent == true && IsShare == false)才支持配置
    % B  T+ ^5 c" a; c/ J
  15.    // If IsConcurrent == -1, 不检查登录数量" A2 n+ I/ D2 o8 w! @
  16.    MaxLoginCount int16
    8 U8 f8 u1 \3 S+ {) W
  17. 7 ^6 P# |. n8 X2 T4 ~. u/ \
  18.    // 读取token的方式
    1 [( D7 l, V0 `+ f- T% M$ Y. `
  19.    IsReadBody   bool/ F4 q. `3 M( S) I* a! I/ M  }
  20.    IsReadHeader bool+ \) h' D' ~3 ~' x$ f
  21.    IsReadCookie bool% S8 D1 E8 C; D: d2 I, X7 A7 u

  22. * |+ `9 S% L4 d- ]* T
  23.    // 是否把token写入响应头4 a8 ?/ H; d- ?  r5 n
  24.    IsWriteHeader bool" S" b$ r$ N; |- d# t8 o# N

  25. " B, G9 `4 K% a/ l2 c; S
  26.    CookieConfig *CookieConfig" ^& S4 u; \; E. P6 g7 x
  27. }
复制代码
# g& c1 f" \0 \3 I

1 W/ q4 M( F6 z" D* V: s& t  ^[size=1em]3.Adapter

adapter是底层用来存储数据的结构,为了兼容不同的实现(不同的存储方式),设计成一个接口。

token-go/adapter.go at master · weloe/token-go · GitHub

  1. type Adapter interface {
    2 [4 ^8 A) R( ]; T1 ~. w+ L
  2. ) D8 ?1 }3 [  r! \6 c
  3.     // GetStr string operate string value4 L  Y  N4 O" N  o4 O+ O0 C. c
  4.     GetStr(key string) string) x( b3 j  Q" \2 p" F
  5.     // SetStr set store value and timeout, ]1 p; y# G: l0 n4 o5 C
  6.     SetStr(key string, value string, timeout int64) error
    8 j/ d! T, j& X3 @1 E
  7.     // UpdateStr only update value
    ' [0 _# c- R3 B
  8.     UpdateStr(key string, value string) error+ W5 ~" u, r/ V9 @+ ~) Z% ]+ P+ v% L
  9.     // DeleteStr delete string value+ ~- H, a4 t9 t9 `2 W$ `
  10.     DeleteStr(key string) error+ W. [  D* r4 W
  11.     // GetStrTimeout get expire
    % Y4 E9 n+ ~9 d1 F; x1 z
  12.     GetStrTimeout(key string) int64' |0 j& M. e  L8 _$ L6 R% G/ N
  13.     // UpdateStrTimeout update expire time% u6 f/ J# H' k3 |
  14.     UpdateStrTimeout(key string, timeout int64) error
    6 U0 N' c. {+ P

  15. % m! D0 m! [$ s' R6 U! l
  16.     // Get get interface{}. j5 D( U* ]/ R3 ^+ V* ~3 H
  17.     Get(key string) interface{}' V1 F) @/ s) m1 w* J0 p
  18.     // Set store interface{}
    0 G$ u0 P5 q1 x  _* `# ^9 u
  19.     Set(key string, value interface{}, timeout int64) error1 s: G# J8 D: ?/ Z$ |1 w
  20.     // Update only update interface{} value
    9 L5 r1 j) c4 h+ Y0 H+ q9 _( O
  21.     Update(key string, value interface{}) error3 X% ]' H: x; i5 K$ k
  22.     // Delete delete interface{} value
    " W8 a. @  y$ S
  23.     Delete(key string) error
    ! L( {7 k, @0 b8 ^+ B1 \! k% P
  24.     // GetTimeout get expire
    & p) @8 w/ C; e. T! W; v
  25.     GetTimeout(key string) int64
    0 D  U' b; D3 n* I# j
  26.     // UpdateTimeout update timeout" [) w6 h0 `. o' U# a, W
  27.     UpdateTimeout(key string, timeout int64) error" [  q2 I, x+ n. \  ?
  28. }
复制代码
* O- [6 Y' Z  q: X  p- P
4.Context

我们需要从请求读取token,可能也需要写出token,因此需要兼容不同的web上下文,我们需要设计一个Context接口

token-go/context.go at master · weloe/token-go · GitHub

  1. type Context interface {1 C9 {7 C, j. E
  2.     Request() Request
    - i! x1 I; d2 G( M
  3.     Response() Response
    , _+ t( ]$ @5 f/ c+ f
  4.     ReqStorage() ReqStorage3 Q9 t. o. l  |. s
  5.     MatchPath(pattern string, path string) bool" W4 |4 Y4 D. u  c, M" l  I5 C
  6.     IsValidContext() bool6 C  Q  X1 `2 E# ^  ^
  7. }
复制代码
5.Watcher

监听器,用于在一些事件发生的时候进行一些其他操作。

token-go/watcher.go at master · weloe/token-go · GitHub

  1. // Watcher event watcher
    9 o/ a* F( p" P: i$ @5 m
  2. type Watcher interface {
    * G* b# [7 [9 s. h) b7 B% z' ~! |
  3.     // Login called after login; l) R2 \2 h/ v# y/ ]1 I4 {5 N) L* X
  4.     Login(loginType string, id interface{}, tokenValue string, loginModel *model.Login)
    5 u- _! w0 D0 M: j% |
  5.     // Logout called after logout+ v% p* }3 g7 C
  6.     Logout(loginType string, id interface{}, tokenValue string)
    " x, {- B+ ?; Z7 _" P
  7. }
复制代码
! H$ {$ c0 T- F, i, }- ?
6.Logger

Logger,用于记录日志,方便debug等等,需要设计成可以自由开启关闭。

token-go/logger.go at master · weloe/token-go · GitHub

  1. type Logger interface {
    0 J' l; l! b: w
  2.     persist.Watcher
    ; D) C. g2 [4 z( R2 I; {2 O* b
  3. % M$ k: U1 Z: S8 e/ {# n
  4.     // Enable turn on or off
      R. {% `5 L4 y3 X% g
  5.     Enable(bool bool)4 U3 X$ d$ z! g6 i8 R6 y

  6. 2 r7 {  T% L, M; l- |( k1 q
  7.     // IsEnabled return if logger is enabled
    7 w) g9 }! m+ K$ F: J3 g# _
  8.     IsEnabled() bool6 t* |* \" I) l5 @
  9. }
复制代码

到此,项目的大致的结构就设计完成,下一篇会讲讲本业务的具体实现

到此这篇关于基于Golang开发一个轻量级登录库/框架的文章就介绍到这了,


; m9 }9 y: G" B, \) j) F! x5 k  e: Y- Q- f1 n7 R

7 l1 \6 u: z) S7 W$ ?

7 C" b: \# ~3 t2 T* Z; x8 \# f/ P3 I* k4 {/ d
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 马上注册

本版积分规则

热议作品
精华帖子排行
精彩推荐

虾皮社区,成立十年了!

站长自己也搞不懂想做个什么,反正就是一直在努力的做!

Copyright © 2007-2019 xp6.org Powered by Discuz

QQ|小黑屋|手机版|Archiver|虾皮社区 ( 鲁ICP备13006813号-1 ) 鲁公网安备 37021102000261号 |网站地图
返回顶部 返回列表