feat: add redis
This commit is contained in:
@ -1,10 +1,14 @@
|
||||
RUST_LOG=info,udmin=debug
|
||||
APP_ENV=development
|
||||
APP_HOST=0.0.0.0
|
||||
APP_PORT=9898
|
||||
APP_PORT=9891
|
||||
DB_URL=mysql://root:123456@127.0.0.1:3306/udmin
|
||||
JWT_SECRET=dev_secret_change_me
|
||||
JWT_ISS=udmin
|
||||
JWT_ACCESS_EXP_SECS=1800
|
||||
JWT_REFRESH_EXP_SECS=1209600
|
||||
CORS_ALLOW_ORIGINS=http://localhost:5173,http://localhost:5174,http://localhost:5175
|
||||
|
||||
# Redis配置
|
||||
REDIS_URL=redis://:123456@127.0.0.1:6379/9
|
||||
REDIS_TOKEN_VALIDATION=true
|
||||
@ -1,10 +1,22 @@
|
||||
RUST_LOG=info,udmin=debug
|
||||
APP_ENV=development
|
||||
APP_HOST=0.0.0.0
|
||||
APP_PORT=9898
|
||||
DB_URL=mysql://root:123456@127.0.0.1:3306/udmin
|
||||
JWT_SECRET=please_change_me
|
||||
# 数据库配置
|
||||
DATABASE_URL=sqlite://udmin.db?mode=rwc
|
||||
|
||||
# JWT配置
|
||||
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
|
||||
JWT_ISS=udmin
|
||||
JWT_ACCESS_EXP_SECS=1800
|
||||
JWT_REFRESH_EXP_SECS=1209600
|
||||
JWT_ACCESS_EXP_SECS=1800 # 30分钟
|
||||
JWT_REFRESH_EXP_SECS=1209600 # 14天
|
||||
|
||||
# Redis配置
|
||||
REDIS_URL=redis://:123456@127.0.0.1:6379/9
|
||||
REDIS_TOKEN_VALIDATION=true # 是否启用Redis token验证
|
||||
|
||||
# 服务器配置
|
||||
APP_HOST=0.0.0.0
|
||||
APP_PORT=8080
|
||||
|
||||
# CORS配置
|
||||
CORS_ALLOW_ORIGINS=http://localhost:5173
|
||||
|
||||
# 日志级别
|
||||
RUST_LOG=info
|
||||
573
backend/Cargo.lock
generated
573
backend/Cargo.lock
generated
@ -35,6 +35,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"const-random",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
@ -132,6 +134,12 @@ version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
@ -220,10 +228,10 @@ dependencies = [
|
||||
"axum-core 0.4.5",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper 1.7.0",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit 0.7.3",
|
||||
@ -236,7 +244,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"sync_wrapper 1.0.2",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
@ -254,10 +262,10 @@ dependencies = [
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper 1.7.0",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit 0.8.4",
|
||||
@ -270,7 +278,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"sync_wrapper 1.0.2",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
@ -287,13 +295,13 @@ dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"sync_wrapper",
|
||||
"sync_wrapper 1.0.2",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@ -307,18 +315,27 @@ checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"sync_wrapper",
|
||||
"sync_wrapper 1.0.2",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backon"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "592277618714fbcecda9a02ba7a8781f319d26532a88553bbacc77ba5d2b3a8d"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.75"
|
||||
@ -366,6 +383,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.2"
|
||||
@ -550,6 +573,20 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
@ -613,6 +650,16 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
@ -829,6 +876,18 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.2"
|
||||
@ -885,6 +944,7 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
@ -935,6 +995,17 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
@ -953,8 +1024,10 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
@ -1010,6 +1083,25 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"indexmap 2.10.0",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
@ -1103,6 +1195,17 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.3.1"
|
||||
@ -1114,6 +1217,17 @@ dependencies = [
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 0.2.12",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.1"
|
||||
@ -1121,7 +1235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"http 1.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1132,8 +1246,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
@ -1149,6 +1263,30 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.7.0"
|
||||
@ -1159,8 +1297,8 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
@ -1170,6 +1308,20 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"hyper 0.14.32",
|
||||
"rustls 0.21.12",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.16"
|
||||
@ -1178,9 +1330,9 @@ checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"hyper 1.7.0",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
@ -1356,23 +1508,47 @@ dependencies = [
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.9.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
@ -1442,7 +1618,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.9.2",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
@ -1666,6 +1842,9 @@ name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
@ -1837,6 +2016,16 @@ dependencies = [
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pgvector"
|
||||
version = "0.4.1"
|
||||
@ -1885,6 +2074,12 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.2"
|
||||
@ -2057,13 +2252,39 @@ dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "0.27.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d8f99a4090c89cc489a94833c901ead69bfbf3877b4867d5482e321ee875bc"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
"backon",
|
||||
"bytes",
|
||||
"combine",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"itertools",
|
||||
"itoa",
|
||||
"num-bigint",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"ryu",
|
||||
"sha1_smol",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2139,6 +2360,77 @@ dependencies = [
|
||||
"bytecheck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.32",
|
||||
"hyper-rustls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls 0.21.12",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 0.1.2",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots 0.25.4",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rhai"
|
||||
version = "1.22.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2780e813b755850e50b178931aaf94ed24f6817f46aaaf5d21c13c12d939a249"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"bitflags 2.9.2",
|
||||
"instant",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"rhai_codegen",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"smartstring",
|
||||
"thin-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rhai_codegen"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
@ -2189,7 +2481,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bitflags",
|
||||
"bitflags 2.9.2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
]
|
||||
@ -2280,6 +2572,18 @@ version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-webpki 0.101.7",
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.31"
|
||||
@ -2289,11 +2593,20 @@ dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"rustls-webpki 0.103.4",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.12.0"
|
||||
@ -2303,6 +2616,16 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.4"
|
||||
@ -2365,6 +2688,16 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sea-bae"
|
||||
version = "0.2.1"
|
||||
@ -2624,6 +2957,19 @@ dependencies = [
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.34+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
||||
dependencies = [
|
||||
"indexmap 2.10.0",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
@ -2635,6 +2981,12 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
@ -2713,6 +3065,28 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smartstring"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.0"
|
||||
@ -2781,7 +3155,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"rust_decimal",
|
||||
"rustls",
|
||||
"rustls 0.23.31",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
@ -2843,7 +3217,7 @@ dependencies = [
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
"bigdecimal",
|
||||
"bitflags",
|
||||
"bitflags 2.9.2",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
@ -2890,7 +3264,7 @@ dependencies = [
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
"bigdecimal",
|
||||
"bitflags",
|
||||
"bitflags 2.9.2",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"crc",
|
||||
@ -3014,6 +3388,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "1.0.2"
|
||||
@ -3031,12 +3411,42 @@ dependencies = [
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "thin-vec"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
@ -3166,7 +3576,7 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"socket2",
|
||||
"socket2 0.6.0",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
@ -3182,6 +3592,16 @@ dependencies = [
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||
dependencies = [
|
||||
"rustls 0.21.12",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.17"
|
||||
@ -3193,6 +3613,19 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.23"
|
||||
@ -3243,7 +3676,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper",
|
||||
"sync_wrapper 1.0.2",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
@ -3256,10 +3689,10 @@ version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.9.2",
|
||||
"bytes",
|
||||
"http",
|
||||
"http-body",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"pin-project-lite",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
@ -3340,6 +3773,12 @@ dependencies = [
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
@ -3364,15 +3803,21 @@ dependencies = [
|
||||
"chrono",
|
||||
"config",
|
||||
"dotenvy",
|
||||
"hyper",
|
||||
"hyper 1.7.0",
|
||||
"jsonwebtoken",
|
||||
"migration",
|
||||
"once_cell",
|
||||
"petgraph",
|
||||
"rand",
|
||||
"redis",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"rhai",
|
||||
"sea-orm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"serde_yaml",
|
||||
"sha2",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
@ -3424,6 +3869,12 @@ version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
@ -3558,6 +4009,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||
dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
@ -3605,6 +4065,19 @@ dependencies = [
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.100"
|
||||
@ -3637,6 +4110,22 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.25.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.11"
|
||||
@ -3986,13 +4475,23 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
name = "udmin"
|
||||
version = "0.1.0"
|
||||
edition = "2024" # ✅ 升级到最新 Rust Edition
|
||||
default-run = "udmin"
|
||||
|
||||
[dependencies]
|
||||
axum = "0.8.4"
|
||||
@ -30,6 +31,14 @@ utoipa-swagger-ui = { version = "6.0.0", features = ["axum"] }
|
||||
sha2 = "0.10"
|
||||
rand = "0.8"
|
||||
async-trait = "0.1"
|
||||
redis = { version = "0.27", features = ["tokio-comp", "connection-manager"] }
|
||||
|
||||
# 流程管理相关依赖
|
||||
petgraph = "0.6"
|
||||
rhai = { version = "1.17", features = ["serde", "metadata", "internals"] }
|
||||
serde_yaml = "0.9"
|
||||
regex = "1.10"
|
||||
reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features = false }
|
||||
|
||||
[dependencies.migration]
|
||||
path = "migration"
|
||||
|
||||
78
backend/REDIS_INTEGRATION.md
Normal file
78
backend/REDIS_INTEGRATION.md
Normal file
@ -0,0 +1,78 @@
|
||||
# Redis集成测试
|
||||
|
||||
这个文件包含了Redis集成的测试说明。
|
||||
|
||||
## 环境配置
|
||||
|
||||
1. 复制环境配置文件:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. 根据需要修改 `.env` 文件中的Redis配置:
|
||||
```bash
|
||||
REDIS_URL=redis://:123456@127.0.0.1:6379/9
|
||||
```
|
||||
|
||||
## 启动Redis服务
|
||||
|
||||
确保Redis服务器正在运行:
|
||||
```bash
|
||||
# 使用Docker运行Redis
|
||||
docker run -d -p 6379:6379 --name redis-udmin redis:7-alpine --requirepass 123456
|
||||
|
||||
# 或者使用本地Redis服务
|
||||
redis-server --requirepass 123456
|
||||
```
|
||||
|
||||
## 测试Redis连接
|
||||
|
||||
启动服务器:
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
查看日志中是否有Redis连接成功的信息:
|
||||
```
|
||||
[INFO] Connecting to Redis at: redis://:***@127.0.0.1:6379/9
|
||||
[INFO] Redis connection established successfully
|
||||
```
|
||||
|
||||
## 测试Token存储
|
||||
|
||||
1. 登录获取token:
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/api/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"Admin@123"}'
|
||||
```
|
||||
|
||||
2. 检查Redis中的token:
|
||||
```bash
|
||||
# 连接到Redis CLI
|
||||
redis-cli -a 123456 -n 9
|
||||
|
||||
# 查看所有token键
|
||||
KEYS token:*
|
||||
|
||||
# 查看特定用户的token
|
||||
GET token:access:user:1
|
||||
GET token:refresh:user:1
|
||||
```
|
||||
|
||||
## 功能验证
|
||||
|
||||
Redis集成后的新功能:
|
||||
|
||||
1. **Token存储**:所有JWT token都存储在Redis中
|
||||
2. **Token验证**:每次API调用都会验证Redis中的token
|
||||
3. **Token撤销**:logout时会从Redis中删除token
|
||||
4. **性能提升**:减少数据库查询,提升认证性能
|
||||
5. **单点登录**:防止多重登录(可配置)
|
||||
|
||||
## 环境变量说明
|
||||
|
||||
- `REDIS_URL`: Redis连接字符串
|
||||
- `REDIS_TOKEN_VALIDATION`: 是否启用Redis token验证(默认true)
|
||||
- `JWT_ACCESS_EXP_SECS`: 访问token过期时间(秒)
|
||||
- `JWT_REFRESH_EXP_SECS`: 刷新token过期时间(秒)
|
||||
@ -1,4 +1,5 @@
|
||||
mod db;
|
||||
mod redis;
|
||||
mod response;
|
||||
mod error;
|
||||
pub mod middlewares;
|
||||
@ -6,6 +7,7 @@ pub mod models;
|
||||
pub mod services;
|
||||
pub mod routes;
|
||||
pub mod utils;
|
||||
pub mod workflow;
|
||||
|
||||
use axum::Router;
|
||||
use axum::http::{HeaderValue, Method};
|
||||
@ -37,6 +39,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
let db = db::init_db().await?;
|
||||
|
||||
// initialize Redis connection
|
||||
let redis_pool = redis::init_redis().await?;
|
||||
redis::set_redis_pool(redis_pool)?;
|
||||
|
||||
// run migrations
|
||||
migration::Migrator::up(&db, None).await.expect("migration up");
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ use axum::{http::HeaderMap, http::header::AUTHORIZATION};
|
||||
use chrono::{Utc, Duration as ChronoDuration};
|
||||
use jsonwebtoken::{EncodingKey, DecodingKey, Header, Validation};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::error::AppError;
|
||||
use crate::{error::AppError, redis::TokenRedis};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Claims {
|
||||
@ -37,6 +37,21 @@ impl<S> axum::extract::FromRequestParts<S> for AuthUser where S: Send + Sync + '
|
||||
let secret = std::env::var("JWT_SECRET").map_err(|_| AppError::Unauthorized)?;
|
||||
let claims = decode_token(token, &secret)?;
|
||||
if claims.typ != "access" { return Err(AppError::Unauthorized); }
|
||||
|
||||
// 验证token是否在Redis中存在(可选:添加环境变量控制是否启用Redis验证)
|
||||
let redis_validation_enabled = std::env::var("REDIS_TOKEN_VALIDATION")
|
||||
.unwrap_or_else(|_| "true".to_string())
|
||||
.parse::<bool>()
|
||||
.unwrap_or(true);
|
||||
|
||||
if redis_validation_enabled {
|
||||
let is_valid = TokenRedis::validate_access_token(token, claims.uid).await
|
||||
.unwrap_or(false);
|
||||
if !is_valid {
|
||||
return Err(AppError::Unauthorized);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(AuthUser { uid: claims.uid, username: claims.sub })
|
||||
}
|
||||
}
|
||||
|
||||
154
backend/src/redis.rs
Normal file
154
backend/src/redis.rs
Normal file
@ -0,0 +1,154 @@
|
||||
use redis::{Client, AsyncCommands};
|
||||
use redis::aio::ConnectionManager;
|
||||
use once_cell::sync::OnceCell;
|
||||
use crate::error::AppError;
|
||||
|
||||
pub type RedisPool = ConnectionManager;
|
||||
|
||||
static REDIS_POOL: OnceCell<RedisPool> = OnceCell::new();
|
||||
|
||||
/// 初始化Redis连接池
|
||||
pub async fn init_redis() -> Result<RedisPool, AppError> {
|
||||
let redis_url = std::env::var("REDIS_URL")
|
||||
.unwrap_or_else(|_| "redis://:123456@127.0.0.1:6379/9".into());
|
||||
|
||||
tracing::info!("Connecting to Redis at: {}", redis_url.replace(":123456", ":***"));
|
||||
|
||||
let client = Client::open(redis_url)
|
||||
.map_err(|e| AppError::Anyhow(anyhow::anyhow!("Failed to create Redis client: {}", e)))?;
|
||||
|
||||
let manager = ConnectionManager::new(client).await
|
||||
.map_err(|e| AppError::Anyhow(anyhow::anyhow!("Failed to create Redis connection manager: {}", e)))?;
|
||||
|
||||
tracing::info!("Redis connection established successfully");
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
/// 获取Redis连接池
|
||||
pub fn get_redis() -> Result<&'static RedisPool, AppError> {
|
||||
REDIS_POOL.get().ok_or_else(|| AppError::Anyhow(anyhow::anyhow!("Redis pool not initialized")))
|
||||
}
|
||||
|
||||
/// 设置Redis连接池
|
||||
pub fn set_redis_pool(pool: RedisPool) -> Result<(), AppError> {
|
||||
REDIS_POOL.set(pool)
|
||||
.map_err(|_| AppError::Anyhow(anyhow::anyhow!("Redis pool already initialized")))
|
||||
}
|
||||
|
||||
/// Redis工具函数
|
||||
pub struct RedisHelper;
|
||||
|
||||
impl RedisHelper {
|
||||
/// 设置带过期时间的键值对
|
||||
pub async fn set_ex(key: &str, value: &str, expire_seconds: u64) -> Result<(), AppError> {
|
||||
let mut conn = get_redis()?.clone();
|
||||
tracing::debug!("Redis SET_EX: key={}, value_len={}, expire_seconds={}", key, value.len(), expire_seconds);
|
||||
let _: String = conn.set_ex(key, value, expire_seconds).await
|
||||
.map_err(|e| {
|
||||
tracing::error!("Redis set_ex failed for key {}: {}", key, e);
|
||||
AppError::Anyhow(anyhow::anyhow!("Redis set_ex failed: {}", e))
|
||||
})?;
|
||||
tracing::debug!("Redis SET_EX successful for key: {}", key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 获取键值
|
||||
pub async fn get(key: &str) -> Result<Option<String>, AppError> {
|
||||
let mut conn = get_redis()?.clone();
|
||||
let result: Option<String> = conn.get(key).await
|
||||
.map_err(|e| AppError::Anyhow(anyhow::anyhow!("Redis get failed: {}", e)))?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 删除键
|
||||
pub async fn del(key: &str) -> Result<(), AppError> {
|
||||
let mut conn = get_redis()?.clone();
|
||||
let _: i32 = conn.del(key).await
|
||||
.map_err(|e| AppError::Anyhow(anyhow::anyhow!("Redis del failed: {}", e)))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 检查键是否存在
|
||||
pub async fn exists(key: &str) -> Result<bool, AppError> {
|
||||
let mut conn = get_redis()?.clone();
|
||||
let result: bool = conn.exists(key).await
|
||||
.map_err(|e| AppError::Anyhow(anyhow::anyhow!("Redis exists failed: {}", e)))?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 设置键的过期时间
|
||||
pub async fn expire(key: &str, seconds: u64) -> Result<(), AppError> {
|
||||
let mut conn = get_redis()?.clone();
|
||||
let _: bool = conn.expire(key, seconds as i64).await
|
||||
.map_err(|e| AppError::Anyhow(anyhow::anyhow!("Redis expire failed: {}", e)))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 删除用户相关的所有token
|
||||
pub async fn del_user_tokens(user_id: i64) -> Result<(), AppError> {
|
||||
let pattern = format!("token:*:user:{}", user_id);
|
||||
let mut conn = get_redis()?.clone();
|
||||
|
||||
// 获取匹配的键
|
||||
let keys: Vec<String> = conn.keys(&pattern).await
|
||||
.map_err(|e| AppError::Anyhow(anyhow::anyhow!("Redis keys failed: {}", e)))?;
|
||||
|
||||
// 删除所有匹配的键
|
||||
if !keys.is_empty() {
|
||||
let _: i32 = conn.del(&keys).await
|
||||
.map_err(|e| AppError::Anyhow(anyhow::anyhow!("Redis del multiple failed: {}", e)))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Token相关的Redis操作
|
||||
pub struct TokenRedis;
|
||||
|
||||
impl TokenRedis {
|
||||
/// 存储访问token
|
||||
pub async fn store_access_token(token: &str, user_id: i64, expire_seconds: u64) -> Result<(), AppError> {
|
||||
let key = format!("token:access:user:{}", user_id);
|
||||
tracing::info!("Storing access token for user {} with key: {}, expires in {} seconds", user_id, key, expire_seconds);
|
||||
RedisHelper::set_ex(&key, token, expire_seconds).await
|
||||
}
|
||||
|
||||
/// 存储刷新token
|
||||
pub async fn store_refresh_token(token: &str, user_id: i64, expire_seconds: u64) -> Result<(), AppError> {
|
||||
let key = format!("token:refresh:user:{}", user_id);
|
||||
tracing::info!("Storing refresh token for user {} with key: {}, expires in {} seconds", user_id, key, expire_seconds);
|
||||
RedisHelper::set_ex(&key, token, expire_seconds).await
|
||||
}
|
||||
|
||||
/// 验证访问token
|
||||
pub async fn validate_access_token(token: &str, user_id: i64) -> Result<bool, AppError> {
|
||||
let key = format!("token:access:user:{}", user_id);
|
||||
let stored_token = RedisHelper::get(&key).await?;
|
||||
Ok(stored_token.as_deref() == Some(token))
|
||||
}
|
||||
|
||||
/// 验证刷新token
|
||||
pub async fn validate_refresh_token(token: &str, user_id: i64) -> Result<bool, AppError> {
|
||||
let key = format!("token:refresh:user:{}", user_id);
|
||||
let stored_token = RedisHelper::get(&key).await?;
|
||||
Ok(stored_token.as_deref() == Some(token))
|
||||
}
|
||||
|
||||
/// 删除用户的访问token
|
||||
pub async fn revoke_access_token(user_id: i64) -> Result<(), AppError> {
|
||||
let key = format!("token:access:user:{}", user_id);
|
||||
RedisHelper::del(&key).await
|
||||
}
|
||||
|
||||
/// 删除用户的刷新token
|
||||
pub async fn revoke_refresh_token(user_id: i64) -> Result<(), AppError> {
|
||||
let key = format!("token:refresh:user:{}", user_id);
|
||||
RedisHelper::del(&key).await
|
||||
}
|
||||
|
||||
/// 删除用户的所有token
|
||||
pub async fn revoke_all_tokens(user_id: i64) -> Result<(), AppError> {
|
||||
RedisHelper::del_user_tokens(user_id).await
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
use sea_orm::{EntityTrait, ColumnTrait, QueryFilter, ActiveModelTrait, Set};
|
||||
use crate::{db::Db, models::{user, refresh_token}, utils::password, error::AppError};
|
||||
use crate::{db::Db, models::{user, refresh_token}, utils::password, error::AppError, redis::TokenRedis};
|
||||
use chrono::{Utc, Duration, FixedOffset};
|
||||
use sha2::{Sha256, Digest};
|
||||
use sea_orm::ActiveValue::NotSet;
|
||||
@ -11,13 +11,38 @@ pub async fn login(db: &Db, username: String, password_plain: String) -> Result<
|
||||
if u.status != 1 { return Err(AppError::Forbidden); }
|
||||
let ok = password::verify_password(&password_plain, &u.password_hash).map_err(|_| AppError::Unauthorized)?;
|
||||
if !ok { return Err(AppError::Unauthorized); }
|
||||
|
||||
let access_claims = crate::middlewares::jwt::new_access_claims(u.id, &u.username);
|
||||
let refresh_claims = crate::middlewares::jwt::new_refresh_claims(u.id, &u.username);
|
||||
let secret = std::env::var("JWT_SECRET").unwrap();
|
||||
let access = crate::middlewares::jwt::encode_token(&access_claims, &secret)?;
|
||||
let refresh = crate::middlewares::jwt::encode_token(&refresh_claims, &secret)?;
|
||||
|
||||
// persist refresh token hash
|
||||
// 获取过期时间(秒)
|
||||
let access_exp_secs = std::env::var("JWT_ACCESS_EXP_SECS").ok().and_then(|v| v.parse().ok()).unwrap_or(1800);
|
||||
let refresh_exp_secs = std::env::var("JWT_REFRESH_EXP_SECS").ok().and_then(|v| v.parse().ok()).unwrap_or(1209600);
|
||||
|
||||
// 先删除用户的所有旧token(防止多重登录)
|
||||
if let Err(e) = TokenRedis::revoke_all_tokens(u.id).await {
|
||||
tracing::warn!("Failed to revoke old tokens for user {}: {}", u.id, e);
|
||||
}
|
||||
|
||||
// 存储新token到Redis
|
||||
if let Err(e) = TokenRedis::store_access_token(&access, u.id, access_exp_secs as u64).await {
|
||||
tracing::error!("Failed to store access token to Redis for user {}: {}", u.id, e);
|
||||
// 不返回错误,降级到仅使用JWT模式
|
||||
} else {
|
||||
tracing::info!("Successfully stored access token to Redis for user {}", u.id);
|
||||
}
|
||||
|
||||
if let Err(e) = TokenRedis::store_refresh_token(&refresh, u.id, refresh_exp_secs as u64).await {
|
||||
tracing::error!("Failed to store refresh token to Redis for user {}: {}", u.id, e);
|
||||
// 不返回错误,降级到仅使用JWT模式
|
||||
} else {
|
||||
tracing::info!("Successfully stored refresh token to Redis for user {}", u.id);
|
||||
}
|
||||
|
||||
// persist refresh token hash to database (backup)
|
||||
let mut hasher = Sha256::new(); hasher.update(refresh.as_bytes());
|
||||
let token_hash = format!("{:x}", hasher.finalize());
|
||||
let exp_secs = std::env::var("JWT_REFRESH_EXP_SECS").ok().and_then(|v| v.parse().ok()).unwrap_or(1209600);
|
||||
@ -36,15 +61,26 @@ pub async fn login(db: &Db, username: String, password_plain: String) -> Result<
|
||||
}
|
||||
|
||||
pub async fn logout(db: &Db, uid: i64) -> Result<(), AppError> {
|
||||
// 从 Redis 中删除所有 token
|
||||
let _ = TokenRedis::revoke_all_tokens(uid).await;
|
||||
|
||||
// 从数据库中删除 refresh token
|
||||
let _ = refresh_token::Entity::delete_many().filter(refresh_token::Column::UserId.eq(uid)).exec(db).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn rotate_refresh(db: &Db, uid: i64, old_refresh: String) -> Result<(String, String), AppError> {
|
||||
// 验证Redis中的refresh token
|
||||
let is_valid_redis = TokenRedis::validate_refresh_token(&old_refresh, uid).await.unwrap_or(false);
|
||||
|
||||
// 同时验证数据库中的token hash(备用验证)
|
||||
let mut hasher = Sha256::new(); hasher.update(old_refresh.as_bytes());
|
||||
let token_hash = format!("{:x}", hasher.finalize());
|
||||
let existing = refresh_token::Entity::find().filter(refresh_token::Column::UserId.eq(uid)).filter(refresh_token::Column::TokenHash.eq(token_hash.clone())).one(db).await?;
|
||||
if existing.is_none() { return Err(AppError::Unauthorized); }
|
||||
|
||||
if !is_valid_redis && existing.is_none() {
|
||||
return Err(AppError::Unauthorized);
|
||||
}
|
||||
|
||||
let u = user::Entity::find_by_id(uid).one(db).await?.ok_or(AppError::Unauthorized)?;
|
||||
let access_claims = crate::middlewares::jwt::new_access_claims(u.id, &u.username);
|
||||
@ -53,6 +89,15 @@ pub async fn rotate_refresh(db: &Db, uid: i64, old_refresh: String) -> Result<(S
|
||||
let access = crate::middlewares::jwt::encode_token(&access_claims, &secret)?;
|
||||
let refresh = crate::middlewares::jwt::encode_token(&refresh_claims, &secret)?;
|
||||
|
||||
// 获取过期时间
|
||||
let access_exp_secs = std::env::var("JWT_ACCESS_EXP_SECS").ok().and_then(|v| v.parse().ok()).unwrap_or(1800);
|
||||
let refresh_exp_secs = std::env::var("JWT_REFRESH_EXP_SECS").ok().and_then(|v| v.parse().ok()).unwrap_or(1209600);
|
||||
|
||||
// 更新Redis中的token
|
||||
TokenRedis::store_access_token(&access, u.id, access_exp_secs as u64).await?;
|
||||
TokenRedis::store_refresh_token(&refresh, u.id, refresh_exp_secs as u64).await?;
|
||||
|
||||
// 更新数据库中的refresh token
|
||||
let _ = refresh_token::Entity::delete_many().filter(refresh_token::Column::UserId.eq(uid)).filter(refresh_token::Column::TokenHash.eq(token_hash)).exec(db).await?;
|
||||
let mut hasher2 = Sha256::new(); hasher2.update(refresh.as_bytes());
|
||||
let token_hash2 = format!("{:x}", hasher2.finalize());
|
||||
|
||||
1
backend/temp_hash.rs
Normal file
1
backend/temp_hash.rs
Normal file
@ -0,0 +1 @@
|
||||
use argon2::{Argon2, PasswordHasher}; use argon2::password_hash::{SaltString, rand_core::OsRng}; fn main() { let argon2 = Argon2::default(); let salt = SaltString::generate(&mut OsRng); let password_hash = argon2.hash_password(b"123456", &salt).unwrap().to_string(); println!("{}", password_hash); }
|
||||
Reference in New Issue
Block a user