|
几乎每个项目都会有登录,退出等用户功能,而登录又不单仅仅是登录,我们要考虑很多东西。所以本文就来用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) - type Enforcer struct {: y3 C7 D4 M, t0 O% M% Z
- // 从配置文件读取配置需要! a, N+ J! @$ @
- conf string/ K) A: q9 X* E
- // 登录类型
( g8 J, A; @: l( L! G - loginType string$ @# Y f, }$ v
- config config.TokenConfig
: K) @$ y. P- Y; a" y: \" v - // 生成token的函数
4 Z5 J! Z& r0 w' |$ | - generateFunc model.GenerateTokenFunc
5 k7 _( }" o0 I- H* ] - // 用于存储数据
6 N3 M3 v9 X! e! k/ ]1 n$ V - adapter persist.Adapter% X. m2 L3 m2 M6 ]0 @
- // 监听器% M0 Q. m7 i6 L) u( w( k. A8 \1 Z' O2 K
- watcher persist.Watcher" @# z g7 k3 b! p5 W
- // 用于记录日志
6 R; ?$ [7 L8 }7 c - logger log.Logger
4 q+ Z9 Q% D- W0 u$ ] L - }
复制代码 9 w& I! y4 Y: A) P7 b
执行器的接口,包含供外部调用的方法 token-go/enforcer_interface.go at master · weloe/token-go · GitHub - var _ IEnforcer = &Enforcer{}
/ v- n; n1 n* K& \" [) \6 U -
( Z- m9 @( {6 y2 @1 K - type IEnforcer interface {5 T) F9 G* S0 f w
-
9 p! I7 }7 ?& M! o6 G; O& Q- s" ~ - Login(id string) (string, error) f$ a) s. R3 q0 S1 i) B
- LoginByModel(id string, loginModel *model.Login) (string, error)
& p+ N9 k! j: G- l7 ]. D! E0 I - Logout() error( a3 G& _& f* V* k8 T
- IsLogin() (bool, error)
: X) N& Z4 }' U& w - IsLoginById(id string) (bool, error)3 t g9 `: ?0 D- ?- Q
- GetLoginId() (string, error)
. b4 O5 F$ N {! z5 p - " J6 L4 `3 z- A5 j- c
- Replaced(id string, device string) error6 d4 q( |' F" B" A) E8 }. w, m
- Kickout(id string, device string) error7 K5 e! t- }2 v$ R
-
6 ^5 f$ M3 K2 b m+ m) K5 }; O - GetRequestToken() string
# J# H! B, U6 E( U( ^ -
! L1 ~3 | [& k' c( N+ p - SetType(t string)
4 l' i8 ~* o9 ~9 R9 O! ^ - GetType() string
$ H& b# c5 Y/ o) v* Z: Q q - SetContext(ctx ctx.Context)
7 c2 A# G& t# q% g6 | - GetAdapter() persist.Adapter7 R- b; Z8 Q) b L# ]4 t# ?& P
- SetAdapter(adapter persist.Adapter)
9 S+ u% M& Q& \0 f2 s1 [( y# d0 {* g - SetWatcher(watcher persist.Watcher)2 _9 N# ]: k8 l+ w. C
- SetLogger(logger log.Logger)
+ K: H" V; D6 t# w$ I% ] - EnableLog()
+ W, W; |# j5 P0 ?* S# m - IsLogEnable() bool
# k" }* ?- O$ A, S' e - GetSession(id string) *model.Session
( y# E- j t8 _. l$ {9 @ o - SetSession(id string, session *model.Session, timeout int64) error' m2 R6 y" L O8 s
- }
复制代码
6 n1 D5 p: y" d$ Q2.Config首先就是根据需求抽象出配置信息 一个是cookie的配置 token-go/cookie.go at master · weloe/token-go · GitHub - type CookieConfig struct {
3 W$ |5 d; {5 u$ a4 l7 ~' J7 K: x# U - Domain string
) |( @7 V Q7 B, F5 | - Path string
! W/ D( O7 V2 T - Secure bool
8 { C; J* z7 Q - HttpOnly bool! E8 f+ x! w2 W4 S
- SameSite string
3 c( t& P, M& w }' d C; ] - }
复制代码 ( A: g% ]7 }6 V0 b- B
一个是token的配置 token-go/token.go at master · weloe/token-go · GitHub
+ V) }# @$ \8 T: Z5 a[size=1em]- type TokenConfig struct {
! G- f4 u, k* D - // TokenStyle
h4 V4 G f3 _2 B - // uuid | uuid-simple | random-string32 | random-string64 | random-string128
5 B9 B; ?& L4 X/ y, u2 p a - TokenStyle string: X) Q3 b/ Z7 I4 k* `; X3 L( ?
-
& @1 B3 e# ?1 l6 ^! }# y$ ^ - TokenName string ~1 k+ i7 w) _% @
- : Y+ P6 t# m3 i5 A
- Timeout int64
% _( Q/ \# I- J: z$ ^; D( B -
8 h+ m q2 Q- N6 x - // 允许多次登录% E5 c% I: }$ I* r$ n! a1 ~9 g7 x( L4 l
- IsConcurrent bool9 d# I* O) O0 Y, S3 s
- // 多次登录共享一个token
4 {. j9 @- E: O& C - IsShare bool# J. W! ^: Y* i: o5 G
- // If (IsConcurrent == true && IsShare == false)才支持配置
% B T+ ^5 c" a; c/ J - // If IsConcurrent == -1, 不检查登录数量" A2 n+ I/ D2 o8 w! @
- MaxLoginCount int16
8 U8 f8 u1 \3 S+ {) W - 7 ^6 P# |. n8 X2 T4 ~. u/ \
- // 读取token的方式
1 [( D7 l, V0 `+ f- T% M$ Y. ` - IsReadBody bool/ F4 q. `3 M( S) I* a! I/ M }
- IsReadHeader bool+ \) h' D' ~3 ~' x$ f
- IsReadCookie bool% S8 D1 E8 C; D: d2 I, X7 A7 u
-
* |+ `9 S% L4 d- ]* T - // 是否把token写入响应头4 a8 ?/ H; d- ? r5 n
- IsWriteHeader bool" S" b$ r$ N; |- d# t8 o# N
-
" B, G9 `4 K% a/ l2 c; S - CookieConfig *CookieConfig" ^& S4 u; \; E. P6 g7 x
- }
复制代码 # g& c1 f" \0 \3 I
1 W/ q4 M( F6 z" D* V: s& t ^[size=1em]3.Adapteradapter是底层用来存储数据的结构,为了兼容不同的实现(不同的存储方式),设计成一个接口。 token-go/adapter.go at master · weloe/token-go · GitHub - type Adapter interface {
2 [4 ^8 A) R( ]; T1 ~. w+ L - ) D8 ?1 }3 [ r! \6 c
- // GetStr string operate string value4 L Y N4 O" N o4 O+ O0 C. c
- GetStr(key string) string) x( b3 j Q" \2 p" F
- // SetStr set store value and timeout, ]1 p; y# G: l0 n4 o5 C
- SetStr(key string, value string, timeout int64) error
8 j/ d! T, j& X3 @1 E - // UpdateStr only update value
' [0 _# c- R3 B - UpdateStr(key string, value string) error+ W5 ~" u, r/ V9 @+ ~) Z% ]+ P+ v% L
- // DeleteStr delete string value+ ~- H, a4 t9 t9 `2 W$ `
- DeleteStr(key string) error+ W. [ D* r4 W
- // GetStrTimeout get expire
% Y4 E9 n+ ~9 d1 F; x1 z - GetStrTimeout(key string) int64' |0 j& M. e L8 _$ L6 R% G/ N
- // UpdateStrTimeout update expire time% u6 f/ J# H' k3 |
- UpdateStrTimeout(key string, timeout int64) error
6 U0 N' c. {+ P -
% m! D0 m! [$ s' R6 U! l - // Get get interface{}. j5 D( U* ]/ R3 ^+ V* ~3 H
- Get(key string) interface{}' V1 F) @/ s) m1 w* J0 p
- // Set store interface{}
0 G$ u0 P5 q1 x _* `# ^9 u - Set(key string, value interface{}, timeout int64) error1 s: G# J8 D: ?/ Z$ |1 w
- // Update only update interface{} value
9 L5 r1 j) c4 h+ Y0 H+ q9 _( O - Update(key string, value interface{}) error3 X% ]' H: x; i5 K$ k
- // Delete delete interface{} value
" W8 a. @ y$ S - Delete(key string) error
! L( {7 k, @0 b8 ^+ B1 \! k% P - // GetTimeout get expire
& p) @8 w/ C; e. T! W; v - GetTimeout(key string) int64
0 D U' b; D3 n* I# j - // UpdateTimeout update timeout" [) w6 h0 `. o' U# a, W
- UpdateTimeout(key string, timeout int64) error" [ q2 I, x+ n. \ ?
- }
复制代码 * O- [6 Y' Z q: X p- P
4.Context我们需要从请求读取token,可能也需要写出token,因此需要兼容不同的web上下文,我们需要设计一个Context接口 token-go/context.go at master · weloe/token-go · GitHub - type Context interface {1 C9 {7 C, j. E
- Request() Request
- i! x1 I; d2 G( M - Response() Response
, _+ t( ]$ @5 f/ c+ f - ReqStorage() ReqStorage3 Q9 t. o. l |. s
- MatchPath(pattern string, path string) bool" W4 |4 Y4 D. u c, M" l I5 C
- IsValidContext() bool6 C Q X1 `2 E# ^ ^
- }
复制代码 5.Watcher监听器,用于在一些事件发生的时候进行一些其他操作。 token-go/watcher.go at master · weloe/token-go · GitHub - // Watcher event watcher
9 o/ a* F( p" P: i$ @5 m - type Watcher interface {
* G* b# [7 [9 s. h) b7 B% z' ~! | - // Login called after login; l) R2 \2 h/ v# y/ ]1 I4 {5 N) L* X
- Login(loginType string, id interface{}, tokenValue string, loginModel *model.Login)
5 u- _! w0 D0 M: j% | - // Logout called after logout+ v% p* }3 g7 C
- Logout(loginType string, id interface{}, tokenValue string)
" x, {- B+ ?; Z7 _" P - }
复制代码 ! H$ {$ c0 T- F, i, }- ?
6.LoggerLogger,用于记录日志,方便debug等等,需要设计成可以自由开启关闭。 token-go/logger.go at master · weloe/token-go · GitHub - type Logger interface {
0 J' l; l! b: w - persist.Watcher
; D) C. g2 [4 z( R2 I; {2 O* b - % M$ k: U1 Z: S8 e/ {# n
- // Enable turn on or off
R. {% `5 L4 y3 X% g - Enable(bool bool)4 U3 X$ d$ z! g6 i8 R6 y
-
2 r7 { T% L, M; l- |( k1 q - // IsEnabled return if logger is enabled
7 w) g9 }! m+ K$ F: J3 g# _ - IsEnabled() bool6 t* |* \" I) l5 @
- }
复制代码到此,项目的大致的结构就设计完成,下一篇会讲讲本业务的具体实现 到此这篇关于基于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
|
|