[{"data":1,"prerenderedAt":4093},["ShallowReactive",2],{"content:\u002F2025\u002F08\u002Fgo-jwt-redis-demo":3,"surround:\u002F2025\u002F08\u002Fgo-jwt-redis-demo":4082},{"id":4,"title":5,"body":6,"categories":4061,"date":4063,"description":4064,"draft":4065,"extension":4066,"image":4067,"meta":4068,"navigation":84,"path":4070,"permalink":4067,"published":4067,"readingTime":4071,"recommend":4067,"references":4067,"seo":4076,"sitemap":4077,"stem":4078,"tags":4079,"type":4080,"updated":4063,"__hash__":4081},"content\u002Fposts\u002F2025\u002F08\u002Fgo-jwt-redis-demo.md","go jwt和redis实现用户登录注册以及查询",{"type":7,"value":8,"toc":4049},"minimark",[9,13,16,28,32,48,51,56,691,695,704,938,941,953,1212,1216,1232,1576,1854,1858,1871,2452,2455,2460,2647,2651,2659,3204,3208,3229,3866,3869,3874,4042,4045],[10,11,12],"p",{},"今天来使用go中的jwt来完成用户的登录注册，并使用redis缓存用户信息的功能",[10,14,15],{},"项目地址：",[17,18,19],"ul",{},[20,21,22],"li",{},[23,24,25],"a",{"href":25,"rel":26},"https:\u002F\u002Fgithub.com\u002Fcodepzj\u002Fgo-jwt-redis-demo.git",[27],"nofollow",[29,30,31],"h2",{"id":31},"背景与目标",[17,33,34],{},[20,35,36,37],{},"本项目用 Gin + GORM + MySQL + Redis 实现一套“JWT 认证 + 用户资料缓存”的最小闭环：\n",[17,38,39,42,45],{},[20,40,41],{},"注册\u002F登录获取 JWT",[20,43,44],{},"中间件校验 JWT 保护接口",[20,46,47],{},"访问资料时优先读 Redis 缓存，未命中则查询数据库 并回填缓存",[29,49,50],{"id":50},"配置加载",[17,52,53],{},[20,54,55],{},"通过环境变量覆盖默认值（JWT 密钥、MySQL、Redis 等）：",[57,58,63],"pre",{"className":59,"code":60,"language":61,"meta":62,"style":62},"language-go shiki shiki-themes github-light github-dark","package config\n\nimport (\n\t\"os\"\n)\n\ntype Config struct {\n\tJWTSecret    string\n\tDBHost       string\n\tDBPort       string\n\tDBUser       string\n\tDBPassword   string\n\tDBName       string\n\tRedisHost    string\n\tRedisPort    string\n\tRedisPassword string\n\tRedisDB      int\n}\n\nvar AppConfig Config\n\nfunc InitConfig() {\n\tAppConfig = Config{\n\t\tJWTSecret:     getEnv(\"JWT_SECRET\", \"codepzj\"),\n\t\tDBHost:        getEnv(\"DB_HOST\", \"localhost\"),\n\t\tDBPort:        getEnv(\"DB_PORT\", \"3306\"),\n\t\tDBUser:        getEnv(\"DB_USER\", \"root\"),\n\t\tDBPassword:    getEnv(\"DB_PASSWORD\", \"123456\"),\n\t\tDBName:        getEnv(\"DB_NAME\", \"mytest2\"),\n\t\tRedisHost:     getEnv(\"REDIS_HOST\", \"localhost\"),\n\t\tRedisPort:     getEnv(\"REDIS_PORT\", \"6379\"),\n\t\tRedisPassword: getEnv(\"REDIS_PASSWORD\", \"\"),\n\t\tRedisDB:       0,\n\t}\n}\n\nfunc getEnv(key, defaultValue string) string {\n\tif value := os.Getenv(key); value != \"\" {\n\t\treturn value\n\t}\n\treturn defaultValue\n}\n","go","",[64,65,66,79,86,100,117,123,128,145,156,166,176,186,196,206,216,226,236,247,253,258,270,275,294,310,340,366,392,418,444,470,495,521,547,563,572,577,582,615,654,667,676,686],"code",{"__ignoreMap":62},[67,68,71,75],"span",{"class":69,"data-line":70,"line":70},"line",1,[67,72,74],{"class":73},"szBVR","package",[67,76,78],{"class":77},"sScJk"," config\n",[67,80,82],{"class":69,"data-line":81,"line":81},2,[67,83,85],{"emptyLinePlaceholder":84},true,"\n",[67,87,89,92,96],{"class":69,"data-line":88,"line":88},3,[67,90,91],{"class":73},"import",[67,93,95],{"class":94},"sVt8B"," ",[67,97,99],{"class":98},"sSDpw","(\n",[67,101,103,107,111,114],{"class":69,"data-line":102,"line":102},4,[67,104,106],{"class":105},"indent","\t",[67,108,110],{"class":109},"sZZnC","\"",[67,112,113],{"class":77},"os",[67,115,116],{"class":109},"\"\n",[67,118,120],{"class":69,"data-line":119,"line":119},5,[67,121,122],{"class":98},")\n",[67,124,126],{"class":69,"data-line":125,"line":125},6,[67,127,85],{"emptyLinePlaceholder":84},[67,129,131,134,137,140,142],{"class":69,"data-line":130,"line":130},7,[67,132,133],{"class":73},"type",[67,135,136],{"class":77}," Config",[67,138,139],{"class":73}," struct",[67,141,95],{"class":94},[67,143,144],{"class":98},"{\n",[67,146,148,150,153],{"class":69,"data-line":147,"line":147},8,[67,149,106],{"class":105},[67,151,152],{"class":94},"JWTSecret    ",[67,154,155],{"class":73},"string\n",[67,157,159,161,164],{"class":69,"data-line":158,"line":158},9,[67,160,106],{"class":105},[67,162,163],{"class":94},"DBHost       ",[67,165,155],{"class":73},[67,167,169,171,174],{"class":69,"data-line":168,"line":168},10,[67,170,106],{"class":105},[67,172,173],{"class":94},"DBPort       ",[67,175,155],{"class":73},[67,177,179,181,184],{"class":69,"data-line":178,"line":178},11,[67,180,106],{"class":105},[67,182,183],{"class":94},"DBUser       ",[67,185,155],{"class":73},[67,187,189,191,194],{"class":69,"data-line":188,"line":188},12,[67,190,106],{"class":105},[67,192,193],{"class":94},"DBPassword   ",[67,195,155],{"class":73},[67,197,199,201,204],{"class":69,"data-line":198,"line":198},13,[67,200,106],{"class":105},[67,202,203],{"class":94},"DBName       ",[67,205,155],{"class":73},[67,207,209,211,214],{"class":69,"data-line":208,"line":208},14,[67,210,106],{"class":105},[67,212,213],{"class":94},"RedisHost    ",[67,215,155],{"class":73},[67,217,219,221,224],{"class":69,"data-line":218,"line":218},15,[67,220,106],{"class":105},[67,222,223],{"class":94},"RedisPort    ",[67,225,155],{"class":73},[67,227,229,231,234],{"class":69,"data-line":228,"line":228},16,[67,230,106],{"class":105},[67,232,233],{"class":94},"RedisPassword ",[67,235,155],{"class":73},[67,237,239,241,244],{"class":69,"data-line":238,"line":238},17,[67,240,106],{"class":105},[67,242,243],{"class":94},"RedisDB      ",[67,245,246],{"class":73},"int\n",[67,248,250],{"class":69,"data-line":249,"line":249},18,[67,251,252],{"class":98},"}\n",[67,254,256],{"class":69,"data-line":255,"line":255},19,[67,257,85],{"emptyLinePlaceholder":84},[67,259,261,264,267],{"class":69,"data-line":260,"line":260},20,[67,262,263],{"class":73},"var",[67,265,266],{"class":94}," AppConfig ",[67,268,269],{"class":77},"Config\n",[67,271,273],{"class":69,"data-line":272,"line":272},21,[67,274,85],{"emptyLinePlaceholder":84},[67,276,278,281,284,287,290,292],{"class":69,"data-line":277,"line":277},22,[67,279,280],{"class":73},"func",[67,282,283],{"class":77}," InitConfig",[67,285,286],{"class":98},"(",[67,288,289],{"class":98},")",[67,291,95],{"class":94},[67,293,144],{"class":98},[67,295,297,299,302,305,307],{"class":69,"data-line":296,"line":296},23,[67,298,106],{"class":105},[67,300,301],{"class":94},"AppConfig ",[67,303,304],{"class":73},"=",[67,306,136],{"class":77},[67,308,144],{"class":309},"sJX2D",[67,311,313,315,317,320,323,326,329,332,335,337],{"class":69,"data-line":312,"line":312},24,[67,314,106],{"class":105},[67,316,106],{"class":105},[67,318,319],{"class":94},"JWTSecret:     ",[67,321,322],{"class":77},"getEnv",[67,324,286],{"class":325},"sqkie",[67,327,328],{"class":109},"\"JWT_SECRET\"",[67,330,331],{"class":94},", ",[67,333,334],{"class":109},"\"codepzj\"",[67,336,289],{"class":325},[67,338,339],{"class":94},",\n",[67,341,343,345,347,350,352,354,357,359,362,364],{"class":69,"data-line":342,"line":342},25,[67,344,106],{"class":105},[67,346,106],{"class":105},[67,348,349],{"class":94},"DBHost:        ",[67,351,322],{"class":77},[67,353,286],{"class":325},[67,355,356],{"class":109},"\"DB_HOST\"",[67,358,331],{"class":94},[67,360,361],{"class":109},"\"localhost\"",[67,363,289],{"class":325},[67,365,339],{"class":94},[67,367,369,371,373,376,378,380,383,385,388,390],{"class":69,"data-line":368,"line":368},26,[67,370,106],{"class":105},[67,372,106],{"class":105},[67,374,375],{"class":94},"DBPort:        ",[67,377,322],{"class":77},[67,379,286],{"class":325},[67,381,382],{"class":109},"\"DB_PORT\"",[67,384,331],{"class":94},[67,386,387],{"class":109},"\"3306\"",[67,389,289],{"class":325},[67,391,339],{"class":94},[67,393,395,397,399,402,404,406,409,411,414,416],{"class":69,"data-line":394,"line":394},27,[67,396,106],{"class":105},[67,398,106],{"class":105},[67,400,401],{"class":94},"DBUser:        ",[67,403,322],{"class":77},[67,405,286],{"class":325},[67,407,408],{"class":109},"\"DB_USER\"",[67,410,331],{"class":94},[67,412,413],{"class":109},"\"root\"",[67,415,289],{"class":325},[67,417,339],{"class":94},[67,419,421,423,425,428,430,432,435,437,440,442],{"class":69,"data-line":420,"line":420},28,[67,422,106],{"class":105},[67,424,106],{"class":105},[67,426,427],{"class":94},"DBPassword:    ",[67,429,322],{"class":77},[67,431,286],{"class":325},[67,433,434],{"class":109},"\"DB_PASSWORD\"",[67,436,331],{"class":94},[67,438,439],{"class":109},"\"123456\"",[67,441,289],{"class":325},[67,443,339],{"class":94},[67,445,447,449,451,454,456,458,461,463,466,468],{"class":69,"data-line":446,"line":446},29,[67,448,106],{"class":105},[67,450,106],{"class":105},[67,452,453],{"class":94},"DBName:        ",[67,455,322],{"class":77},[67,457,286],{"class":325},[67,459,460],{"class":109},"\"DB_NAME\"",[67,462,331],{"class":94},[67,464,465],{"class":109},"\"mytest2\"",[67,467,289],{"class":325},[67,469,339],{"class":94},[67,471,473,475,477,480,482,484,487,489,491,493],{"class":69,"data-line":472,"line":472},30,[67,474,106],{"class":105},[67,476,106],{"class":105},[67,478,479],{"class":94},"RedisHost:     ",[67,481,322],{"class":77},[67,483,286],{"class":325},[67,485,486],{"class":109},"\"REDIS_HOST\"",[67,488,331],{"class":94},[67,490,361],{"class":109},[67,492,289],{"class":325},[67,494,339],{"class":94},[67,496,498,500,502,505,507,509,512,514,517,519],{"class":69,"data-line":497,"line":497},31,[67,499,106],{"class":105},[67,501,106],{"class":105},[67,503,504],{"class":94},"RedisPort:     ",[67,506,322],{"class":77},[67,508,286],{"class":325},[67,510,511],{"class":109},"\"REDIS_PORT\"",[67,513,331],{"class":94},[67,515,516],{"class":109},"\"6379\"",[67,518,289],{"class":325},[67,520,339],{"class":94},[67,522,524,526,528,531,533,535,538,540,543,545],{"class":69,"data-line":523,"line":523},32,[67,525,106],{"class":105},[67,527,106],{"class":105},[67,529,530],{"class":94},"RedisPassword: ",[67,532,322],{"class":77},[67,534,286],{"class":325},[67,536,537],{"class":109},"\"REDIS_PASSWORD\"",[67,539,331],{"class":94},[67,541,542],{"class":109},"\"\"",[67,544,289],{"class":325},[67,546,339],{"class":94},[67,548,550,552,554,557,561],{"class":69,"data-line":549,"line":549},33,[67,551,106],{"class":105},[67,553,106],{"class":105},[67,555,556],{"class":94},"RedisDB:       ",[67,558,560],{"class":559},"sj4cs","0",[67,562,339],{"class":94},[67,564,566,568,570],{"class":69,"data-line":565,"line":565},34,[67,567,106],{"class":105},[67,569,62],{"class":94},[67,571,252],{"class":309},[67,573,575],{"class":69,"data-line":574,"line":574},35,[67,576,252],{"class":98},[67,578,580],{"class":69,"data-line":579,"line":579},36,[67,581,85],{"emptyLinePlaceholder":84},[67,583,585,587,590,592,596,598,601,604,606,608,611,613],{"class":69,"data-line":584,"line":584},37,[67,586,280],{"class":73},[67,588,589],{"class":77}," getEnv",[67,591,286],{"class":98},[67,593,595],{"class":594},"s4XuR","key",[67,597,331],{"class":94},[67,599,600],{"class":594},"defaultValue",[67,602,603],{"class":73}," string",[67,605,289],{"class":98},[67,607,95],{"class":94},[67,609,610],{"class":73},"string",[67,612,95],{"class":94},[67,614,144],{"class":98},[67,616,618,620,623,626,629,632,635,637,639,641,644,647,650,652],{"class":69,"data-line":617,"line":617},38,[67,619,106],{"class":105},[67,621,622],{"class":73},"if",[67,624,625],{"class":94}," value ",[67,627,628],{"class":73},":=",[67,630,631],{"class":94}," os.",[67,633,634],{"class":77},"Getenv",[67,636,286],{"class":309},[67,638,595],{"class":94},[67,640,289],{"class":309},[67,642,643],{"class":94},"; value ",[67,645,646],{"class":73},"!=",[67,648,649],{"class":109}," \"\"",[67,651,95],{"class":94},[67,653,144],{"class":309},[67,655,657,659,661,664],{"class":69,"data-line":656,"line":656},39,[67,658,106],{"class":105},[67,660,106],{"class":105},[67,662,663],{"class":73},"return",[67,665,666],{"class":94}," value\n",[67,668,670,672,674],{"class":69,"data-line":669,"line":669},40,[67,671,106],{"class":105},[67,673,62],{"class":94},[67,675,252],{"class":309},[67,677,679,681,683],{"class":69,"data-line":678,"line":678},41,[67,680,106],{"class":105},[67,682,663],{"class":73},[67,684,685],{"class":94}," defaultValue\n",[67,687,689],{"class":69,"data-line":688,"line":688},42,[67,690,252],{"class":98},[29,692,694],{"id":693},"数据库与-redis-初始化","数据库与 Redis 初始化",[17,696,697],{},[20,698,699,700,703],{},"初始化 MySQL 连接并记录日志；初始化 Redis 客户端并 ",[64,701,702],{"code":702},"Ping"," 测试连接。",[57,705,707],{"className":59,"code":706,"language":61,"meta":62,"style":62},"func InitRedis() {\n\tRedisClient = redis.NewClient(&redis.Options{\n\t\tAddr:     fmt.Sprintf(\"%s:%s\", config.AppConfig.RedisHost, config.AppConfig.RedisPort),\n\t\tPassword: config.AppConfig.RedisPassword,\n\t\tDB:       config.AppConfig.RedisDB,\n\t})\n\n\t\u002F\u002F 测试Redis连接\n\t_, err := RedisClient.Ping(context.Background()).Result()\n\tif err != nil {\n\t\tlog.Fatal(\"Redis连接失败:\", err)\n\t}\n\n\tlog.Println(\"Redis连接成功\")\n}\n",[64,708,709,724,755,788,797,806,817,821,829,866,884,906,914,918,934],{"__ignoreMap":62},[67,710,711,713,716,718,720,722],{"class":69,"data-line":70,"line":70},[67,712,280],{"class":73},[67,714,715],{"class":77}," InitRedis",[67,717,286],{"class":98},[67,719,289],{"class":98},[67,721,95],{"class":94},[67,723,144],{"class":98},[67,725,726,728,731,733,736,739,741,744,747,750,753],{"class":69,"data-line":81,"line":81},[67,727,106],{"class":105},[67,729,730],{"class":94},"RedisClient ",[67,732,304],{"class":73},[67,734,735],{"class":94}," redis.",[67,737,738],{"class":77},"NewClient",[67,740,286],{"class":309},[67,742,743],{"class":73},"&",[67,745,746],{"class":77},"redis",[67,748,749],{"class":94},".",[67,751,752],{"class":77},"Options",[67,754,144],{"class":325},[67,756,757,759,761,764,767,769,771,774,777,779,781,784,786],{"class":69,"data-line":88,"line":88},[67,758,106],{"class":105},[67,760,106],{"class":105},[67,762,763],{"class":94},"Addr:     fmt.",[67,765,766],{"class":77},"Sprintf",[67,768,286],{"class":98},[67,770,110],{"class":109},[67,772,773],{"class":559},"%s",[67,775,776],{"class":109},":",[67,778,773],{"class":559},[67,780,110],{"class":109},[67,782,783],{"class":94},", config.AppConfig.RedisHost, config.AppConfig.RedisPort",[67,785,289],{"class":98},[67,787,339],{"class":94},[67,789,790,792,794],{"class":69,"data-line":102,"line":102},[67,791,106],{"class":105},[67,793,106],{"class":105},[67,795,796],{"class":94},"Password: config.AppConfig.RedisPassword,\n",[67,798,799,801,803],{"class":69,"data-line":119,"line":119},[67,800,106],{"class":105},[67,802,106],{"class":105},[67,804,805],{"class":94},"DB:       config.AppConfig.RedisDB,\n",[67,807,808,810,812,815],{"class":69,"data-line":125,"line":125},[67,809,106],{"class":105},[67,811,62],{"class":94},[67,813,814],{"class":325},"}",[67,816,122],{"class":309},[67,818,819],{"class":69,"data-line":130,"line":130},[67,820,85],{"emptyLinePlaceholder":84},[67,822,823,825],{"class":69,"data-line":147,"line":147},[67,824,106],{"class":105},[67,826,828],{"class":827},"sJ8bj","\u002F\u002F 测试Redis连接\n",[67,830,831,833,836,838,841,843,845,848,851,853,855,857,859,862,864],{"class":69,"data-line":158,"line":158},[67,832,106],{"class":105},[67,834,835],{"class":94},"_, err ",[67,837,628],{"class":73},[67,839,840],{"class":94}," RedisClient.",[67,842,702],{"class":77},[67,844,286],{"class":309},[67,846,847],{"class":94},"context.",[67,849,850],{"class":77},"Background",[67,852,286],{"class":325},[67,854,289],{"class":325},[67,856,289],{"class":309},[67,858,749],{"class":94},[67,860,861],{"class":77},"Result",[67,863,286],{"class":309},[67,865,122],{"class":309},[67,867,868,870,872,875,877,880,882],{"class":69,"data-line":168,"line":168},[67,869,106],{"class":105},[67,871,622],{"class":73},[67,873,874],{"class":94}," err ",[67,876,646],{"class":73},[67,878,879],{"class":559}," nil",[67,881,95],{"class":94},[67,883,144],{"class":309},[67,885,886,888,890,893,896,898,901,904],{"class":69,"data-line":178,"line":178},[67,887,106],{"class":105},[67,889,106],{"class":105},[67,891,892],{"class":94},"log.",[67,894,895],{"class":77},"Fatal",[67,897,286],{"class":325},[67,899,900],{"class":109},"\"Redis连接失败:\"",[67,902,903],{"class":94},", err",[67,905,122],{"class":325},[67,907,908,910,912],{"class":69,"data-line":188,"line":188},[67,909,106],{"class":105},[67,911,62],{"class":94},[67,913,252],{"class":309},[67,915,916],{"class":69,"data-line":198,"line":198},[67,917,85],{"emptyLinePlaceholder":84},[67,919,920,922,924,927,929,932],{"class":69,"data-line":208,"line":208},[67,921,106],{"class":105},[67,923,892],{"class":94},[67,925,926],{"class":77},"Println",[67,928,286],{"class":309},[67,930,931],{"class":109},"\"Redis连接成功\"",[67,933,122],{"class":309},[67,935,936],{"class":69,"data-line":218,"line":218},[67,937,252],{"class":98},[29,939,940],{"id":940},"模型与密码加密",[17,942,943],{},[20,944,945,948,949,952],{},[64,946,947],{"code":947},"User"," 模型唯一索引用户名\u002F邮箱，注册时对密码进行 ",[64,950,951],{"code":951},"bcrypt"," 加密；登录比对哈希。",[57,954,956],{"className":59,"code":955,"language":61,"meta":62,"style":62},"\u002F\u002F 密码加密\nfunc (u *User) HashPassword() error {\n\thashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)\n\tif err != nil {\n\t\treturn err\n\t}\n\tu.Password = string(hashedPassword)\n\treturn nil\n}\n\n\u002F\u002F 验证密码\nfunc (u *User) CheckPassword(password string) bool {\n\terr := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))\n\treturn err == nil\n}\n",[64,957,958,963,999,1037,1053,1064,1072,1090,1099,1103,1107,1112,1151,1195,1208],{"__ignoreMap":62},[67,959,960],{"class":69,"data-line":70,"line":70},[67,961,962],{"class":827},"\u002F\u002F 密码加密\n",[67,964,965,967,969,971,974,977,979,981,983,986,988,990,992,995,997],{"class":69,"data-line":81,"line":81},[67,966,280],{"class":73},[67,968,95],{"class":94},[67,970,286],{"class":98},[67,972,973],{"class":594},"u ",[67,975,976],{"class":73},"*",[67,978,947],{"class":77},[67,980,289],{"class":98},[67,982,95],{"class":94},[67,984,985],{"class":77},"HashPassword",[67,987,286],{"class":98},[67,989,289],{"class":98},[67,991,95],{"class":94},[67,993,994],{"class":73},"error",[67,996,95],{"class":94},[67,998,144],{"class":98},[67,1000,1001,1003,1006,1008,1011,1014,1016,1019,1022,1025,1027,1030,1032,1035],{"class":69,"data-line":88,"line":88},[67,1002,106],{"class":105},[67,1004,1005],{"class":94},"hashedPassword, err ",[67,1007,628],{"class":73},[67,1009,1010],{"class":94}," bcrypt.",[67,1012,1013],{"class":77},"GenerateFromPassword",[67,1015,286],{"class":309},[67,1017,1018],{"class":325},"[",[67,1020,1021],{"class":325},"]",[67,1023,1024],{"class":73},"byte",[67,1026,286],{"class":325},[67,1028,1029],{"class":94},"u.Password",[67,1031,289],{"class":325},[67,1033,1034],{"class":94},", bcrypt.DefaultCost",[67,1036,122],{"class":309},[67,1038,1039,1041,1043,1045,1047,1049,1051],{"class":69,"data-line":102,"line":102},[67,1040,106],{"class":105},[67,1042,622],{"class":73},[67,1044,874],{"class":94},[67,1046,646],{"class":73},[67,1048,879],{"class":559},[67,1050,95],{"class":94},[67,1052,144],{"class":309},[67,1054,1055,1057,1059,1061],{"class":69,"data-line":119,"line":119},[67,1056,106],{"class":105},[67,1058,106],{"class":105},[67,1060,663],{"class":73},[67,1062,1063],{"class":94}," err\n",[67,1065,1066,1068,1070],{"class":69,"data-line":125,"line":125},[67,1067,106],{"class":105},[67,1069,62],{"class":94},[67,1071,252],{"class":309},[67,1073,1074,1076,1079,1081,1083,1085,1088],{"class":69,"data-line":130,"line":130},[67,1075,106],{"class":105},[67,1077,1078],{"class":94},"u.Password ",[67,1080,304],{"class":73},[67,1082,603],{"class":73},[67,1084,286],{"class":309},[67,1086,1087],{"class":94},"hashedPassword",[67,1089,122],{"class":309},[67,1091,1092,1094,1096],{"class":69,"data-line":147,"line":147},[67,1093,106],{"class":105},[67,1095,663],{"class":73},[67,1097,1098],{"class":559}," nil\n",[67,1100,1101],{"class":69,"data-line":158,"line":158},[67,1102,252],{"class":98},[67,1104,1105],{"class":69,"data-line":168,"line":168},[67,1106,85],{"emptyLinePlaceholder":84},[67,1108,1109],{"class":69,"data-line":178,"line":178},[67,1110,1111],{"class":827},"\u002F\u002F 验证密码\n",[67,1113,1114,1116,1118,1120,1122,1124,1126,1128,1130,1133,1135,1138,1140,1142,1144,1147,1149],{"class":69,"data-line":188,"line":188},[67,1115,280],{"class":73},[67,1117,95],{"class":94},[67,1119,286],{"class":98},[67,1121,973],{"class":594},[67,1123,976],{"class":73},[67,1125,947],{"class":77},[67,1127,289],{"class":98},[67,1129,95],{"class":94},[67,1131,1132],{"class":77},"CheckPassword",[67,1134,286],{"class":98},[67,1136,1137],{"class":594},"password",[67,1139,603],{"class":73},[67,1141,289],{"class":98},[67,1143,95],{"class":94},[67,1145,1146],{"class":73},"bool",[67,1148,95],{"class":94},[67,1150,144],{"class":98},[67,1152,1153,1155,1158,1160,1162,1165,1167,1169,1171,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193],{"class":69,"data-line":198,"line":198},[67,1154,106],{"class":105},[67,1156,1157],{"class":94},"err ",[67,1159,628],{"class":73},[67,1161,1010],{"class":94},[67,1163,1164],{"class":77},"CompareHashAndPassword",[67,1166,286],{"class":309},[67,1168,1018],{"class":325},[67,1170,1021],{"class":325},[67,1172,1024],{"class":73},[67,1174,286],{"class":325},[67,1176,1029],{"class":94},[67,1178,289],{"class":325},[67,1180,331],{"class":94},[67,1182,1018],{"class":325},[67,1184,1021],{"class":325},[67,1186,1024],{"class":73},[67,1188,286],{"class":325},[67,1190,1137],{"class":94},[67,1192,289],{"class":325},[67,1194,122],{"class":309},[67,1196,1197,1199,1201,1203,1206],{"class":69,"data-line":208,"line":208},[67,1198,106],{"class":105},[67,1200,663],{"class":73},[67,1202,874],{"class":94},[67,1204,1205],{"class":73},"==",[67,1207,1098],{"class":559},[67,1209,1210],{"class":69,"data-line":218,"line":218},[67,1211,252],{"class":98},[29,1213,1215],{"id":1214},"jwt签发与校验","JWT：签发与校验",[17,1217,1218],{},[20,1219,1220,1221,1224,1225,1228,1229,1231],{},"签发时将用户 ",[64,1222,1223],{"code":1223},"ID\u002FUsername"," 写入自定义 ",[64,1226,1227],{"code":1227},"Claims","，过期时间 24 小时，HS256 对称签名；校验时解出 ",[64,1230,1227],{"code":1227},"。",[57,1233,1235],{"className":59,"code":1234,"language":61,"meta":62,"style":62},"type Claims struct {\n\tUserID   uint   `json:\"user_id\"`\n\tUsername string `json:\"username\"`\n\tjwt.RegisteredClaims\n}\t\n\n\u002F\u002F 生成JWT Token\nfunc GenerateToken(userID uint, username string) (string, error) {\n\tclaims := Claims{\n\t\tUserID:   userID,\n\t\tUsername: username,\n\t\tRegisteredClaims: jwt.RegisteredClaims{\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), \u002F\u002F 24小时过期\n\t\t\tIssuedAt:  jwt.NewNumericDate(time.Now()),\n\t\t\tNotBefore: jwt.NewNumericDate(time.Now()),\n\t\t},\n\t}\n\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n\treturn token.SignedString([]byte(config.AppConfig.JWTSecret))\n}\n",[64,1236,1237,1250,1263,1275,1287,1294,1298,1303,1343,1356,1365,1374,1392,1443,1470,1497,1509,1517,1521,1543,1572],{"__ignoreMap":62},[67,1238,1239,1241,1244,1246,1248],{"class":69,"data-line":70,"line":70},[67,1240,133],{"class":73},[67,1242,1243],{"class":77}," Claims",[67,1245,139],{"class":73},[67,1247,95],{"class":94},[67,1249,144],{"class":98},[67,1251,1252,1254,1257,1260],{"class":69,"data-line":81,"line":81},[67,1253,106],{"class":105},[67,1255,1256],{"class":94},"UserID   ",[67,1258,1259],{"class":73},"uint",[67,1261,1262],{"class":109},"   `json:\"user_id\"`\n",[67,1264,1265,1267,1270,1272],{"class":69,"data-line":88,"line":88},[67,1266,106],{"class":105},[67,1268,1269],{"class":94},"Username ",[67,1271,610],{"class":73},[67,1273,1274],{"class":109}," `json:\"username\"`\n",[67,1276,1277,1279,1282,1284],{"class":69,"data-line":102,"line":102},[67,1278,106],{"class":105},[67,1280,1281],{"class":77},"jwt",[67,1283,749],{"class":94},[67,1285,1286],{"class":77},"RegisteredClaims\n",[67,1288,1289,1291],{"class":69,"data-line":119,"line":119},[67,1290,814],{"class":98},[67,1292,1293],{"class":94},"\t\n",[67,1295,1296],{"class":69,"data-line":125,"line":125},[67,1297,85],{"emptyLinePlaceholder":84},[67,1299,1300],{"class":69,"data-line":130,"line":130},[67,1301,1302],{"class":827},"\u002F\u002F 生成JWT Token\n",[67,1304,1305,1307,1310,1312,1315,1318,1320,1323,1325,1327,1329,1331,1333,1335,1337,1339,1341],{"class":69,"data-line":147,"line":147},[67,1306,280],{"class":73},[67,1308,1309],{"class":77}," GenerateToken",[67,1311,286],{"class":98},[67,1313,1314],{"class":594},"userID",[67,1316,1317],{"class":73}," uint",[67,1319,331],{"class":94},[67,1321,1322],{"class":594},"username",[67,1324,603],{"class":73},[67,1326,289],{"class":98},[67,1328,95],{"class":94},[67,1330,286],{"class":98},[67,1332,610],{"class":73},[67,1334,331],{"class":94},[67,1336,994],{"class":73},[67,1338,289],{"class":98},[67,1340,95],{"class":94},[67,1342,144],{"class":98},[67,1344,1345,1347,1350,1352,1354],{"class":69,"data-line":158,"line":158},[67,1346,106],{"class":105},[67,1348,1349],{"class":94},"claims ",[67,1351,628],{"class":73},[67,1353,1243],{"class":77},[67,1355,144],{"class":309},[67,1357,1358,1360,1362],{"class":69,"data-line":168,"line":168},[67,1359,106],{"class":105},[67,1361,106],{"class":105},[67,1363,1364],{"class":94},"UserID:   userID,\n",[67,1366,1367,1369,1371],{"class":69,"data-line":178,"line":178},[67,1368,106],{"class":105},[67,1370,106],{"class":105},[67,1372,1373],{"class":94},"Username: username,\n",[67,1375,1376,1378,1380,1383,1385,1387,1390],{"class":69,"data-line":188,"line":188},[67,1377,106],{"class":105},[67,1379,106],{"class":105},[67,1381,1382],{"class":94},"RegisteredClaims: ",[67,1384,1281],{"class":77},[67,1386,749],{"class":94},[67,1388,1389],{"class":77},"RegisteredClaims",[67,1391,144],{"class":325},[67,1393,1394,1396,1398,1400,1403,1406,1408,1411,1414,1416,1418,1420,1423,1425,1428,1431,1434,1436,1438,1440],{"class":69,"data-line":198,"line":198},[67,1395,106],{"class":105},[67,1397,106],{"class":105},[67,1399,106],{"class":105},[67,1401,1402],{"class":94},"ExpiresAt: jwt.",[67,1404,1405],{"class":77},"NewNumericDate",[67,1407,286],{"class":98},[67,1409,1410],{"class":94},"time.",[67,1412,1413],{"class":77},"Now",[67,1415,286],{"class":309},[67,1417,289],{"class":309},[67,1419,749],{"class":94},[67,1421,1422],{"class":77},"Add",[67,1424,286],{"class":309},[67,1426,1427],{"class":559},"24",[67,1429,1430],{"class":73}," *",[67,1432,1433],{"class":94}," time.Hour",[67,1435,289],{"class":309},[67,1437,289],{"class":98},[67,1439,331],{"class":94},[67,1441,1442],{"class":827},"\u002F\u002F 24小时过期\n",[67,1444,1445,1447,1449,1451,1454,1456,1458,1460,1462,1464,1466,1468],{"class":69,"data-line":208,"line":208},[67,1446,106],{"class":105},[67,1448,106],{"class":105},[67,1450,106],{"class":105},[67,1452,1453],{"class":94},"IssuedAt:  jwt.",[67,1455,1405],{"class":77},[67,1457,286],{"class":98},[67,1459,1410],{"class":94},[67,1461,1413],{"class":77},[67,1463,286],{"class":309},[67,1465,289],{"class":309},[67,1467,289],{"class":98},[67,1469,339],{"class":94},[67,1471,1472,1474,1476,1478,1481,1483,1485,1487,1489,1491,1493,1495],{"class":69,"data-line":218,"line":218},[67,1473,106],{"class":105},[67,1475,106],{"class":105},[67,1477,106],{"class":105},[67,1479,1480],{"class":94},"NotBefore: jwt.",[67,1482,1405],{"class":77},[67,1484,286],{"class":98},[67,1486,1410],{"class":94},[67,1488,1413],{"class":77},[67,1490,286],{"class":309},[67,1492,289],{"class":309},[67,1494,289],{"class":98},[67,1496,339],{"class":94},[67,1498,1499,1501,1503,1505,1507],{"class":69,"data-line":228,"line":228},[67,1500,106],{"class":105},[67,1502,106],{"class":105},[67,1504,62],{"class":94},[67,1506,814],{"class":325},[67,1508,339],{"class":94},[67,1510,1511,1513,1515],{"class":69,"data-line":238,"line":238},[67,1512,106],{"class":105},[67,1514,62],{"class":94},[67,1516,252],{"class":309},[67,1518,1519],{"class":69,"data-line":249,"line":249},[67,1520,85],{"emptyLinePlaceholder":84},[67,1522,1523,1525,1528,1530,1533,1536,1538,1541],{"class":69,"data-line":255,"line":255},[67,1524,106],{"class":105},[67,1526,1527],{"class":94},"token ",[67,1529,628],{"class":73},[67,1531,1532],{"class":94}," jwt.",[67,1534,1535],{"class":77},"NewWithClaims",[67,1537,286],{"class":309},[67,1539,1540],{"class":94},"jwt.SigningMethodHS256, claims",[67,1542,122],{"class":309},[67,1544,1545,1547,1549,1552,1555,1557,1559,1561,1563,1565,1568,1570],{"class":69,"data-line":260,"line":260},[67,1546,106],{"class":105},[67,1548,663],{"class":73},[67,1550,1551],{"class":94}," token.",[67,1553,1554],{"class":77},"SignedString",[67,1556,286],{"class":309},[67,1558,1018],{"class":325},[67,1560,1021],{"class":325},[67,1562,1024],{"class":73},[67,1564,286],{"class":325},[67,1566,1567],{"class":94},"config.AppConfig.JWTSecret",[67,1569,289],{"class":325},[67,1571,122],{"class":309},[67,1573,1574],{"class":69,"data-line":272,"line":272},[67,1575,252],{"class":98},[57,1577,1579],{"className":59,"code":1578,"language":61,"meta":62,"style":62},"\u002F\u002F 验证JWT Token\nfunc ValidateToken(tokenString string) (*Claims, error) {\n\ttoken, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {\n\t\treturn []byte(config.AppConfig.JWTSecret), nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif claims, ok := token.Claims.(*Claims); ok && token.Valid {\n\t\treturn claims, nil\n\t}\n\n\treturn nil, errors.New(\"无效的token\")\n}\n",[64,1580,1581,1586,1620,1689,1716,1726,1730,1746,1759,1767,1771,1804,1817,1825,1829,1850],{"__ignoreMap":62},[67,1582,1583],{"class":69,"data-line":70,"line":70},[67,1584,1585],{"class":827},"\u002F\u002F 验证JWT Token\n",[67,1587,1588,1590,1593,1595,1598,1600,1602,1604,1606,1608,1610,1612,1614,1616,1618],{"class":69,"data-line":81,"line":81},[67,1589,280],{"class":73},[67,1591,1592],{"class":77}," ValidateToken",[67,1594,286],{"class":98},[67,1596,1597],{"class":594},"tokenString",[67,1599,603],{"class":73},[67,1601,289],{"class":98},[67,1603,95],{"class":94},[67,1605,286],{"class":98},[67,1607,976],{"class":73},[67,1609,1227],{"class":77},[67,1611,331],{"class":94},[67,1613,994],{"class":73},[67,1615,289],{"class":98},[67,1617,95],{"class":94},[67,1619,144],{"class":98},[67,1621,1622,1624,1627,1629,1631,1634,1636,1639,1641,1643,1646,1648,1650,1652,1654,1657,1659,1661,1663,1666,1668,1670,1672,1675,1677,1679,1681,1683,1685,1687],{"class":69,"data-line":88,"line":88},[67,1623,106],{"class":105},[67,1625,1626],{"class":94},"token, err ",[67,1628,628],{"class":73},[67,1630,1532],{"class":94},[67,1632,1633],{"class":77},"ParseWithClaims",[67,1635,286],{"class":309},[67,1637,1638],{"class":94},"tokenString, ",[67,1640,743],{"class":73},[67,1642,1227],{"class":77},[67,1644,1645],{"class":325},"{",[67,1647,814],{"class":325},[67,1649,331],{"class":94},[67,1651,280],{"class":73},[67,1653,286],{"class":325},[67,1655,1656],{"class":594},"token",[67,1658,1430],{"class":73},[67,1660,1281],{"class":77},[67,1662,749],{"class":94},[67,1664,1665],{"class":77},"Token",[67,1667,289],{"class":325},[67,1669,95],{"class":94},[67,1671,286],{"class":325},[67,1673,1674],{"class":73},"interface",[67,1676,1645],{"class":98},[67,1678,814],{"class":98},[67,1680,331],{"class":94},[67,1682,994],{"class":73},[67,1684,289],{"class":325},[67,1686,95],{"class":94},[67,1688,144],{"class":325},[67,1690,1691,1693,1695,1697,1699,1701,1703,1705,1707,1709,1711,1713],{"class":69,"data-line":102,"line":102},[67,1692,106],{"class":105},[67,1694,106],{"class":105},[67,1696,663],{"class":73},[67,1698,95],{"class":94},[67,1700,1018],{"class":98},[67,1702,1021],{"class":98},[67,1704,1024],{"class":73},[67,1706,286],{"class":98},[67,1708,1567],{"class":94},[67,1710,289],{"class":98},[67,1712,331],{"class":94},[67,1714,1715],{"class":559},"nil\n",[67,1717,1718,1720,1722,1724],{"class":69,"data-line":119,"line":119},[67,1719,106],{"class":105},[67,1721,62],{"class":94},[67,1723,814],{"class":325},[67,1725,122],{"class":309},[67,1727,1728],{"class":69,"data-line":125,"line":125},[67,1729,85],{"emptyLinePlaceholder":84},[67,1731,1732,1734,1736,1738,1740,1742,1744],{"class":69,"data-line":130,"line":130},[67,1733,106],{"class":105},[67,1735,622],{"class":73},[67,1737,874],{"class":94},[67,1739,646],{"class":73},[67,1741,879],{"class":559},[67,1743,95],{"class":94},[67,1745,144],{"class":309},[67,1747,1748,1750,1752,1754,1756],{"class":69,"data-line":147,"line":147},[67,1749,106],{"class":105},[67,1751,106],{"class":105},[67,1753,663],{"class":73},[67,1755,879],{"class":559},[67,1757,1758],{"class":94},", err\n",[67,1760,1761,1763,1765],{"class":69,"data-line":158,"line":158},[67,1762,106],{"class":105},[67,1764,62],{"class":94},[67,1766,252],{"class":309},[67,1768,1769],{"class":69,"data-line":168,"line":168},[67,1770,85],{"emptyLinePlaceholder":84},[67,1772,1773,1775,1777,1780,1782,1785,1787,1789,1791,1793,1796,1799,1802],{"class":69,"data-line":178,"line":178},[67,1774,106],{"class":105},[67,1776,622],{"class":73},[67,1778,1779],{"class":94}," claims, ok ",[67,1781,628],{"class":73},[67,1783,1784],{"class":94}," token.Claims.",[67,1786,286],{"class":309},[67,1788,976],{"class":73},[67,1790,1227],{"class":77},[67,1792,289],{"class":309},[67,1794,1795],{"class":94},"; ok ",[67,1797,1798],{"class":73},"&&",[67,1800,1801],{"class":94}," token.Valid ",[67,1803,144],{"class":309},[67,1805,1806,1808,1810,1812,1815],{"class":69,"data-line":188,"line":188},[67,1807,106],{"class":105},[67,1809,106],{"class":105},[67,1811,663],{"class":73},[67,1813,1814],{"class":94}," claims, ",[67,1816,1715],{"class":559},[67,1818,1819,1821,1823],{"class":69,"data-line":198,"line":198},[67,1820,106],{"class":105},[67,1822,62],{"class":94},[67,1824,252],{"class":309},[67,1826,1827],{"class":69,"data-line":208,"line":208},[67,1828,85],{"emptyLinePlaceholder":84},[67,1830,1831,1833,1835,1837,1840,1843,1845,1848],{"class":69,"data-line":218,"line":218},[67,1832,106],{"class":105},[67,1834,663],{"class":73},[67,1836,879],{"class":559},[67,1838,1839],{"class":94},", errors.",[67,1841,1842],{"class":77},"New",[67,1844,286],{"class":309},[67,1846,1847],{"class":109},"\"无效的token\"",[67,1849,122],{"class":309},[67,1851,1852],{"class":69,"data-line":228,"line":228},[67,1853,252],{"class":98},[29,1855,1857],{"id":1856},"鉴权中间件保护接口","鉴权中间件：保护接口",[17,1859,1860],{},[20,1861,1862,1863,1866,1867,1870],{},"从 ",[64,1864,1865],{"code":1865},"Authorization: Bearer \u003Ctoken>"," 提取 token，校验后把 ",[64,1868,1869],{"code":1869},"user_id\u002Fusername"," 放入上下文，用于后续处理。",[57,1872,1874],{"className":59,"code":1873,"language":61,"meta":62,"style":62},"\u002F\u002F 认证中间件\nfunc AuthMiddleware() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tauthHeader := c.GetHeader(\"Authorization\")\n\t\tif authHeader == \"\" {\n\t\t\tc.JSON(http.StatusUnauthorized, gin.H{\"error\": \"缺少Authorization头\"})\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\n\t\t\u002F\u002F 检查Bearer前缀\n\t\ttokenParts := strings.Split(authHeader, \" \")\n\t\tif len(tokenParts) != 2 || tokenParts[0] != \"Bearer\" {\n\t\t\tc.JSON(http.StatusUnauthorized, gin.H{\"error\": \"无效的Authorization格式\"})\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\n\t\ttokenString := tokenParts[1]\n\t\tclaims, err := utils.ValidateToken(tokenString)\n\t\tif err != nil {\n\t\t\tc.JSON(http.StatusUnauthorized, gin.H{\"error\": \"无效的token\"})\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\n\t\t\u002F\u002F 将用户信息存储到上下文中\n\t\tfmt.Println(\"user_id\", claims.UserID)\n\t\tc.Set(\"user_id\", claims.UserID)\n\t\tc.Set(\"username\", claims.Username)\n\t\tc.Next()\n\t}\n}\n",[64,1875,1876,1881,1906,1935,1959,1978,2019,2036,2047,2057,2061,2070,2097,2145,2180,2196,2206,2216,2220,2241,2264,2282,2316,2332,2342,2352,2356,2365,2386,2405,2425,2440,2448],{"__ignoreMap":62},[67,1877,1878],{"class":69,"data-line":70,"line":70},[67,1879,1880],{"class":827},"\u002F\u002F 认证中间件\n",[67,1882,1883,1885,1888,1890,1892,1894,1897,1899,1902,1904],{"class":69,"data-line":81,"line":81},[67,1884,280],{"class":73},[67,1886,1887],{"class":77}," AuthMiddleware",[67,1889,286],{"class":98},[67,1891,289],{"class":98},[67,1893,95],{"class":94},[67,1895,1896],{"class":77},"gin",[67,1898,749],{"class":94},[67,1900,1901],{"class":77},"HandlerFunc",[67,1903,95],{"class":94},[67,1905,144],{"class":98},[67,1907,1908,1910,1912,1915,1917,1920,1922,1924,1926,1929,1931,1933],{"class":69,"data-line":88,"line":88},[67,1909,106],{"class":105},[67,1911,663],{"class":73},[67,1913,1914],{"class":73}," func",[67,1916,286],{"class":309},[67,1918,1919],{"class":594},"c",[67,1921,1430],{"class":73},[67,1923,1896],{"class":77},[67,1925,749],{"class":94},[67,1927,1928],{"class":77},"Context",[67,1930,289],{"class":309},[67,1932,95],{"class":94},[67,1934,144],{"class":309},[67,1936,1937,1939,1941,1944,1946,1949,1952,1954,1957],{"class":69,"data-line":102,"line":102},[67,1938,106],{"class":105},[67,1940,106],{"class":105},[67,1942,1943],{"class":94},"authHeader ",[67,1945,628],{"class":73},[67,1947,1948],{"class":94}," c.",[67,1950,1951],{"class":77},"GetHeader",[67,1953,286],{"class":325},[67,1955,1956],{"class":109},"\"Authorization\"",[67,1958,122],{"class":325},[67,1960,1961,1963,1965,1967,1970,1972,1974,1976],{"class":69,"data-line":119,"line":119},[67,1962,106],{"class":105},[67,1964,106],{"class":105},[67,1966,622],{"class":73},[67,1968,1969],{"class":94}," authHeader ",[67,1971,1205],{"class":73},[67,1973,649],{"class":109},[67,1975,95],{"class":94},[67,1977,144],{"class":325},[67,1979,1980,1982,1984,1986,1989,1992,1994,1997,1999,2001,2004,2006,2009,2012,2015,2017],{"class":69,"data-line":125,"line":125},[67,1981,106],{"class":105},[67,1983,106],{"class":105},[67,1985,106],{"class":105},[67,1987,1988],{"class":94},"c.",[67,1990,1991],{"class":77},"JSON",[67,1993,286],{"class":98},[67,1995,1996],{"class":94},"http.StatusUnauthorized, ",[67,1998,1896],{"class":77},[67,2000,749],{"class":94},[67,2002,2003],{"class":77},"H",[67,2005,1645],{"class":309},[67,2007,2008],{"class":109},"\"error\"",[67,2010,2011],{"class":94},": ",[67,2013,2014],{"class":109},"\"缺少Authorization头\"",[67,2016,814],{"class":309},[67,2018,122],{"class":98},[67,2020,2021,2023,2025,2027,2029,2032,2034],{"class":69,"data-line":130,"line":130},[67,2022,106],{"class":105},[67,2024,106],{"class":105},[67,2026,106],{"class":105},[67,2028,1988],{"class":94},[67,2030,2031],{"class":77},"Abort",[67,2033,286],{"class":98},[67,2035,122],{"class":98},[67,2037,2038,2040,2042,2044],{"class":69,"data-line":147,"line":147},[67,2039,106],{"class":105},[67,2041,106],{"class":105},[67,2043,106],{"class":105},[67,2045,2046],{"class":73},"return\n",[67,2048,2049,2051,2053,2055],{"class":69,"data-line":158,"line":158},[67,2050,106],{"class":105},[67,2052,106],{"class":105},[67,2054,62],{"class":94},[67,2056,252],{"class":325},[67,2058,2059],{"class":69,"data-line":168,"line":168},[67,2060,85],{"emptyLinePlaceholder":84},[67,2062,2063,2065,2067],{"class":69,"data-line":178,"line":178},[67,2064,106],{"class":105},[67,2066,106],{"class":105},[67,2068,2069],{"class":827},"\u002F\u002F 检查Bearer前缀\n",[67,2071,2072,2074,2076,2079,2081,2084,2087,2089,2092,2095],{"class":69,"data-line":188,"line":188},[67,2073,106],{"class":105},[67,2075,106],{"class":105},[67,2077,2078],{"class":94},"tokenParts ",[67,2080,628],{"class":73},[67,2082,2083],{"class":94}," strings.",[67,2085,2086],{"class":77},"Split",[67,2088,286],{"class":325},[67,2090,2091],{"class":94},"authHeader, ",[67,2093,2094],{"class":109},"\" \"",[67,2096,122],{"class":325},[67,2098,2099,2101,2103,2105,2108,2110,2113,2115,2117,2119,2122,2125,2128,2130,2132,2134,2136,2138,2141,2143],{"class":69,"data-line":198,"line":198},[67,2100,106],{"class":105},[67,2102,106],{"class":105},[67,2104,622],{"class":73},[67,2106,2107],{"class":77}," len",[67,2109,286],{"class":325},[67,2111,2112],{"class":94},"tokenParts",[67,2114,289],{"class":325},[67,2116,95],{"class":94},[67,2118,646],{"class":73},[67,2120,2121],{"class":559}," 2",[67,2123,2124],{"class":73}," ||",[67,2126,2127],{"class":94}," tokenParts",[67,2129,1018],{"class":325},[67,2131,560],{"class":559},[67,2133,1021],{"class":325},[67,2135,95],{"class":94},[67,2137,646],{"class":73},[67,2139,2140],{"class":109}," \"Bearer\"",[67,2142,95],{"class":94},[67,2144,144],{"class":325},[67,2146,2147,2149,2151,2153,2155,2157,2159,2161,2163,2165,2167,2169,2171,2173,2176,2178],{"class":69,"data-line":208,"line":208},[67,2148,106],{"class":105},[67,2150,106],{"class":105},[67,2152,106],{"class":105},[67,2154,1988],{"class":94},[67,2156,1991],{"class":77},[67,2158,286],{"class":98},[67,2160,1996],{"class":94},[67,2162,1896],{"class":77},[67,2164,749],{"class":94},[67,2166,2003],{"class":77},[67,2168,1645],{"class":309},[67,2170,2008],{"class":109},[67,2172,2011],{"class":94},[67,2174,2175],{"class":109},"\"无效的Authorization格式\"",[67,2177,814],{"class":309},[67,2179,122],{"class":98},[67,2181,2182,2184,2186,2188,2190,2192,2194],{"class":69,"data-line":218,"line":218},[67,2183,106],{"class":105},[67,2185,106],{"class":105},[67,2187,106],{"class":105},[67,2189,1988],{"class":94},[67,2191,2031],{"class":77},[67,2193,286],{"class":98},[67,2195,122],{"class":98},[67,2197,2198,2200,2202,2204],{"class":69,"data-line":228,"line":228},[67,2199,106],{"class":105},[67,2201,106],{"class":105},[67,2203,106],{"class":105},[67,2205,2046],{"class":73},[67,2207,2208,2210,2212,2214],{"class":69,"data-line":238,"line":238},[67,2209,106],{"class":105},[67,2211,106],{"class":105},[67,2213,62],{"class":94},[67,2215,252],{"class":325},[67,2217,2218],{"class":69,"data-line":249,"line":249},[67,2219,85],{"emptyLinePlaceholder":84},[67,2221,2222,2224,2226,2229,2231,2233,2235,2238],{"class":69,"data-line":255,"line":255},[67,2223,106],{"class":105},[67,2225,106],{"class":105},[67,2227,2228],{"class":94},"tokenString ",[67,2230,628],{"class":73},[67,2232,2127],{"class":94},[67,2234,1018],{"class":325},[67,2236,2237],{"class":559},"1",[67,2239,2240],{"class":325},"]\n",[67,2242,2243,2245,2247,2250,2252,2255,2258,2260,2262],{"class":69,"data-line":260,"line":260},[67,2244,106],{"class":105},[67,2246,106],{"class":105},[67,2248,2249],{"class":94},"claims, err ",[67,2251,628],{"class":73},[67,2253,2254],{"class":94}," utils.",[67,2256,2257],{"class":77},"ValidateToken",[67,2259,286],{"class":325},[67,2261,1597],{"class":94},[67,2263,122],{"class":325},[67,2265,2266,2268,2270,2272,2274,2276,2278,2280],{"class":69,"data-line":272,"line":272},[67,2267,106],{"class":105},[67,2269,106],{"class":105},[67,2271,622],{"class":73},[67,2273,874],{"class":94},[67,2275,646],{"class":73},[67,2277,879],{"class":559},[67,2279,95],{"class":94},[67,2281,144],{"class":325},[67,2283,2284,2286,2288,2290,2292,2294,2296,2298,2300,2302,2304,2306,2308,2310,2312,2314],{"class":69,"data-line":277,"line":277},[67,2285,106],{"class":105},[67,2287,106],{"class":105},[67,2289,106],{"class":105},[67,2291,1988],{"class":94},[67,2293,1991],{"class":77},[67,2295,286],{"class":98},[67,2297,1996],{"class":94},[67,2299,1896],{"class":77},[67,2301,749],{"class":94},[67,2303,2003],{"class":77},[67,2305,1645],{"class":309},[67,2307,2008],{"class":109},[67,2309,2011],{"class":94},[67,2311,1847],{"class":109},[67,2313,814],{"class":309},[67,2315,122],{"class":98},[67,2317,2318,2320,2322,2324,2326,2328,2330],{"class":69,"data-line":296,"line":296},[67,2319,106],{"class":105},[67,2321,106],{"class":105},[67,2323,106],{"class":105},[67,2325,1988],{"class":94},[67,2327,2031],{"class":77},[67,2329,286],{"class":98},[67,2331,122],{"class":98},[67,2333,2334,2336,2338,2340],{"class":69,"data-line":312,"line":312},[67,2335,106],{"class":105},[67,2337,106],{"class":105},[67,2339,106],{"class":105},[67,2341,2046],{"class":73},[67,2343,2344,2346,2348,2350],{"class":69,"data-line":342,"line":342},[67,2345,106],{"class":105},[67,2347,106],{"class":105},[67,2349,62],{"class":94},[67,2351,252],{"class":325},[67,2353,2354],{"class":69,"data-line":368,"line":368},[67,2355,85],{"emptyLinePlaceholder":84},[67,2357,2358,2360,2362],{"class":69,"data-line":394,"line":394},[67,2359,106],{"class":105},[67,2361,106],{"class":105},[67,2363,2364],{"class":827},"\u002F\u002F 将用户信息存储到上下文中\n",[67,2366,2367,2369,2371,2374,2376,2378,2381,2384],{"class":69,"data-line":420,"line":420},[67,2368,106],{"class":105},[67,2370,106],{"class":105},[67,2372,2373],{"class":94},"fmt.",[67,2375,926],{"class":77},[67,2377,286],{"class":325},[67,2379,2380],{"class":109},"\"user_id\"",[67,2382,2383],{"class":94},", claims.UserID",[67,2385,122],{"class":325},[67,2387,2388,2390,2392,2394,2397,2399,2401,2403],{"class":69,"data-line":446,"line":446},[67,2389,106],{"class":105},[67,2391,106],{"class":105},[67,2393,1988],{"class":94},[67,2395,2396],{"class":77},"Set",[67,2398,286],{"class":325},[67,2400,2380],{"class":109},[67,2402,2383],{"class":94},[67,2404,122],{"class":325},[67,2406,2407,2409,2411,2413,2415,2417,2420,2423],{"class":69,"data-line":472,"line":472},[67,2408,106],{"class":105},[67,2410,106],{"class":105},[67,2412,1988],{"class":94},[67,2414,2396],{"class":77},[67,2416,286],{"class":325},[67,2418,2419],{"class":109},"\"username\"",[67,2421,2422],{"class":94},", claims.Username",[67,2424,122],{"class":325},[67,2426,2427,2429,2431,2433,2436,2438],{"class":69,"data-line":497,"line":497},[67,2428,106],{"class":105},[67,2430,106],{"class":105},[67,2432,1988],{"class":94},[67,2434,2435],{"class":77},"Next",[67,2437,286],{"class":325},[67,2439,122],{"class":325},[67,2441,2442,2444,2446],{"class":69,"data-line":523,"line":523},[67,2443,106],{"class":105},[67,2445,62],{"class":94},[67,2447,252],{"class":309},[67,2449,2450],{"class":69,"data-line":549,"line":549},[67,2451,252],{"class":98},[29,2453,2454],{"id":2454},"路由与模块装配",[17,2456,2457],{},[20,2458,2459],{},"公开 API：注册、登录；保护 API：获取资料、登出、更新资料；并挂载静态演示页。",[57,2461,2463],{"className":59,"code":2462,"language":61,"meta":62,"style":62},"\u002F\u002F 公开路由\npublic := r.Group(\"\u002Fapi\")\n{\n\tpublic.POST(\"\u002Fregister\", handlers.Register)\n\tpublic.POST(\"\u002Flogin\", handlers.Login)\n}\n\n\u002F\u002F 需要认证的路由\nprotected := r.Group(\"\u002Fapi\")\nprotected.Use(middleware.AuthMiddleware())\n{\n\tprotected.GET(\"\u002Fprofile\", handlers.GetProfile)\n\tprotected.POST(\"\u002Flogout\", handlers.Logout)\n\tprotected.PUT(\"\u002Fprofile\", handlers.UpdateProfile)\n}\n",[64,2464,2465,2470,2490,2494,2514,2532,2536,2540,2545,2562,2584,2588,2607,2625,2643],{"__ignoreMap":62},[67,2466,2467],{"class":69,"data-line":70,"line":70},[67,2468,2469],{"class":827},"\u002F\u002F 公开路由\n",[67,2471,2472,2475,2477,2480,2483,2485,2488],{"class":69,"data-line":81,"line":81},[67,2473,2474],{"class":94},"public ",[67,2476,628],{"class":73},[67,2478,2479],{"class":94}," r.",[67,2481,2482],{"class":77},"Group",[67,2484,286],{"class":98},[67,2486,2487],{"class":109},"\"\u002Fapi\"",[67,2489,122],{"class":98},[67,2491,2492],{"class":69,"data-line":88,"line":88},[67,2493,144],{"class":98},[67,2495,2496,2498,2501,2504,2506,2509,2512],{"class":69,"data-line":102,"line":102},[67,2497,106],{"class":105},[67,2499,2500],{"class":94},"public.",[67,2502,2503],{"class":77},"POST",[67,2505,286],{"class":309},[67,2507,2508],{"class":109},"\"\u002Fregister\"",[67,2510,2511],{"class":94},", handlers.Register",[67,2513,122],{"class":309},[67,2515,2516,2518,2520,2522,2524,2527,2530],{"class":69,"data-line":119,"line":119},[67,2517,106],{"class":105},[67,2519,2500],{"class":94},[67,2521,2503],{"class":77},[67,2523,286],{"class":309},[67,2525,2526],{"class":109},"\"\u002Flogin\"",[67,2528,2529],{"class":94},", handlers.Login",[67,2531,122],{"class":309},[67,2533,2534],{"class":69,"data-line":125,"line":125},[67,2535,252],{"class":98},[67,2537,2538],{"class":69,"data-line":130,"line":130},[67,2539,85],{"emptyLinePlaceholder":84},[67,2541,2542],{"class":69,"data-line":147,"line":147},[67,2543,2544],{"class":827},"\u002F\u002F 需要认证的路由\n",[67,2546,2547,2550,2552,2554,2556,2558,2560],{"class":69,"data-line":158,"line":158},[67,2548,2549],{"class":94},"protected ",[67,2551,628],{"class":73},[67,2553,2479],{"class":94},[67,2555,2482],{"class":77},[67,2557,286],{"class":98},[67,2559,2487],{"class":109},[67,2561,122],{"class":98},[67,2563,2564,2567,2570,2572,2575,2578,2580,2582],{"class":69,"data-line":168,"line":168},[67,2565,2566],{"class":94},"protected.",[67,2568,2569],{"class":77},"Use",[67,2571,286],{"class":98},[67,2573,2574],{"class":94},"middleware.",[67,2576,2577],{"class":77},"AuthMiddleware",[67,2579,286],{"class":309},[67,2581,289],{"class":309},[67,2583,122],{"class":98},[67,2585,2586],{"class":69,"data-line":178,"line":178},[67,2587,144],{"class":98},[67,2589,2590,2592,2594,2597,2599,2602,2605],{"class":69,"data-line":188,"line":188},[67,2591,106],{"class":105},[67,2593,2566],{"class":94},[67,2595,2596],{"class":77},"GET",[67,2598,286],{"class":309},[67,2600,2601],{"class":109},"\"\u002Fprofile\"",[67,2603,2604],{"class":94},", handlers.GetProfile",[67,2606,122],{"class":309},[67,2608,2609,2611,2613,2615,2617,2620,2623],{"class":69,"data-line":198,"line":198},[67,2610,106],{"class":105},[67,2612,2566],{"class":94},[67,2614,2503],{"class":77},[67,2616,286],{"class":309},[67,2618,2619],{"class":109},"\"\u002Flogout\"",[67,2621,2622],{"class":94},", handlers.Logout",[67,2624,122],{"class":309},[67,2626,2627,2629,2631,2634,2636,2638,2641],{"class":69,"data-line":208,"line":208},[67,2628,106],{"class":105},[67,2630,2566],{"class":94},[67,2632,2633],{"class":77},"PUT",[67,2635,286],{"class":309},[67,2637,2601],{"class":109},[67,2639,2640],{"class":94},", handlers.UpdateProfile",[67,2642,122],{"class":309},[67,2644,2645],{"class":69,"data-line":218,"line":218},[67,2646,252],{"class":98},[29,2648,2650],{"id":2649},"登录与签发-token","登录与签发 Token",[17,2652,2653],{},[20,2654,2655,2656,2658],{},"登录成功后返回 ",[64,2657,1656],{"code":1656}," 与用户信息：",[57,2660,2662],{"className":59,"code":2661,"language":61,"meta":62,"style":62},"\u002F\u002F 登录处理器\nfunc Login(c *gin.Context) {\n\tvar req models.LoginRequest\n\tif err := c.ShouldBindJSON(&req); err != nil {\n\t\tc.JSON(http.StatusBadRequest, gin.H{\"error\": \"请求参数错误: \" + err.Error()})\n\t\treturn\n\t}\n\n\t\u002F\u002F 查找用户\n\tvar user models.User\n\tif err := database.DB.Where(\"username = ?\", req.Username).First(&user).Error; err != nil {\n\t\tc.JSON(http.StatusUnauthorized, gin.H{\"error\": \"用户名或密码错误\"})\n\t\treturn\n\t}\n\n\t\u002F\u002F 验证密码\n\tif !user.CheckPassword(req.Password) {\n\t\tc.JSON(http.StatusUnauthorized, gin.H{\"error\": \"用户名或密码错误\"})\n\t\treturn\n\t}\n\n\t\u002F\u002F 生成JWT token\n\ttoken, err := utils.GenerateToken(user.ID, user.Username)\n\tif err != nil {\n\t\tc.JSON(http.StatusInternalServerError, gin.H{\"error\": \"Token生成失败\"})\n\t\treturn\n\t}\n\n\tc.JSON(http.StatusOK, gin.H{\n\t\t\"message\": \"登录成功\",\n\t\t\"token\":   token,\n\t\t\"user\":    user.ToResponse(),\n\t})\n}\n",[64,2663,2664,2669,2694,2711,2746,2793,2801,2809,2813,2820,2836,2887,2920,2928,2936,2940,2946,2971,3003,3011,3019,3023,3030,3050,3066,3100,3108,3116,3120,3141,3157,3169,3190,3200],{"__ignoreMap":62},[67,2665,2666],{"class":69,"data-line":70,"line":70},[67,2667,2668],{"class":827},"\u002F\u002F 登录处理器\n",[67,2670,2671,2673,2676,2678,2680,2682,2684,2686,2688,2690,2692],{"class":69,"data-line":81,"line":81},[67,2672,280],{"class":73},[67,2674,2675],{"class":77}," Login",[67,2677,286],{"class":98},[67,2679,1919],{"class":594},[67,2681,1430],{"class":73},[67,2683,1896],{"class":77},[67,2685,749],{"class":94},[67,2687,1928],{"class":77},[67,2689,289],{"class":98},[67,2691,95],{"class":94},[67,2693,144],{"class":98},[67,2695,2696,2698,2700,2703,2706,2708],{"class":69,"data-line":88,"line":88},[67,2697,106],{"class":105},[67,2699,263],{"class":73},[67,2701,2702],{"class":94}," req ",[67,2704,2705],{"class":77},"models",[67,2707,749],{"class":94},[67,2709,2710],{"class":77},"LoginRequest\n",[67,2712,2713,2715,2717,2719,2721,2723,2726,2728,2730,2733,2735,2738,2740,2742,2744],{"class":69,"data-line":102,"line":102},[67,2714,106],{"class":105},[67,2716,622],{"class":73},[67,2718,874],{"class":94},[67,2720,628],{"class":73},[67,2722,1948],{"class":94},[67,2724,2725],{"class":77},"ShouldBindJSON",[67,2727,286],{"class":309},[67,2729,743],{"class":73},[67,2731,2732],{"class":94},"req",[67,2734,289],{"class":309},[67,2736,2737],{"class":94},"; err ",[67,2739,646],{"class":73},[67,2741,879],{"class":559},[67,2743,95],{"class":94},[67,2745,144],{"class":309},[67,2747,2748,2750,2752,2754,2756,2758,2761,2763,2765,2767,2769,2771,2773,2776,2779,2782,2785,2787,2789,2791],{"class":69,"data-line":119,"line":119},[67,2749,106],{"class":105},[67,2751,106],{"class":105},[67,2753,1988],{"class":94},[67,2755,1991],{"class":77},[67,2757,286],{"class":325},[67,2759,2760],{"class":94},"http.StatusBadRequest, ",[67,2762,1896],{"class":77},[67,2764,749],{"class":94},[67,2766,2003],{"class":77},[67,2768,1645],{"class":98},[67,2770,2008],{"class":109},[67,2772,2011],{"class":94},[67,2774,2775],{"class":109},"\"请求参数错误: \"",[67,2777,2778],{"class":73}," +",[67,2780,2781],{"class":94}," err.",[67,2783,2784],{"class":77},"Error",[67,2786,286],{"class":309},[67,2788,289],{"class":309},[67,2790,814],{"class":98},[67,2792,122],{"class":325},[67,2794,2795,2797,2799],{"class":69,"data-line":125,"line":125},[67,2796,106],{"class":105},[67,2798,106],{"class":105},[67,2800,2046],{"class":73},[67,2802,2803,2805,2807],{"class":69,"data-line":130,"line":130},[67,2804,106],{"class":105},[67,2806,62],{"class":94},[67,2808,252],{"class":309},[67,2810,2811],{"class":69,"data-line":147,"line":147},[67,2812,85],{"emptyLinePlaceholder":84},[67,2814,2815,2817],{"class":69,"data-line":158,"line":158},[67,2816,106],{"class":105},[67,2818,2819],{"class":827},"\u002F\u002F 查找用户\n",[67,2821,2822,2824,2826,2829,2831,2833],{"class":69,"data-line":168,"line":168},[67,2823,106],{"class":105},[67,2825,263],{"class":73},[67,2827,2828],{"class":94}," user ",[67,2830,2705],{"class":77},[67,2832,749],{"class":94},[67,2834,2835],{"class":77},"User\n",[67,2837,2838,2840,2842,2844,2846,2849,2852,2854,2857,2860,2862,2864,2867,2869,2871,2874,2876,2879,2881,2883,2885],{"class":69,"data-line":178,"line":178},[67,2839,106],{"class":105},[67,2841,622],{"class":73},[67,2843,874],{"class":94},[67,2845,628],{"class":73},[67,2847,2848],{"class":94}," database.DB.",[67,2850,2851],{"class":77},"Where",[67,2853,286],{"class":309},[67,2855,2856],{"class":109},"\"username = ?\"",[67,2858,2859],{"class":94},", req.Username",[67,2861,289],{"class":309},[67,2863,749],{"class":94},[67,2865,2866],{"class":77},"First",[67,2868,286],{"class":309},[67,2870,743],{"class":73},[67,2872,2873],{"class":94},"user",[67,2875,289],{"class":309},[67,2877,2878],{"class":94},".Error; err ",[67,2880,646],{"class":73},[67,2882,879],{"class":559},[67,2884,95],{"class":94},[67,2886,144],{"class":309},[67,2888,2889,2891,2893,2895,2897,2899,2901,2903,2905,2907,2909,2911,2913,2916,2918],{"class":69,"data-line":188,"line":188},[67,2890,106],{"class":105},[67,2892,106],{"class":105},[67,2894,1988],{"class":94},[67,2896,1991],{"class":77},[67,2898,286],{"class":325},[67,2900,1996],{"class":94},[67,2902,1896],{"class":77},[67,2904,749],{"class":94},[67,2906,2003],{"class":77},[67,2908,1645],{"class":98},[67,2910,2008],{"class":109},[67,2912,2011],{"class":94},[67,2914,2915],{"class":109},"\"用户名或密码错误\"",[67,2917,814],{"class":98},[67,2919,122],{"class":325},[67,2921,2922,2924,2926],{"class":69,"data-line":198,"line":198},[67,2923,106],{"class":105},[67,2925,106],{"class":105},[67,2927,2046],{"class":73},[67,2929,2930,2932,2934],{"class":69,"data-line":208,"line":208},[67,2931,106],{"class":105},[67,2933,62],{"class":94},[67,2935,252],{"class":309},[67,2937,2938],{"class":69,"data-line":218,"line":218},[67,2939,85],{"emptyLinePlaceholder":84},[67,2941,2942,2944],{"class":69,"data-line":228,"line":228},[67,2943,106],{"class":105},[67,2945,1111],{"class":827},[67,2947,2948,2950,2952,2955,2958,2960,2962,2965,2967,2969],{"class":69,"data-line":238,"line":238},[67,2949,106],{"class":105},[67,2951,622],{"class":73},[67,2953,2954],{"class":73}," !",[67,2956,2957],{"class":94},"user.",[67,2959,1132],{"class":77},[67,2961,286],{"class":309},[67,2963,2964],{"class":94},"req.Password",[67,2966,289],{"class":309},[67,2968,95],{"class":94},[67,2970,144],{"class":309},[67,2972,2973,2975,2977,2979,2981,2983,2985,2987,2989,2991,2993,2995,2997,2999,3001],{"class":69,"data-line":249,"line":249},[67,2974,106],{"class":105},[67,2976,106],{"class":105},[67,2978,1988],{"class":94},[67,2980,1991],{"class":77},[67,2982,286],{"class":325},[67,2984,1996],{"class":94},[67,2986,1896],{"class":77},[67,2988,749],{"class":94},[67,2990,2003],{"class":77},[67,2992,1645],{"class":98},[67,2994,2008],{"class":109},[67,2996,2011],{"class":94},[67,2998,2915],{"class":109},[67,3000,814],{"class":98},[67,3002,122],{"class":325},[67,3004,3005,3007,3009],{"class":69,"data-line":255,"line":255},[67,3006,106],{"class":105},[67,3008,106],{"class":105},[67,3010,2046],{"class":73},[67,3012,3013,3015,3017],{"class":69,"data-line":260,"line":260},[67,3014,106],{"class":105},[67,3016,62],{"class":94},[67,3018,252],{"class":309},[67,3020,3021],{"class":69,"data-line":272,"line":272},[67,3022,85],{"emptyLinePlaceholder":84},[67,3024,3025,3027],{"class":69,"data-line":277,"line":277},[67,3026,106],{"class":105},[67,3028,3029],{"class":827},"\u002F\u002F 生成JWT token\n",[67,3031,3032,3034,3036,3038,3040,3043,3045,3048],{"class":69,"data-line":296,"line":296},[67,3033,106],{"class":105},[67,3035,1626],{"class":94},[67,3037,628],{"class":73},[67,3039,2254],{"class":94},[67,3041,3042],{"class":77},"GenerateToken",[67,3044,286],{"class":309},[67,3046,3047],{"class":94},"user.ID, user.Username",[67,3049,122],{"class":309},[67,3051,3052,3054,3056,3058,3060,3062,3064],{"class":69,"data-line":312,"line":312},[67,3053,106],{"class":105},[67,3055,622],{"class":73},[67,3057,874],{"class":94},[67,3059,646],{"class":73},[67,3061,879],{"class":559},[67,3063,95],{"class":94},[67,3065,144],{"class":309},[67,3067,3068,3070,3072,3074,3076,3078,3081,3083,3085,3087,3089,3091,3093,3096,3098],{"class":69,"data-line":342,"line":342},[67,3069,106],{"class":105},[67,3071,106],{"class":105},[67,3073,1988],{"class":94},[67,3075,1991],{"class":77},[67,3077,286],{"class":325},[67,3079,3080],{"class":94},"http.StatusInternalServerError, ",[67,3082,1896],{"class":77},[67,3084,749],{"class":94},[67,3086,2003],{"class":77},[67,3088,1645],{"class":98},[67,3090,2008],{"class":109},[67,3092,2011],{"class":94},[67,3094,3095],{"class":109},"\"Token生成失败\"",[67,3097,814],{"class":98},[67,3099,122],{"class":325},[67,3101,3102,3104,3106],{"class":69,"data-line":368,"line":368},[67,3103,106],{"class":105},[67,3105,106],{"class":105},[67,3107,2046],{"class":73},[67,3109,3110,3112,3114],{"class":69,"data-line":394,"line":394},[67,3111,106],{"class":105},[67,3113,62],{"class":94},[67,3115,252],{"class":309},[67,3117,3118],{"class":69,"data-line":420,"line":420},[67,3119,85],{"emptyLinePlaceholder":84},[67,3121,3122,3124,3126,3128,3130,3133,3135,3137,3139],{"class":69,"data-line":446,"line":446},[67,3123,106],{"class":105},[67,3125,1988],{"class":94},[67,3127,1991],{"class":77},[67,3129,286],{"class":309},[67,3131,3132],{"class":94},"http.StatusOK, ",[67,3134,1896],{"class":77},[67,3136,749],{"class":94},[67,3138,2003],{"class":77},[67,3140,144],{"class":325},[67,3142,3143,3145,3147,3150,3152,3155],{"class":69,"data-line":472,"line":472},[67,3144,106],{"class":105},[67,3146,106],{"class":105},[67,3148,3149],{"class":109},"\"message\"",[67,3151,2011],{"class":94},[67,3153,3154],{"class":109},"\"登录成功\"",[67,3156,339],{"class":94},[67,3158,3159,3161,3163,3166],{"class":69,"data-line":497,"line":497},[67,3160,106],{"class":105},[67,3162,106],{"class":105},[67,3164,3165],{"class":109},"\"token\"",[67,3167,3168],{"class":94},":   token,\n",[67,3170,3171,3173,3175,3178,3181,3184,3186,3188],{"class":69,"data-line":523,"line":523},[67,3172,106],{"class":105},[67,3174,106],{"class":105},[67,3176,3177],{"class":109},"\"user\"",[67,3179,3180],{"class":94},":    user.",[67,3182,3183],{"class":77},"ToResponse",[67,3185,286],{"class":98},[67,3187,289],{"class":98},[67,3189,339],{"class":94},[67,3191,3192,3194,3196,3198],{"class":69,"data-line":549,"line":549},[67,3193,106],{"class":105},[67,3195,62],{"class":94},[67,3197,814],{"class":325},[67,3199,122],{"class":309},[67,3201,3202],{"class":69,"data-line":565,"line":565},[67,3203,252],{"class":98},[29,3205,3207],{"id":3206},"使用-redis-做用户资料缓存","使用 Redis 做用户资料缓存",[17,3209,3210],{},[20,3211,3212,3213,3216,3217],{},"访问 ",[64,3214,3215],{"code":3215},"GET \u002Fapi\u002Fprofile"," 时：\n",[17,3218,3219,3226],{},[20,3220,3221,3222,3225],{},"先读 Redis ",[64,3223,3224],{"code":3224},"userId:\u003Cid>","，命中则直接返回；",[20,3227,3228],{},"未命中则查 DB 并以 TTL=1 分钟回填缓存。",[57,3230,3232],{"className":59,"code":3231,"language":61,"meta":62,"style":62},"\u002F\u002F 获取用户资料\nfunc GetProfile(c *gin.Context) {\n\tuserId, exists := c.Get(\"user_id\")\n\t\u002F\u002F ... 省略鉴权存在性与类型断言检查 ...\n\n\t\u002F\u002F从redis获取\n\tval, err := database.RedisClient.Get(c, fmt.Sprintf(\"userId:%d\", userIdVal)).Result()\n\tif err == nil {\n\t\tvar userStru models.User\n\t\tif err := json.Unmarshal([]byte(val), &userStru); err != nil {\n\t\t\tc.JSON(http.StatusInternalServerError, gin.H{\n\t\t\t\t\"message\": err.Error(),\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"user\": userStru.ToResponse(),\n\t\t})\n\t\treturn\n\t}\n\n\tvar user models.User\n\tif err := database.DB.First(&user, userIdVal).Error; err != nil {\n\t\tc.JSON(http.StatusNotFound, gin.H{\"error\": \"用户不存在\"})\n\t\treturn\n\t}\n\n\t\u002F\u002F 将user置入redis\n\tuserBytes, err := json.Marshal(user)\n\t\u002F\u002F ... 省略错误处理 ...\n\tif err := database.RedisClient.Set(c, fmt.Sprintf(\"userId:%d\", userIdVal), userBytes, time.Minute).Err(); err != nil {\n\t\t\u002F\u002F ... 错误处理 ...\n\t\treturn\n\t}\n\n\tc.JSON(http.StatusOK, gin.H{\n\t\t\"user\": user.ToResponse(),\n\t})\n}\n",[64,3233,3234,3239,3264,3284,3291,3295,3302,3348,3364,3381,3433,3457,3480,3494,3504,3514,3518,3540,3561,3573,3581,3589,3593,3607,3640,3674,3682,3690,3694,3701,3721,3728,3784,3793,3801,3809,3813,3833,3852,3862],{"__ignoreMap":62},[67,3235,3236],{"class":69,"data-line":70,"line":70},[67,3237,3238],{"class":827},"\u002F\u002F 获取用户资料\n",[67,3240,3241,3243,3246,3248,3250,3252,3254,3256,3258,3260,3262],{"class":69,"data-line":81,"line":81},[67,3242,280],{"class":73},[67,3244,3245],{"class":77}," GetProfile",[67,3247,286],{"class":98},[67,3249,1919],{"class":594},[67,3251,1430],{"class":73},[67,3253,1896],{"class":77},[67,3255,749],{"class":94},[67,3257,1928],{"class":77},[67,3259,289],{"class":98},[67,3261,95],{"class":94},[67,3263,144],{"class":98},[67,3265,3266,3268,3271,3273,3275,3278,3280,3282],{"class":69,"data-line":88,"line":88},[67,3267,106],{"class":105},[67,3269,3270],{"class":94},"userId, exists ",[67,3272,628],{"class":73},[67,3274,1948],{"class":94},[67,3276,3277],{"class":77},"Get",[67,3279,286],{"class":309},[67,3281,2380],{"class":109},[67,3283,122],{"class":309},[67,3285,3286,3288],{"class":69,"data-line":102,"line":102},[67,3287,106],{"class":105},[67,3289,3290],{"class":827},"\u002F\u002F ... 省略鉴权存在性与类型断言检查 ...\n",[67,3292,3293],{"class":69,"data-line":119,"line":119},[67,3294,85],{"emptyLinePlaceholder":84},[67,3296,3297,3299],{"class":69,"data-line":125,"line":125},[67,3298,106],{"class":105},[67,3300,3301],{"class":827},"\u002F\u002F从redis获取\n",[67,3303,3304,3306,3309,3311,3314,3316,3318,3321,3323,3325,3328,3331,3333,3336,3338,3340,3342,3344,3346],{"class":69,"data-line":130,"line":130},[67,3305,106],{"class":105},[67,3307,3308],{"class":94},"val, err ",[67,3310,628],{"class":73},[67,3312,3313],{"class":94}," database.RedisClient.",[67,3315,3277],{"class":77},[67,3317,286],{"class":309},[67,3319,3320],{"class":94},"c, fmt.",[67,3322,766],{"class":77},[67,3324,286],{"class":325},[67,3326,3327],{"class":109},"\"userId:",[67,3329,3330],{"class":559},"%d",[67,3332,110],{"class":109},[67,3334,3335],{"class":94},", userIdVal",[67,3337,289],{"class":325},[67,3339,289],{"class":309},[67,3341,749],{"class":94},[67,3343,861],{"class":77},[67,3345,286],{"class":309},[67,3347,122],{"class":309},[67,3349,3350,3352,3354,3356,3358,3360,3362],{"class":69,"data-line":147,"line":147},[67,3351,106],{"class":105},[67,3353,622],{"class":73},[67,3355,874],{"class":94},[67,3357,1205],{"class":73},[67,3359,879],{"class":559},[67,3361,95],{"class":94},[67,3363,144],{"class":309},[67,3365,3366,3368,3370,3372,3375,3377,3379],{"class":69,"data-line":158,"line":158},[67,3367,106],{"class":105},[67,3369,106],{"class":105},[67,3371,263],{"class":73},[67,3373,3374],{"class":94}," userStru ",[67,3376,2705],{"class":77},[67,3378,749],{"class":94},[67,3380,2835],{"class":77},[67,3382,3383,3385,3387,3389,3391,3393,3396,3399,3401,3403,3405,3407,3409,3412,3414,3416,3418,3421,3423,3425,3427,3429,3431],{"class":69,"data-line":168,"line":168},[67,3384,106],{"class":105},[67,3386,106],{"class":105},[67,3388,622],{"class":73},[67,3390,874],{"class":94},[67,3392,628],{"class":73},[67,3394,3395],{"class":94}," json.",[67,3397,3398],{"class":77},"Unmarshal",[67,3400,286],{"class":325},[67,3402,1018],{"class":98},[67,3404,1021],{"class":98},[67,3406,1024],{"class":73},[67,3408,286],{"class":98},[67,3410,3411],{"class":94},"val",[67,3413,289],{"class":98},[67,3415,331],{"class":94},[67,3417,743],{"class":73},[67,3419,3420],{"class":94},"userStru",[67,3422,289],{"class":325},[67,3424,2737],{"class":94},[67,3426,646],{"class":73},[67,3428,879],{"class":559},[67,3430,95],{"class":94},[67,3432,144],{"class":325},[67,3434,3435,3437,3439,3441,3443,3445,3447,3449,3451,3453,3455],{"class":69,"data-line":178,"line":178},[67,3436,106],{"class":105},[67,3438,106],{"class":105},[67,3440,106],{"class":105},[67,3442,1988],{"class":94},[67,3444,1991],{"class":77},[67,3446,286],{"class":98},[67,3448,3080],{"class":94},[67,3450,1896],{"class":77},[67,3452,749],{"class":94},[67,3454,2003],{"class":77},[67,3456,144],{"class":309},[67,3458,3459,3461,3463,3465,3467,3469,3472,3474,3476,3478],{"class":69,"data-line":188,"line":188},[67,3460,106],{"class":105},[67,3462,106],{"class":105},[67,3464,106],{"class":105},[67,3466,106],{"class":105},[67,3468,3149],{"class":109},[67,3470,3471],{"class":94},": err.",[67,3473,2784],{"class":77},[67,3475,286],{"class":325},[67,3477,289],{"class":325},[67,3479,339],{"class":94},[67,3481,3482,3484,3486,3488,3490,3492],{"class":69,"data-line":198,"line":198},[67,3483,106],{"class":105},[67,3485,106],{"class":105},[67,3487,106],{"class":105},[67,3489,62],{"class":94},[67,3491,814],{"class":309},[67,3493,122],{"class":98},[67,3495,3496,3498,3500,3502],{"class":69,"data-line":208,"line":208},[67,3497,106],{"class":105},[67,3499,106],{"class":105},[67,3501,106],{"class":105},[67,3503,2046],{"class":73},[67,3505,3506,3508,3510,3512],{"class":69,"data-line":218,"line":218},[67,3507,106],{"class":105},[67,3509,106],{"class":105},[67,3511,62],{"class":94},[67,3513,252],{"class":325},[67,3515,3516],{"class":69,"data-line":228,"line":228},[67,3517,85],{"emptyLinePlaceholder":84},[67,3519,3520,3522,3524,3526,3528,3530,3532,3534,3536,3538],{"class":69,"data-line":238,"line":238},[67,3521,106],{"class":105},[67,3523,106],{"class":105},[67,3525,1988],{"class":94},[67,3527,1991],{"class":77},[67,3529,286],{"class":325},[67,3531,3132],{"class":94},[67,3533,1896],{"class":77},[67,3535,749],{"class":94},[67,3537,2003],{"class":77},[67,3539,144],{"class":98},[67,3541,3542,3544,3546,3548,3550,3553,3555,3557,3559],{"class":69,"data-line":249,"line":249},[67,3543,106],{"class":105},[67,3545,106],{"class":105},[67,3547,106],{"class":105},[67,3549,3177],{"class":109},[67,3551,3552],{"class":94},": userStru.",[67,3554,3183],{"class":77},[67,3556,286],{"class":309},[67,3558,289],{"class":309},[67,3560,339],{"class":94},[67,3562,3563,3565,3567,3569,3571],{"class":69,"data-line":255,"line":255},[67,3564,106],{"class":105},[67,3566,106],{"class":105},[67,3568,62],{"class":94},[67,3570,814],{"class":98},[67,3572,122],{"class":325},[67,3574,3575,3577,3579],{"class":69,"data-line":260,"line":260},[67,3576,106],{"class":105},[67,3578,106],{"class":105},[67,3580,2046],{"class":73},[67,3582,3583,3585,3587],{"class":69,"data-line":272,"line":272},[67,3584,106],{"class":105},[67,3586,62],{"class":94},[67,3588,252],{"class":309},[67,3590,3591],{"class":69,"data-line":277,"line":277},[67,3592,85],{"emptyLinePlaceholder":84},[67,3594,3595,3597,3599,3601,3603,3605],{"class":69,"data-line":296,"line":296},[67,3596,106],{"class":105},[67,3598,263],{"class":73},[67,3600,2828],{"class":94},[67,3602,2705],{"class":77},[67,3604,749],{"class":94},[67,3606,2835],{"class":77},[67,3608,3609,3611,3613,3615,3617,3619,3621,3623,3625,3628,3630,3632,3634,3636,3638],{"class":69,"data-line":312,"line":312},[67,3610,106],{"class":105},[67,3612,622],{"class":73},[67,3614,874],{"class":94},[67,3616,628],{"class":73},[67,3618,2848],{"class":94},[67,3620,2866],{"class":77},[67,3622,286],{"class":309},[67,3624,743],{"class":73},[67,3626,3627],{"class":94},"user, userIdVal",[67,3629,289],{"class":309},[67,3631,2878],{"class":94},[67,3633,646],{"class":73},[67,3635,879],{"class":559},[67,3637,95],{"class":94},[67,3639,144],{"class":309},[67,3641,3642,3644,3646,3648,3650,3652,3655,3657,3659,3661,3663,3665,3667,3670,3672],{"class":69,"data-line":342,"line":342},[67,3643,106],{"class":105},[67,3645,106],{"class":105},[67,3647,1988],{"class":94},[67,3649,1991],{"class":77},[67,3651,286],{"class":325},[67,3653,3654],{"class":94},"http.StatusNotFound, ",[67,3656,1896],{"class":77},[67,3658,749],{"class":94},[67,3660,2003],{"class":77},[67,3662,1645],{"class":98},[67,3664,2008],{"class":109},[67,3666,2011],{"class":94},[67,3668,3669],{"class":109},"\"用户不存在\"",[67,3671,814],{"class":98},[67,3673,122],{"class":325},[67,3675,3676,3678,3680],{"class":69,"data-line":368,"line":368},[67,3677,106],{"class":105},[67,3679,106],{"class":105},[67,3681,2046],{"class":73},[67,3683,3684,3686,3688],{"class":69,"data-line":394,"line":394},[67,3685,106],{"class":105},[67,3687,62],{"class":94},[67,3689,252],{"class":309},[67,3691,3692],{"class":69,"data-line":420,"line":420},[67,3693,85],{"emptyLinePlaceholder":84},[67,3695,3696,3698],{"class":69,"data-line":446,"line":446},[67,3697,106],{"class":105},[67,3699,3700],{"class":827},"\u002F\u002F 将user置入redis\n",[67,3702,3703,3705,3708,3710,3712,3715,3717,3719],{"class":69,"data-line":472,"line":472},[67,3704,106],{"class":105},[67,3706,3707],{"class":94},"userBytes, err ",[67,3709,628],{"class":73},[67,3711,3395],{"class":94},[67,3713,3714],{"class":77},"Marshal",[67,3716,286],{"class":309},[67,3718,2873],{"class":94},[67,3720,122],{"class":309},[67,3722,3723,3725],{"class":69,"data-line":497,"line":497},[67,3724,106],{"class":105},[67,3726,3727],{"class":827},"\u002F\u002F ... 省略错误处理 ...\n",[67,3729,3730,3732,3734,3736,3738,3740,3742,3744,3746,3748,3750,3752,3754,3756,3758,3760,3763,3765,3767,3770,3772,3774,3776,3778,3780,3782],{"class":69,"data-line":523,"line":523},[67,3731,106],{"class":105},[67,3733,622],{"class":73},[67,3735,874],{"class":94},[67,3737,628],{"class":73},[67,3739,3313],{"class":94},[67,3741,2396],{"class":77},[67,3743,286],{"class":309},[67,3745,3320],{"class":94},[67,3747,766],{"class":77},[67,3749,286],{"class":325},[67,3751,3327],{"class":109},[67,3753,3330],{"class":559},[67,3755,110],{"class":109},[67,3757,3335],{"class":94},[67,3759,289],{"class":325},[67,3761,3762],{"class":94},", userBytes, time.Minute",[67,3764,289],{"class":309},[67,3766,749],{"class":94},[67,3768,3769],{"class":77},"Err",[67,3771,286],{"class":309},[67,3773,289],{"class":309},[67,3775,2737],{"class":94},[67,3777,646],{"class":73},[67,3779,879],{"class":559},[67,3781,95],{"class":94},[67,3783,144],{"class":309},[67,3785,3786,3788,3790],{"class":69,"data-line":549,"line":549},[67,3787,106],{"class":105},[67,3789,106],{"class":105},[67,3791,3792],{"class":827},"\u002F\u002F ... 错误处理 ...\n",[67,3794,3795,3797,3799],{"class":69,"data-line":565,"line":565},[67,3796,106],{"class":105},[67,3798,106],{"class":105},[67,3800,2046],{"class":73},[67,3802,3803,3805,3807],{"class":69,"data-line":574,"line":574},[67,3804,106],{"class":105},[67,3806,62],{"class":94},[67,3808,252],{"class":309},[67,3810,3811],{"class":69,"data-line":579,"line":579},[67,3812,85],{"emptyLinePlaceholder":84},[67,3814,3815,3817,3819,3821,3823,3825,3827,3829,3831],{"class":69,"data-line":584,"line":584},[67,3816,106],{"class":105},[67,3818,1988],{"class":94},[67,3820,1991],{"class":77},[67,3822,286],{"class":309},[67,3824,3132],{"class":94},[67,3826,1896],{"class":77},[67,3828,749],{"class":94},[67,3830,2003],{"class":77},[67,3832,144],{"class":325},[67,3834,3835,3837,3839,3841,3844,3846,3848,3850],{"class":69,"data-line":617,"line":617},[67,3836,106],{"class":105},[67,3838,106],{"class":105},[67,3840,3177],{"class":109},[67,3842,3843],{"class":94},": user.",[67,3845,3183],{"class":77},[67,3847,286],{"class":98},[67,3849,289],{"class":98},[67,3851,339],{"class":94},[67,3853,3854,3856,3858,3860],{"class":69,"data-line":656,"line":656},[67,3855,106],{"class":105},[67,3857,62],{"class":94},[67,3859,814],{"class":325},[67,3861,122],{"class":309},[67,3863,3864],{"class":69,"data-line":669,"line":669},[67,3865,252],{"class":98},[29,3867,3868],{"id":3868},"时序与数据流",[17,3870,3871],{},[20,3872,3873],{},"简图说明（登录与访问资料）：",[57,3875,3879],{"className":3876,"code":3877,"language":3878,"meta":62,"style":62},"language-mermaid shiki shiki-themes github-light github-dark","sequenceDiagram\n  autonumber\n  participant UI as 浏览器\n  participant API as Gin API\n  participant DB as MySQL\n  participant R as Redis\n\n  UI->>API: POST \u002Fapi\u002Flogin (username,password)\n  API->>DB: 查询用户 & 校验密码\n  DB-->>API: 用户信息\n  API-->>UI: 200 { token, user }\n\n  UI->>API: GET \u002Fapi\u002Fprofile (Authorization: Bearer token)\n  API->>API: 中间件校验 JWT\n  API->>R: GET userId:\u003Cid>\n  alt 命中\n    R-->>API: 用户缓存\n    API-->>UI: 200 { user }\n  else 未命中\n    API->>DB: SELECT user by id\n    DB-->>API: 用户记录\n    API->>R: SET userId:\u003Cid> TTL=60s\n    API-->>UI: 200 { user }\n  end\n","mermaid",[64,3880,3881,3886,3894,3901,3908,3915,3922,3926,3933,3940,3947,3954,3958,3965,3972,3979,3986,3994,4001,4008,4015,4022,4029,4035],{"__ignoreMap":62},[67,3882,3883],{"class":69,"data-line":70,"line":70},[67,3884,3885],{},"sequenceDiagram\n",[67,3887,3888,3891],{"class":69,"data-line":81,"line":81},[67,3889,3890],{"class":105},"  ",[67,3892,3893],{},"autonumber\n",[67,3895,3896,3898],{"class":69,"data-line":88,"line":88},[67,3897,3890],{"class":105},[67,3899,3900],{},"participant UI as 浏览器\n",[67,3902,3903,3905],{"class":69,"data-line":102,"line":102},[67,3904,3890],{"class":105},[67,3906,3907],{},"participant API as Gin API\n",[67,3909,3910,3912],{"class":69,"data-line":119,"line":119},[67,3911,3890],{"class":105},[67,3913,3914],{},"participant DB as MySQL\n",[67,3916,3917,3919],{"class":69,"data-line":125,"line":125},[67,3918,3890],{"class":105},[67,3920,3921],{},"participant R as Redis\n",[67,3923,3924],{"class":69,"data-line":130,"line":130},[67,3925,85],{"emptyLinePlaceholder":84},[67,3927,3928,3930],{"class":69,"data-line":147,"line":147},[67,3929,3890],{"class":105},[67,3931,3932],{},"UI->>API: POST \u002Fapi\u002Flogin (username,password)\n",[67,3934,3935,3937],{"class":69,"data-line":158,"line":158},[67,3936,3890],{"class":105},[67,3938,3939],{},"API->>DB: 查询用户 & 校验密码\n",[67,3941,3942,3944],{"class":69,"data-line":168,"line":168},[67,3943,3890],{"class":105},[67,3945,3946],{},"DB-->>API: 用户信息\n",[67,3948,3949,3951],{"class":69,"data-line":178,"line":178},[67,3950,3890],{"class":105},[67,3952,3953],{},"API-->>UI: 200 { token, user }\n",[67,3955,3956],{"class":69,"data-line":188,"line":188},[67,3957,85],{"emptyLinePlaceholder":84},[67,3959,3960,3962],{"class":69,"data-line":198,"line":198},[67,3961,3890],{"class":105},[67,3963,3964],{},"UI->>API: GET \u002Fapi\u002Fprofile (Authorization: Bearer token)\n",[67,3966,3967,3969],{"class":69,"data-line":208,"line":208},[67,3968,3890],{"class":105},[67,3970,3971],{},"API->>API: 中间件校验 JWT\n",[67,3973,3974,3976],{"class":69,"data-line":218,"line":218},[67,3975,3890],{"class":105},[67,3977,3978],{},"API->>R: GET userId:\u003Cid>\n",[67,3980,3981,3983],{"class":69,"data-line":228,"line":228},[67,3982,3890],{"class":105},[67,3984,3985],{},"alt 命中\n",[67,3987,3988,3991],{"class":69,"data-line":238,"line":238},[67,3989,3990],{"class":105},"    ",[67,3992,3993],{},"R-->>API: 用户缓存\n",[67,3995,3996,3998],{"class":69,"data-line":249,"line":249},[67,3997,3990],{"class":105},[67,3999,4000],{},"API-->>UI: 200 { user }\n",[67,4002,4003,4005],{"class":69,"data-line":255,"line":255},[67,4004,3890],{"class":105},[67,4006,4007],{},"else 未命中\n",[67,4009,4010,4012],{"class":69,"data-line":260,"line":260},[67,4011,3990],{"class":105},[67,4013,4014],{},"API->>DB: SELECT user by id\n",[67,4016,4017,4019],{"class":69,"data-line":272,"line":272},[67,4018,3990],{"class":105},[67,4020,4021],{},"DB-->>API: 用户记录\n",[67,4023,4024,4026],{"class":69,"data-line":277,"line":277},[67,4025,3990],{"class":105},[67,4027,4028],{},"API->>R: SET userId:\u003Cid> TTL=60s\n",[67,4030,4031,4033],{"class":69,"data-line":296,"line":296},[67,4032,3990],{"class":105},[67,4034,4000],{},[67,4036,4037,4039],{"class":69,"data-line":312,"line":312},[67,4038,3890],{"class":105},[67,4040,4041],{},"end\n",[10,4043,4044],{},"今天的教程就到这里，这只是一个demo，大概熟悉一下redis如何使用",[4046,4047,4048],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sSDpw, html code.shiki .sSDpw{--shiki-default:#005cc5;--shiki-dark:#79b8ff}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJX2D, html code.shiki .sJX2D{--shiki-default:#e36209;--shiki-dark:#ffab70}html pre.shiki code .sqkie, html code.shiki .sqkie{--shiki-default:#5a32a3;--shiki-dark:#b392f0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":62,"searchDepth":102,"depth":102,"links":4050},[4051,4052,4053,4054,4055,4056,4057,4058,4059,4060],{"id":31,"depth":81,"text":31},{"id":50,"depth":81,"text":50},{"id":693,"depth":81,"text":694},{"id":940,"depth":81,"text":940},{"id":1214,"depth":81,"text":1215},{"id":1856,"depth":81,"text":1857},{"id":2454,"depth":81,"text":2454},{"id":2649,"depth":81,"text":2650},{"id":3206,"depth":81,"text":3207},{"id":3868,"depth":81,"text":3868},[4062],"Go语言","2025-08-08","本项目基于 Gin + GORM + MySQL + Redis，实现了一个简单的用户登录注册系统，核心功能包括 JWT 鉴权与 Redis 用户缓存。用户登录成功后签发 JWT，访问受保护接口时通过中间件校验 Token，有效提升接口安全性。用户资料优先从 Redis 获取，未命中则回源数据库并写入缓存，兼顾性能与一致性，适合作为入门级全栈身份认证系统的学习案例。",false,"md",null,{"slots":4069},{},"\u002F2025\u002F08\u002Fgo-jwt-redis-demo",{"text":4072,"minutes":4073,"time":4074,"words":4075},"6 min read",5.93,355800,1186,{"title":5,"description":4064},{"loc":4070},"posts\u002F2025\u002F08\u002Fgo-jwt-redis-demo",[1281,746],"tech","mSS7rpEwMFB0IgHlGG9xv8GUz1hAKYTZ5fuJIuXEWQU",[4083,4088],{"title":4084,"path":4085,"stem":4086,"date":4087,"type":4080,"children":-1},"Go Panic 与 Recover 异常处理","\u002F2025\u002F08\u002Fgo-panic","posts\u002F2025\u002F08\u002Fgo-panic","2025-08-07",{"title":4089,"path":4090,"stem":4091,"date":4092,"type":4080,"children":-1},"Go Context 原理与使用实践","\u002F2025\u002F08\u002Fgo-context1","posts\u002F2025\u002F08\u002Fgo-context1","2025-08-20",1786555056407]