[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fauth":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fauth":2171,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fauth":2196},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":2167,"stem":2168,"id":2169,"extension":2170},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fauth","认证与授权","认证（Authentication）：确认「你是谁」（如 JWT、Session）。 - 授权（Authorization）：确认「你能做什么」（如 RBAC、策略检查）。","技能小册",[12,13],"nestjs","auth","2025-12-23",false,{"type":17,"value":18,"toc":2156},"minimark",[19,22,39,65,70,105,109,163,167,645,649,869,1130,1133,1486,1502,1506,1603,1943,2036,2040,2073,2082,2085,2088,2152],[20,21,8],"h1",{"id":8},[23,24,25,33],"ul",{},[26,27,28,32],"li",{},[29,30,31],"strong",{},"认证（Authentication）","：确认「你是谁」（如 JWT、Session）。",[26,34,35,38],{},[29,36,37],{},"授权（Authorization）","：确认「你能做什么」（如 RBAC、策略检查）。",[40,41,42,43,50,51,54,55,59,60,64],"p",{},"NestJS 常用 ",[44,45,49],"a",{"href":46,"rel":47},"https:\u002F\u002Fdocs.nestjs.com\u002Frecipes\u002Fpassport",[48],"nofollow","@nestjs\u002Fpassport"," + ",[29,52,53],{},"Guards"," 实现。守卫详见 ",[44,56,58],{"href":57},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fguards","guards","；架构见 ",[44,61,63],{"href":62},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fbase","基础","。",[66,67,69],"h2",{"id":68},"jwt-流程无状态-api","JWT 流程（无状态 API）",[71,72,73,88],"ol",{},[26,74,75,79,80,83,84,87],{},[76,77,78],"code",{},"POST \u002Fauth\u002Flogin","：用 ",[29,81,82],{},"LocalStrategy"," 校验用户名密码，",[76,85,86],{},"AuthService"," 签发 JWT。",[26,89,90,91,94,95,50,98,101,102,64],{},"后续请求：",[76,92,93],{},"Authorization: Bearer \u003Ctoken>","，",[29,96,97],{},"JwtStrategy",[76,99,100],{},"AuthGuard('jwt')"," 校验并将用户挂到 ",[76,103,104],{},"request.user",[106,107,108],"h3",{"id":108},"安装",[110,111,116],"pre",{"className":112,"code":113,"language":114,"meta":115,"style":115},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","pnpm add @nestjs\u002Fpassport passport passport-local passport-jwt @nestjs\u002Fjwt\npnpm add -D @types\u002Fpassport-local @types\u002Fpassport-jwt\n","bash","",[76,117,118,146],{"__ignoreMap":115},[119,120,123,127,131,134,137,140,143],"span",{"class":121,"line":122},"line",1,[119,124,126],{"class":125},"sbgvK","pnpm",[119,128,130],{"class":129},"s_sjI"," add",[119,132,133],{"class":129}," @nestjs\u002Fpassport",[119,135,136],{"class":129}," passport",[119,138,139],{"class":129}," passport-local",[119,141,142],{"class":129}," passport-jwt",[119,144,145],{"class":129}," @nestjs\u002Fjwt\n",[119,147,149,151,153,157,160],{"class":121,"line":148},2,[119,150,126],{"class":125},[119,152,130],{"class":129},[119,154,156],{"class":155},"stzsN"," -D",[119,158,159],{"class":129}," @types\u002Fpassport-local",[119,161,162],{"class":129}," @types\u002Fpassport-jwt\n",[106,164,166],{"id":165},"authservice示例","AuthService（示例）",[110,168,172],{"className":169,"code":170,"language":171,"meta":115,"style":115},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","import { Injectable, UnauthorizedException } from \"@nestjs\u002Fcommon\";\nimport { JwtService } from \"@nestjs\u002Fjwt\";\n\n@Injectable()\nexport class AuthService {\n  constructor(private jwtService: JwtService) {}\n\n  async validateUser(username: string, pass: string) {\n    const user = await this.findUser(username);\n    if (!user || user.password !== pass) {\n      throw new UnauthorizedException();\n    }\n    const { password, ...result } = user;\n    return result;\n  }\n\n  login(user: { id: number; username: string }) {\n    return {\n      access_token: this.jwtService.sign({ sub: user.id, username: user.username }),\n    };\n  }\n\n  private findUser(username: string) {\n    \u002F* 从数据库查询 *\u002F\n    return null;\n  }\n}\n","ts",[76,173,174,213,235,242,255,271,299,304,338,373,409,425,431,457,468,474,479,517,524,580,586,591,596,617,624,634,639],{"__ignoreMap":115},[119,175,176,180,184,188,191,194,197,200,204,207,210],{"class":121,"line":122},[119,177,179],{"class":178},"sVHd0","import",[119,181,183],{"class":182},"sP7_E"," {",[119,185,187],{"class":186},"su5hD"," Injectable",[119,189,190],{"class":182},",",[119,192,193],{"class":186}," UnauthorizedException",[119,195,196],{"class":182}," }",[119,198,199],{"class":178}," from",[119,201,203],{"class":202},"sjJ54"," \"",[119,205,206],{"class":129},"@nestjs\u002Fcommon",[119,208,209],{"class":202},"\"",[119,211,212],{"class":182},";\n",[119,214,215,217,219,222,224,226,228,231,233],{"class":121,"line":148},[119,216,179],{"class":178},[119,218,183],{"class":182},[119,220,221],{"class":186}," JwtService",[119,223,196],{"class":182},[119,225,199],{"class":178},[119,227,203],{"class":202},[119,229,230],{"class":129},"@nestjs\u002Fjwt",[119,232,209],{"class":202},[119,234,212],{"class":182},[119,236,238],{"class":121,"line":237},3,[119,239,241],{"emptyLinePlaceholder":240},true,"\n",[119,243,245,248,252],{"class":121,"line":244},4,[119,246,247],{"class":182},"@",[119,249,251],{"class":250},"sGLFI","Injectable",[119,253,254],{"class":186},"()\n",[119,256,258,261,265,268],{"class":121,"line":257},5,[119,259,260],{"class":178},"export",[119,262,264],{"class":263},"sbsja"," class",[119,266,267],{"class":125}," AuthService",[119,269,270],{"class":182}," {\n",[119,272,274,277,280,283,287,291,293,296],{"class":121,"line":273},6,[119,275,276],{"class":263},"  constructor",[119,278,279],{"class":182},"(",[119,281,282],{"class":263},"private",[119,284,286],{"class":285},"s99_P"," jwtService",[119,288,290],{"class":289},"smGrS",":",[119,292,221],{"class":125},[119,294,295],{"class":182},")",[119,297,298],{"class":182}," {}\n",[119,300,302],{"class":121,"line":301},7,[119,303,241],{"emptyLinePlaceholder":240},[119,305,307,310,314,316,319,321,325,327,330,332,334,336],{"class":121,"line":306},8,[119,308,309],{"class":263},"  async",[119,311,313],{"class":312},"sVXei"," validateUser",[119,315,279],{"class":182},[119,317,318],{"class":285},"username",[119,320,290],{"class":289},[119,322,324],{"class":323},"sZMiF"," string",[119,326,190],{"class":182},[119,328,329],{"class":285}," pass",[119,331,290],{"class":289},[119,333,324],{"class":323},[119,335,295],{"class":182},[119,337,270],{"class":182},[119,339,341,344,348,351,354,358,361,364,367,369,371],{"class":121,"line":340},9,[119,342,343],{"class":263},"    const",[119,345,347],{"class":346},"s_hVV"," user",[119,349,350],{"class":289}," =",[119,352,353],{"class":178}," await",[119,355,357],{"class":356},"s39Yj"," this",[119,359,360],{"class":182},".",[119,362,363],{"class":250},"findUser",[119,365,279],{"class":366},"skxfh",[119,368,318],{"class":186},[119,370,295],{"class":366},[119,372,212],{"class":182},[119,374,376,379,382,385,388,391,393,395,398,401,403,406],{"class":121,"line":375},10,[119,377,378],{"class":178},"    if",[119,380,381],{"class":366}," (",[119,383,384],{"class":289},"!",[119,386,387],{"class":186},"user",[119,389,390],{"class":289}," ||",[119,392,347],{"class":186},[119,394,360],{"class":182},[119,396,397],{"class":186},"password",[119,399,400],{"class":289}," !==",[119,402,329],{"class":186},[119,404,405],{"class":366},") ",[119,407,408],{"class":182},"{\n",[119,410,412,415,418,420,423],{"class":121,"line":411},11,[119,413,414],{"class":178},"      throw",[119,416,417],{"class":289}," new",[119,419,193],{"class":250},[119,421,422],{"class":366},"()",[119,424,212],{"class":182},[119,426,428],{"class":121,"line":427},12,[119,429,430],{"class":182},"    }\n",[119,432,434,436,438,441,443,446,449,451,453,455],{"class":121,"line":433},13,[119,435,343],{"class":263},[119,437,183],{"class":182},[119,439,440],{"class":346}," password",[119,442,190],{"class":182},[119,444,445],{"class":289}," ...",[119,447,448],{"class":346},"result",[119,450,196],{"class":182},[119,452,350],{"class":289},[119,454,347],{"class":186},[119,456,212],{"class":182},[119,458,460,463,466],{"class":121,"line":459},14,[119,461,462],{"class":178},"    return",[119,464,465],{"class":186}," result",[119,467,212],{"class":182},[119,469,471],{"class":121,"line":470},15,[119,472,473],{"class":182},"  }\n",[119,475,477],{"class":121,"line":476},16,[119,478,241],{"emptyLinePlaceholder":240},[119,480,482,485,487,489,491,493,497,499,502,505,508,510,512,515],{"class":121,"line":481},17,[119,483,484],{"class":312},"  login",[119,486,279],{"class":182},[119,488,387],{"class":285},[119,490,290],{"class":289},[119,492,183],{"class":182},[119,494,496],{"class":495},"sucvu"," id",[119,498,290],{"class":289},[119,500,501],{"class":323}," number",[119,503,504],{"class":182},";",[119,506,507],{"class":495}," username",[119,509,290],{"class":289},[119,511,324],{"class":323},[119,513,514],{"class":182}," })",[119,516,270],{"class":182},[119,518,520,522],{"class":121,"line":519},18,[119,521,462],{"class":178},[119,523,270],{"class":182},[119,525,527,530,532,534,536,539,541,544,546,549,552,554,556,558,561,563,565,567,569,571,573,575,577],{"class":121,"line":526},19,[119,528,529],{"class":366},"      access_token",[119,531,290],{"class":182},[119,533,357],{"class":356},[119,535,360],{"class":182},[119,537,538],{"class":186},"jwtService",[119,540,360],{"class":182},[119,542,543],{"class":250},"sign",[119,545,279],{"class":366},[119,547,548],{"class":182},"{",[119,550,551],{"class":366}," sub",[119,553,290],{"class":182},[119,555,347],{"class":186},[119,557,360],{"class":182},[119,559,560],{"class":186},"id",[119,562,190],{"class":182},[119,564,507],{"class":366},[119,566,290],{"class":182},[119,568,347],{"class":186},[119,570,360],{"class":182},[119,572,318],{"class":186},[119,574,196],{"class":182},[119,576,295],{"class":366},[119,578,579],{"class":182},",\n",[119,581,583],{"class":121,"line":582},20,[119,584,585],{"class":182},"    };\n",[119,587,589],{"class":121,"line":588},21,[119,590,473],{"class":182},[119,592,594],{"class":121,"line":593},22,[119,595,241],{"emptyLinePlaceholder":240},[119,597,599,602,605,607,609,611,613,615],{"class":121,"line":598},23,[119,600,601],{"class":263},"  private",[119,603,604],{"class":312}," findUser",[119,606,279],{"class":182},[119,608,318],{"class":285},[119,610,290],{"class":289},[119,612,324],{"class":323},[119,614,295],{"class":182},[119,616,270],{"class":182},[119,618,620],{"class":121,"line":619},24,[119,621,623],{"class":622},"sutJx","    \u002F* 从数据库查询 *\u002F\n",[119,625,627,629,632],{"class":121,"line":626},25,[119,628,462],{"class":178},[119,630,631],{"class":356}," null",[119,633,212],{"class":182},[119,635,637],{"class":121,"line":636},26,[119,638,473],{"class":182},[119,640,642],{"class":121,"line":641},27,[119,643,644],{"class":182},"}\n",[106,646,648],{"id":647},"localstrategy-jwtstrategy","LocalStrategy \u002F JwtStrategy",[110,650,652],{"className":169,"code":651,"language":171,"meta":115,"style":115},"\u002F\u002F local.strategy.ts\nimport { Strategy } from \"passport-local\";\nimport { PassportStrategy } from \"@nestjs\u002Fpassport\";\nimport { Injectable } from \"@nestjs\u002Fcommon\";\nimport { AuthService } from \".\u002Fauth.service\";\n\n@Injectable()\nexport class LocalStrategy extends PassportStrategy(Strategy) {\n  constructor(private authService: AuthService) {\n    super();\n  }\n  async validate(username: string, password: string) {\n    return this.authService.validateUser(username, password);\n  }\n}\n",[76,653,654,659,681,702,722,743,747,755,774,793,802,806,833,861,865],{"__ignoreMap":115},[119,655,656],{"class":121,"line":122},[119,657,658],{"class":622},"\u002F\u002F local.strategy.ts\n",[119,660,661,663,665,668,670,672,674,677,679],{"class":121,"line":148},[119,662,179],{"class":178},[119,664,183],{"class":182},[119,666,667],{"class":186}," Strategy",[119,669,196],{"class":182},[119,671,199],{"class":178},[119,673,203],{"class":202},[119,675,676],{"class":129},"passport-local",[119,678,209],{"class":202},[119,680,212],{"class":182},[119,682,683,685,687,690,692,694,696,698,700],{"class":121,"line":237},[119,684,179],{"class":178},[119,686,183],{"class":182},[119,688,689],{"class":186}," PassportStrategy",[119,691,196],{"class":182},[119,693,199],{"class":178},[119,695,203],{"class":202},[119,697,49],{"class":129},[119,699,209],{"class":202},[119,701,212],{"class":182},[119,703,704,706,708,710,712,714,716,718,720],{"class":121,"line":244},[119,705,179],{"class":178},[119,707,183],{"class":182},[119,709,187],{"class":186},[119,711,196],{"class":182},[119,713,199],{"class":178},[119,715,203],{"class":202},[119,717,206],{"class":129},[119,719,209],{"class":202},[119,721,212],{"class":182},[119,723,724,726,728,730,732,734,736,739,741],{"class":121,"line":257},[119,725,179],{"class":178},[119,727,183],{"class":182},[119,729,267],{"class":186},[119,731,196],{"class":182},[119,733,199],{"class":178},[119,735,203],{"class":202},[119,737,738],{"class":129},".\u002Fauth.service",[119,740,209],{"class":202},[119,742,212],{"class":182},[119,744,745],{"class":121,"line":273},[119,746,241],{"emptyLinePlaceholder":240},[119,748,749,751,753],{"class":121,"line":301},[119,750,247],{"class":182},[119,752,251],{"class":250},[119,754,254],{"class":186},[119,756,757,759,761,764,767,769,772],{"class":121,"line":306},[119,758,260],{"class":178},[119,760,264],{"class":263},[119,762,763],{"class":125}," LocalStrategy",[119,765,766],{"class":263}," extends",[119,768,689],{"class":250},[119,770,771],{"class":186},"(Strategy) ",[119,773,408],{"class":182},[119,775,776,778,780,782,785,787,789,791],{"class":121,"line":340},[119,777,276],{"class":263},[119,779,279],{"class":182},[119,781,282],{"class":263},[119,783,784],{"class":285}," authService",[119,786,290],{"class":289},[119,788,267],{"class":125},[119,790,295],{"class":182},[119,792,270],{"class":182},[119,794,795,798,800],{"class":121,"line":375},[119,796,797],{"class":346},"    super",[119,799,422],{"class":366},[119,801,212],{"class":182},[119,803,804],{"class":121,"line":411},[119,805,473],{"class":182},[119,807,808,810,813,815,817,819,821,823,825,827,829,831],{"class":121,"line":427},[119,809,309],{"class":263},[119,811,812],{"class":312}," validate",[119,814,279],{"class":182},[119,816,318],{"class":285},[119,818,290],{"class":289},[119,820,324],{"class":323},[119,822,190],{"class":182},[119,824,440],{"class":285},[119,826,290],{"class":289},[119,828,324],{"class":323},[119,830,295],{"class":182},[119,832,270],{"class":182},[119,834,835,837,839,841,844,846,849,851,853,855,857,859],{"class":121,"line":433},[119,836,462],{"class":178},[119,838,357],{"class":356},[119,840,360],{"class":182},[119,842,843],{"class":186},"authService",[119,845,360],{"class":182},[119,847,848],{"class":250},"validateUser",[119,850,279],{"class":366},[119,852,318],{"class":186},[119,854,190],{"class":182},[119,856,440],{"class":186},[119,858,295],{"class":366},[119,860,212],{"class":182},[119,862,863],{"class":121,"line":459},[119,864,473],{"class":182},[119,866,867],{"class":121,"line":470},[119,868,644],{"class":182},[110,870,872],{"className":169,"code":871,"language":171,"meta":115,"style":115},"\u002F\u002F jwt.strategy.ts\nimport { ExtractJwt, Strategy } from \"passport-jwt\";\nimport { PassportStrategy } from \"@nestjs\u002Fpassport\";\nimport { Injectable } from \"@nestjs\u002Fcommon\";\n\n@Injectable()\nexport class JwtStrategy extends PassportStrategy(Strategy) {\n  constructor() {\n    super({\n      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),\n      ignoreExpiration: false,\n      secretOrKey: process.env.JWT_SECRET,\n    });\n  }\n  validate(payload: { sub: number; username: string }) {\n    return { userId: payload.sub, username: payload.username };\n  }\n}\n",[76,873,874,879,905,925,945,949,957,974,982,990,1008,1021,1043,1052,1056,1088,1122,1126],{"__ignoreMap":115},[119,875,876],{"class":121,"line":122},[119,877,878],{"class":622},"\u002F\u002F jwt.strategy.ts\n",[119,880,881,883,885,888,890,892,894,896,898,901,903],{"class":121,"line":148},[119,882,179],{"class":178},[119,884,183],{"class":182},[119,886,887],{"class":186}," ExtractJwt",[119,889,190],{"class":182},[119,891,667],{"class":186},[119,893,196],{"class":182},[119,895,199],{"class":178},[119,897,203],{"class":202},[119,899,900],{"class":129},"passport-jwt",[119,902,209],{"class":202},[119,904,212],{"class":182},[119,906,907,909,911,913,915,917,919,921,923],{"class":121,"line":237},[119,908,179],{"class":178},[119,910,183],{"class":182},[119,912,689],{"class":186},[119,914,196],{"class":182},[119,916,199],{"class":178},[119,918,203],{"class":202},[119,920,49],{"class":129},[119,922,209],{"class":202},[119,924,212],{"class":182},[119,926,927,929,931,933,935,937,939,941,943],{"class":121,"line":244},[119,928,179],{"class":178},[119,930,183],{"class":182},[119,932,187],{"class":186},[119,934,196],{"class":182},[119,936,199],{"class":178},[119,938,203],{"class":202},[119,940,206],{"class":129},[119,942,209],{"class":202},[119,944,212],{"class":182},[119,946,947],{"class":121,"line":257},[119,948,241],{"emptyLinePlaceholder":240},[119,950,951,953,955],{"class":121,"line":273},[119,952,247],{"class":182},[119,954,251],{"class":250},[119,956,254],{"class":186},[119,958,959,961,963,966,968,970,972],{"class":121,"line":301},[119,960,260],{"class":178},[119,962,264],{"class":263},[119,964,965],{"class":125}," JwtStrategy",[119,967,766],{"class":263},[119,969,689],{"class":250},[119,971,771],{"class":186},[119,973,408],{"class":182},[119,975,976,978,980],{"class":121,"line":306},[119,977,276],{"class":263},[119,979,422],{"class":182},[119,981,270],{"class":182},[119,983,984,986,988],{"class":121,"line":340},[119,985,797],{"class":346},[119,987,279],{"class":366},[119,989,408],{"class":182},[119,991,992,995,997,999,1001,1004,1006],{"class":121,"line":375},[119,993,994],{"class":366},"      jwtFromRequest",[119,996,290],{"class":182},[119,998,887],{"class":186},[119,1000,360],{"class":182},[119,1002,1003],{"class":250},"fromAuthHeaderAsBearerToken",[119,1005,422],{"class":366},[119,1007,579],{"class":182},[119,1009,1010,1013,1015,1019],{"class":121,"line":411},[119,1011,1012],{"class":366},"      ignoreExpiration",[119,1014,290],{"class":182},[119,1016,1018],{"class":1017},"syTEX"," false",[119,1020,579],{"class":182},[119,1022,1023,1026,1028,1031,1033,1036,1038,1041],{"class":121,"line":427},[119,1024,1025],{"class":366},"      secretOrKey",[119,1027,290],{"class":182},[119,1029,1030],{"class":186}," process",[119,1032,360],{"class":182},[119,1034,1035],{"class":186},"env",[119,1037,360],{"class":182},[119,1039,1040],{"class":346},"JWT_SECRET",[119,1042,579],{"class":182},[119,1044,1045,1048,1050],{"class":121,"line":433},[119,1046,1047],{"class":182},"    }",[119,1049,295],{"class":366},[119,1051,212],{"class":182},[119,1053,1054],{"class":121,"line":459},[119,1055,473],{"class":182},[119,1057,1058,1061,1063,1066,1068,1070,1072,1074,1076,1078,1080,1082,1084,1086],{"class":121,"line":470},[119,1059,1060],{"class":312},"  validate",[119,1062,279],{"class":182},[119,1064,1065],{"class":285},"payload",[119,1067,290],{"class":289},[119,1069,183],{"class":182},[119,1071,551],{"class":495},[119,1073,290],{"class":289},[119,1075,501],{"class":323},[119,1077,504],{"class":182},[119,1079,507],{"class":495},[119,1081,290],{"class":289},[119,1083,324],{"class":323},[119,1085,514],{"class":182},[119,1087,270],{"class":182},[119,1089,1090,1092,1094,1097,1099,1102,1104,1107,1109,1111,1113,1115,1117,1119],{"class":121,"line":476},[119,1091,462],{"class":178},[119,1093,183],{"class":182},[119,1095,1096],{"class":366}," userId",[119,1098,290],{"class":182},[119,1100,1101],{"class":186}," payload",[119,1103,360],{"class":182},[119,1105,1106],{"class":186},"sub",[119,1108,190],{"class":182},[119,1110,507],{"class":366},[119,1112,290],{"class":182},[119,1114,1101],{"class":186},[119,1116,360],{"class":182},[119,1118,318],{"class":186},[119,1120,1121],{"class":182}," };\n",[119,1123,1124],{"class":121,"line":481},[119,1125,473],{"class":182},[119,1127,1128],{"class":121,"line":519},[119,1129,644],{"class":182},[106,1131,1132],{"id":1132},"控制器",[110,1134,1136],{"className":169,"code":1135,"language":171,"meta":115,"style":115},"import { Controller, Post, UseGuards, Request, Get } from \"@nestjs\u002Fcommon\";\nimport { AuthGuard } from \"@nestjs\u002Fpassport\";\nimport { AuthService } from \".\u002Fauth.service\";\n\n@Controller(\"auth\")\nexport class AuthController {\n  constructor(private authService: AuthService) {}\n\n  @UseGuards(AuthGuard(\"local\"))\n  @Post(\"login\")\n  login(@Request() req: { user: { id: number; username: string } }) {\n    return this.authService.login(req.user);\n  }\n\n  @UseGuards(AuthGuard(\"jwt\"))\n  @Get(\"profile\")\n  getProfile(@Request() req: { user: unknown }) {\n    return req.user;\n  }\n}\n",[76,1137,1138,1179,1200,1220,1224,1242,1253,1271,1275,1300,1318,1364,1390,1394,1398,1419,1437,1465,1478,1482],{"__ignoreMap":115},[119,1139,1140,1142,1144,1147,1149,1152,1154,1157,1159,1162,1164,1167,1169,1171,1173,1175,1177],{"class":121,"line":122},[119,1141,179],{"class":178},[119,1143,183],{"class":182},[119,1145,1146],{"class":186}," Controller",[119,1148,190],{"class":182},[119,1150,1151],{"class":186}," Post",[119,1153,190],{"class":182},[119,1155,1156],{"class":186}," UseGuards",[119,1158,190],{"class":182},[119,1160,1161],{"class":186}," Request",[119,1163,190],{"class":182},[119,1165,1166],{"class":186}," Get",[119,1168,196],{"class":182},[119,1170,199],{"class":178},[119,1172,203],{"class":202},[119,1174,206],{"class":129},[119,1176,209],{"class":202},[119,1178,212],{"class":182},[119,1180,1181,1183,1185,1188,1190,1192,1194,1196,1198],{"class":121,"line":148},[119,1182,179],{"class":178},[119,1184,183],{"class":182},[119,1186,1187],{"class":186}," AuthGuard",[119,1189,196],{"class":182},[119,1191,199],{"class":178},[119,1193,203],{"class":202},[119,1195,49],{"class":129},[119,1197,209],{"class":202},[119,1199,212],{"class":182},[119,1201,1202,1204,1206,1208,1210,1212,1214,1216,1218],{"class":121,"line":237},[119,1203,179],{"class":178},[119,1205,183],{"class":182},[119,1207,267],{"class":186},[119,1209,196],{"class":182},[119,1211,199],{"class":178},[119,1213,203],{"class":202},[119,1215,738],{"class":129},[119,1217,209],{"class":202},[119,1219,212],{"class":182},[119,1221,1222],{"class":121,"line":244},[119,1223,241],{"emptyLinePlaceholder":240},[119,1225,1226,1228,1231,1233,1235,1237,1239],{"class":121,"line":257},[119,1227,247],{"class":182},[119,1229,1230],{"class":250},"Controller",[119,1232,279],{"class":186},[119,1234,209],{"class":202},[119,1236,13],{"class":129},[119,1238,209],{"class":202},[119,1240,1241],{"class":186},")\n",[119,1243,1244,1246,1248,1251],{"class":121,"line":273},[119,1245,260],{"class":178},[119,1247,264],{"class":263},[119,1249,1250],{"class":125}," AuthController",[119,1252,270],{"class":182},[119,1254,1255,1257,1259,1261,1263,1265,1267,1269],{"class":121,"line":301},[119,1256,276],{"class":263},[119,1258,279],{"class":182},[119,1260,282],{"class":263},[119,1262,784],{"class":285},[119,1264,290],{"class":289},[119,1266,267],{"class":125},[119,1268,295],{"class":182},[119,1270,298],{"class":182},[119,1272,1273],{"class":121,"line":306},[119,1274,241],{"emptyLinePlaceholder":240},[119,1276,1277,1280,1283,1285,1288,1290,1292,1295,1297],{"class":121,"line":340},[119,1278,1279],{"class":182},"  @",[119,1281,1282],{"class":250},"UseGuards",[119,1284,279],{"class":186},[119,1286,1287],{"class":250},"AuthGuard",[119,1289,279],{"class":186},[119,1291,209],{"class":202},[119,1293,1294],{"class":129},"local",[119,1296,209],{"class":202},[119,1298,1299],{"class":186},"))\n",[119,1301,1302,1304,1307,1309,1311,1314,1316],{"class":121,"line":375},[119,1303,1279],{"class":182},[119,1305,1306],{"class":250},"Post",[119,1308,279],{"class":186},[119,1310,209],{"class":202},[119,1312,1313],{"class":129},"login",[119,1315,209],{"class":202},[119,1317,1241],{"class":186},[119,1319,1320,1322,1325,1328,1331,1334,1336,1338,1340,1342,1344,1346,1348,1350,1352,1354,1356,1358,1360,1362],{"class":121,"line":411},[119,1321,484],{"class":312},[119,1323,1324],{"class":182},"(@",[119,1326,1327],{"class":250},"Request",[119,1329,1330],{"class":186},"() ",[119,1332,1333],{"class":285},"req",[119,1335,290],{"class":289},[119,1337,183],{"class":182},[119,1339,347],{"class":495},[119,1341,290],{"class":289},[119,1343,183],{"class":182},[119,1345,496],{"class":495},[119,1347,290],{"class":289},[119,1349,501],{"class":323},[119,1351,504],{"class":182},[119,1353,507],{"class":495},[119,1355,290],{"class":289},[119,1357,324],{"class":323},[119,1359,196],{"class":182},[119,1361,514],{"class":182},[119,1363,270],{"class":182},[119,1365,1366,1368,1370,1372,1374,1376,1378,1380,1382,1384,1386,1388],{"class":121,"line":427},[119,1367,462],{"class":178},[119,1369,357],{"class":356},[119,1371,360],{"class":182},[119,1373,843],{"class":186},[119,1375,360],{"class":182},[119,1377,1313],{"class":250},[119,1379,279],{"class":366},[119,1381,1333],{"class":186},[119,1383,360],{"class":182},[119,1385,387],{"class":186},[119,1387,295],{"class":366},[119,1389,212],{"class":182},[119,1391,1392],{"class":121,"line":433},[119,1393,473],{"class":182},[119,1395,1396],{"class":121,"line":459},[119,1397,241],{"emptyLinePlaceholder":240},[119,1399,1400,1402,1404,1406,1408,1410,1412,1415,1417],{"class":121,"line":470},[119,1401,1279],{"class":182},[119,1403,1282],{"class":250},[119,1405,279],{"class":186},[119,1407,1287],{"class":250},[119,1409,279],{"class":186},[119,1411,209],{"class":202},[119,1413,1414],{"class":129},"jwt",[119,1416,209],{"class":202},[119,1418,1299],{"class":186},[119,1420,1421,1423,1426,1428,1430,1433,1435],{"class":121,"line":476},[119,1422,1279],{"class":182},[119,1424,1425],{"class":250},"Get",[119,1427,279],{"class":186},[119,1429,209],{"class":202},[119,1431,1432],{"class":129},"profile",[119,1434,209],{"class":202},[119,1436,1241],{"class":186},[119,1438,1439,1442,1444,1446,1448,1450,1452,1454,1456,1458,1461,1463],{"class":121,"line":481},[119,1440,1441],{"class":312},"  getProfile",[119,1443,1324],{"class":182},[119,1445,1327],{"class":250},[119,1447,1330],{"class":186},[119,1449,1333],{"class":285},[119,1451,290],{"class":289},[119,1453,183],{"class":182},[119,1455,347],{"class":495},[119,1457,290],{"class":289},[119,1459,1460],{"class":323}," unknown",[119,1462,514],{"class":182},[119,1464,270],{"class":182},[119,1466,1467,1469,1472,1474,1476],{"class":121,"line":519},[119,1468,462],{"class":178},[119,1470,1471],{"class":186}," req",[119,1473,360],{"class":182},[119,1475,387],{"class":186},[119,1477,212],{"class":182},[119,1479,1480],{"class":121,"line":526},[119,1481,473],{"class":182},[119,1483,1484],{"class":121,"line":582},[119,1485,644],{"class":182},[40,1487,1488,1489,1492,1493,1496,1497,64],{},"在 ",[76,1490,1491],{},"AuthModule"," 中注册 ",[76,1494,1495],{},"JwtModule.register({ secret, signOptions: { expiresIn: '1d' } })"," 与上述 Provider。完整示例见官方 ",[44,1498,1501],{"href":1499,"rel":1500},"https:\u002F\u002Fdocs.nestjs.com\u002Fsecurity\u002Fauthentication",[48],"Authentication",[66,1503,1505],{"id":1504},"rbac角色授权","RBAC（角色授权）",[110,1507,1509],{"className":169,"code":1508,"language":171,"meta":115,"style":115},"\u002F\u002F roles.decorator.ts\nimport { SetMetadata } from \"@nestjs\u002Fcommon\";\nexport const ROLES_KEY = \"roles\";\nexport const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);\n",[76,1510,1511,1516,1537,1558],{"__ignoreMap":115},[119,1512,1513],{"class":121,"line":122},[119,1514,1515],{"class":622},"\u002F\u002F roles.decorator.ts\n",[119,1517,1518,1520,1522,1525,1527,1529,1531,1533,1535],{"class":121,"line":148},[119,1519,179],{"class":178},[119,1521,183],{"class":182},[119,1523,1524],{"class":186}," SetMetadata",[119,1526,196],{"class":182},[119,1528,199],{"class":178},[119,1530,203],{"class":202},[119,1532,206],{"class":129},[119,1534,209],{"class":202},[119,1536,212],{"class":182},[119,1538,1539,1541,1544,1547,1549,1551,1554,1556],{"class":121,"line":237},[119,1540,260],{"class":178},[119,1542,1543],{"class":263}," const",[119,1545,1546],{"class":346}," ROLES_KEY",[119,1548,350],{"class":289},[119,1550,203],{"class":202},[119,1552,1553],{"class":129},"roles",[119,1555,209],{"class":202},[119,1557,212],{"class":182},[119,1559,1560,1562,1564,1568,1570,1572,1575,1577,1579,1581,1584,1586,1589,1591,1593,1596,1598,1601],{"class":121,"line":244},[119,1561,260],{"class":178},[119,1563,1543],{"class":263},[119,1565,1567],{"class":1566},"sfCm-"," Roles",[119,1569,350],{"class":289},[119,1571,381],{"class":182},[119,1573,1574],{"class":289},"...",[119,1576,1553],{"class":285},[119,1578,290],{"class":289},[119,1580,324],{"class":323},[119,1582,1583],{"class":186},"[]",[119,1585,295],{"class":182},[119,1587,1588],{"class":263}," =>",[119,1590,1524],{"class":250},[119,1592,279],{"class":186},[119,1594,1595],{"class":346},"ROLES_KEY",[119,1597,190],{"class":182},[119,1599,1600],{"class":186}," roles)",[119,1602,212],{"class":182},[110,1604,1606],{"className":169,"code":1605,"language":171,"meta":115,"style":115},"\u002F\u002F roles.guard.ts\nimport { CanActivate, ExecutionContext, Injectable } from \"@nestjs\u002Fcommon\";\nimport { Reflector } from \"@nestjs\u002Fcore\";\nimport { ROLES_KEY } from \".\u002Froles.decorator\";\n\n@Injectable()\nexport class RolesGuard implements CanActivate {\n  constructor(private reflector: Reflector) {}\n\n  canActivate(context: ExecutionContext): boolean {\n    const required = this.reflector.getAllAndOverride\u003Cstring[]>(ROLES_KEY, [\n      context.getHandler(),\n      context.getClass(),\n    ]);\n    if (!required?.length) return true;\n    const { user } = context.switchToHttp().getRequest();\n    return required.some((role) => user?.roles?.includes(role));\n  }\n}\n",[76,1607,1608,1613,1643,1665,1686,1690,1698,1714,1733,1737,1760,1801,1815,1828,1835,1862,1893,1935,1939],{"__ignoreMap":115},[119,1609,1610],{"class":121,"line":122},[119,1611,1612],{"class":622},"\u002F\u002F roles.guard.ts\n",[119,1614,1615,1617,1619,1622,1624,1627,1629,1631,1633,1635,1637,1639,1641],{"class":121,"line":148},[119,1616,179],{"class":178},[119,1618,183],{"class":182},[119,1620,1621],{"class":186}," CanActivate",[119,1623,190],{"class":182},[119,1625,1626],{"class":186}," ExecutionContext",[119,1628,190],{"class":182},[119,1630,187],{"class":186},[119,1632,196],{"class":182},[119,1634,199],{"class":178},[119,1636,203],{"class":202},[119,1638,206],{"class":129},[119,1640,209],{"class":202},[119,1642,212],{"class":182},[119,1644,1645,1647,1649,1652,1654,1656,1658,1661,1663],{"class":121,"line":237},[119,1646,179],{"class":178},[119,1648,183],{"class":182},[119,1650,1651],{"class":186}," Reflector",[119,1653,196],{"class":182},[119,1655,199],{"class":178},[119,1657,203],{"class":202},[119,1659,1660],{"class":129},"@nestjs\u002Fcore",[119,1662,209],{"class":202},[119,1664,212],{"class":182},[119,1666,1667,1669,1671,1673,1675,1677,1679,1682,1684],{"class":121,"line":244},[119,1668,179],{"class":178},[119,1670,183],{"class":182},[119,1672,1546],{"class":186},[119,1674,196],{"class":182},[119,1676,199],{"class":178},[119,1678,203],{"class":202},[119,1680,1681],{"class":129},".\u002Froles.decorator",[119,1683,209],{"class":202},[119,1685,212],{"class":182},[119,1687,1688],{"class":121,"line":257},[119,1689,241],{"emptyLinePlaceholder":240},[119,1691,1692,1694,1696],{"class":121,"line":273},[119,1693,247],{"class":182},[119,1695,251],{"class":250},[119,1697,254],{"class":186},[119,1699,1700,1702,1704,1707,1710,1712],{"class":121,"line":301},[119,1701,260],{"class":178},[119,1703,264],{"class":263},[119,1705,1706],{"class":125}," RolesGuard",[119,1708,1709],{"class":263}," implements",[119,1711,1621],{"class":125},[119,1713,270],{"class":182},[119,1715,1716,1718,1720,1722,1725,1727,1729,1731],{"class":121,"line":306},[119,1717,276],{"class":263},[119,1719,279],{"class":182},[119,1721,282],{"class":263},[119,1723,1724],{"class":285}," reflector",[119,1726,290],{"class":289},[119,1728,1651],{"class":125},[119,1730,295],{"class":182},[119,1732,298],{"class":182},[119,1734,1735],{"class":121,"line":340},[119,1736,241],{"emptyLinePlaceholder":240},[119,1738,1739,1742,1744,1747,1749,1751,1753,1755,1758],{"class":121,"line":375},[119,1740,1741],{"class":312},"  canActivate",[119,1743,279],{"class":182},[119,1745,1746],{"class":285},"context",[119,1748,290],{"class":289},[119,1750,1626],{"class":125},[119,1752,295],{"class":182},[119,1754,290],{"class":289},[119,1756,1757],{"class":323}," boolean",[119,1759,270],{"class":182},[119,1761,1762,1764,1767,1769,1771,1773,1776,1778,1781,1784,1787,1789,1792,1794,1796,1798],{"class":121,"line":411},[119,1763,343],{"class":263},[119,1765,1766],{"class":346}," required",[119,1768,350],{"class":289},[119,1770,357],{"class":356},[119,1772,360],{"class":182},[119,1774,1775],{"class":186},"reflector",[119,1777,360],{"class":182},[119,1779,1780],{"class":250},"getAllAndOverride",[119,1782,1783],{"class":182},"\u003C",[119,1785,1786],{"class":323},"string",[119,1788,1583],{"class":366},[119,1790,1791],{"class":182},">",[119,1793,279],{"class":366},[119,1795,1595],{"class":346},[119,1797,190],{"class":182},[119,1799,1800],{"class":366}," [\n",[119,1802,1803,1806,1808,1811,1813],{"class":121,"line":427},[119,1804,1805],{"class":186},"      context",[119,1807,360],{"class":182},[119,1809,1810],{"class":250},"getHandler",[119,1812,422],{"class":366},[119,1814,579],{"class":182},[119,1816,1817,1819,1821,1824,1826],{"class":121,"line":433},[119,1818,1805],{"class":186},[119,1820,360],{"class":182},[119,1822,1823],{"class":250},"getClass",[119,1825,422],{"class":366},[119,1827,579],{"class":182},[119,1829,1830,1833],{"class":121,"line":459},[119,1831,1832],{"class":366},"    ])",[119,1834,212],{"class":182},[119,1836,1837,1839,1841,1843,1846,1849,1852,1854,1857,1860],{"class":121,"line":470},[119,1838,378],{"class":178},[119,1840,381],{"class":366},[119,1842,384],{"class":289},[119,1844,1845],{"class":186},"required",[119,1847,1848],{"class":182},"?.",[119,1850,1851],{"class":346},"length",[119,1853,405],{"class":366},[119,1855,1856],{"class":178},"return",[119,1858,1859],{"class":1017}," true",[119,1861,212],{"class":182},[119,1863,1864,1866,1868,1870,1872,1874,1877,1879,1882,1884,1886,1889,1891],{"class":121,"line":476},[119,1865,343],{"class":263},[119,1867,183],{"class":182},[119,1869,347],{"class":346},[119,1871,196],{"class":182},[119,1873,350],{"class":289},[119,1875,1876],{"class":186}," context",[119,1878,360],{"class":182},[119,1880,1881],{"class":250},"switchToHttp",[119,1883,422],{"class":366},[119,1885,360],{"class":182},[119,1887,1888],{"class":250},"getRequest",[119,1890,422],{"class":366},[119,1892,212],{"class":182},[119,1894,1895,1897,1899,1901,1904,1906,1908,1911,1913,1915,1917,1919,1921,1923,1926,1928,1930,1933],{"class":121,"line":481},[119,1896,462],{"class":178},[119,1898,1766],{"class":186},[119,1900,360],{"class":182},[119,1902,1903],{"class":250},"some",[119,1905,279],{"class":366},[119,1907,279],{"class":182},[119,1909,1910],{"class":285},"role",[119,1912,295],{"class":182},[119,1914,1588],{"class":263},[119,1916,347],{"class":186},[119,1918,1848],{"class":182},[119,1920,1553],{"class":186},[119,1922,1848],{"class":182},[119,1924,1925],{"class":250},"includes",[119,1927,279],{"class":366},[119,1929,1910],{"class":186},[119,1931,1932],{"class":366},"))",[119,1934,212],{"class":182},[119,1936,1937],{"class":121,"line":519},[119,1938,473],{"class":182},[119,1940,1941],{"class":121,"line":526},[119,1942,644],{"class":182},[110,1944,1946],{"className":169,"code":1945,"language":171,"meta":115,"style":115},"@Roles(\"admin\")\n@UseGuards(AuthGuard(\"jwt\"), RolesGuard)\n@Get(\"admin\")\nadminOnly() {\n  return { ok: true };\n}\n",[76,1947,1948,1966,1991,2007,2016,2032],{"__ignoreMap":115},[119,1949,1950,1952,1955,1957,1959,1962,1964],{"class":121,"line":122},[119,1951,247],{"class":182},[119,1953,1954],{"class":250},"Roles",[119,1956,279],{"class":186},[119,1958,209],{"class":202},[119,1960,1961],{"class":129},"admin",[119,1963,209],{"class":202},[119,1965,1241],{"class":186},[119,1967,1968,1970,1972,1974,1976,1978,1980,1982,1984,1986,1988],{"class":121,"line":148},[119,1969,247],{"class":182},[119,1971,1282],{"class":250},[119,1973,279],{"class":186},[119,1975,1287],{"class":250},[119,1977,279],{"class":186},[119,1979,209],{"class":202},[119,1981,1414],{"class":129},[119,1983,209],{"class":202},[119,1985,295],{"class":186},[119,1987,190],{"class":182},[119,1989,1990],{"class":186}," RolesGuard)\n",[119,1992,1993,1995,1997,1999,2001,2003,2005],{"class":121,"line":237},[119,1994,247],{"class":182},[119,1996,1425],{"class":250},[119,1998,279],{"class":186},[119,2000,209],{"class":202},[119,2002,1961],{"class":129},[119,2004,209],{"class":202},[119,2006,1241],{"class":186},[119,2008,2009,2012,2014],{"class":121,"line":244},[119,2010,2011],{"class":250},"adminOnly",[119,2013,1330],{"class":186},[119,2015,408],{"class":182},[119,2017,2018,2021,2023,2026,2028,2030],{"class":121,"line":257},[119,2019,2020],{"class":178},"  return",[119,2022,183],{"class":182},[119,2024,2025],{"class":366}," ok",[119,2027,290],{"class":182},[119,2029,1859],{"class":1017},[119,2031,1121],{"class":182},[119,2033,2034],{"class":121,"line":273},[119,2035,644],{"class":182},[66,2037,2039],{"id":2038},"session-与-oauth","Session 与 OAuth",[23,2041,2042,2059],{},[26,2043,2044,2047,2048,2053,2054,2058],{},[29,2045,2046],{},"Session","：在 Express 上使用 ",[44,2049,2052],{"href":2050,"rel":2051},"https:\u002F\u002Fgithub.com\u002Fexpressjs\u002Fsession",[48],"express-session"," 中间件（见 ",[44,2055,2057],{"href":2056},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fmiddleware","middleware","）；有状态，适合传统 SSR。",[26,2060,2061,2064,2065,2068,2069,64],{},[29,2062,2063],{},"OAuth2","：通过 Passport 策略（如 ",[76,2066,2067],{},"passport-google-oauth20","），见官方 ",[44,2070,2072],{"href":46,"rel":2071},[48],"Passport recipes",[40,2074,2075,2076,2081],{},"细粒度规则可考虑 ",[44,2077,2080],{"href":2078,"rel":2079},"https:\u002F\u002Fcasl.js.org\u002F",[48],"CASL","，按资源与动作建模能力。",[66,2083,2084],{"id":2084},"参考文献",[40,2086,2087],{},"以下链接在编写时均可正常访问：",[2089,2090,2091,2104],"table",{},[2092,2093,2094],"thead",{},[2095,2096,2097,2101],"tr",{},[2098,2099,2100],"th",{},"资料",[2098,2102,2103],{},"说明",[2105,2106,2107,2118,2130,2141],"tbody",{},[2095,2108,2109,2115],{},[2110,2111,2112],"td",{},[44,2113,1501],{"href":1499,"rel":2114},[48],[2110,2116,2117],{},"JWT 官方教程",[2095,2119,2120,2127],{},[2110,2121,2122],{},[44,2123,2126],{"href":2124,"rel":2125},"https:\u002F\u002Fdocs.nestjs.com\u002Fsecurity\u002Fauthorization",[48],"Authorization",[2110,2128,2129],{},"授权",[2095,2131,2132,2138],{},[2110,2133,2134],{},[44,2135,2137],{"href":46,"rel":2136},[48],"Passport",[2110,2139,2140],{},"策略集成",[2095,2142,2143,2149],{},[2110,2144,2145],{},[44,2146,53],{"href":2147,"rel":2148},"https:\u002F\u002Fdocs.nestjs.com\u002Fguards",[48],[2110,2150,2151],{},"守卫",[2153,2154,2155],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sucvu, html code.shiki .sucvu{--shiki-light:#E53935;--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sfCm-, html code.shiki .sfCm-{--shiki-light:#90A4AE;--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":115,"searchDepth":148,"depth":148,"links":2157},[2158,2164,2165,2166],{"id":68,"depth":148,"text":69,"children":2159},[2160,2161,2162,2163],{"id":108,"depth":237,"text":108},{"id":165,"depth":237,"text":166},{"id":647,"depth":237,"text":648},{"id":1132,"depth":237,"text":1132},{"id":1504,"depth":148,"text":1505},{"id":2038,"depth":148,"text":2039},{"id":2084,"depth":148,"text":2084},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fend\u002FNestJS\u002Fnew\u002Fauth","knowledge\u002Fknowledge\u002Fbooklet\u002Fend\u002FNestJS\u002Fnew\u002Fauth.md","md",{"items":2172},[2173,2177,2181,2184,2188,2192],{"path":2174,"title":2175,"description":2176,"category":10,"score":273},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcache","缓存","@nestjs\u002Fcache-manager 统一缓存 API，存储实现可插拔。",{"path":2178,"title":2179,"description":2180,"category":10,"score":273},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fservice","提供者与服务","Service 是最常见的 Provider，封装业务逻辑。见 module。",{"path":57,"title":2182,"description":2183,"category":10,"score":273},"守卫 (Guards) 与授权","守卫决定是否放行请求，常用于认证与授权。见 auth。",{"path":2185,"title":2186,"description":2187,"category":10,"score":273},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fconfig","配置管理（Config 模块）","@nestjs\u002Fconfig 加载 .env 并提供 ConfigService。见 工程化 env。",{"path":2189,"title":2190,"description":2191,"category":10,"score":273},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fdatabase","数据库集成（以 TypeORM 为例）","Nest 通过 @nestjs\u002Ftypeorm 等包集成 ORM；生产环境用 migration，慎用 synchronize。亦可选用 Prisma、MikroORM 等（见 官方 Database）。",{"path":2193,"title":2194,"description":2195,"category":10,"score":273},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcron","定时任务","@nestjs\u002Fschedule 基于 cron 表达式调度任务。",{"series":2197,"title":2198,"items":2199,"index":2258,"prev":2259,"next":2260},"booklet\u002Fend\u002Fnestjs\u002Fnew","new",[2200,2201,2204,2206,2208,2212,2214,2218,2220,2224,2228,2230,2234,2238,2241,2245,2248,2252,2254],{"path":7,"title":8,"date":14,"pinned":15},{"path":62,"title":2202,"date":2203,"pinned":15},"NestJS 基础","2021-03-09",{"path":2174,"title":2175,"date":2205,"pinned":15},"2025-07-04",{"path":2185,"title":2186,"date":2207,"pinned":15},"2023-08-26",{"path":2209,"title":2210,"date":2211,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcontroller","控制器与路由","2020-07-18",{"path":2193,"title":2194,"date":2213,"pinned":15},"2023-04-12",{"path":2215,"title":2216,"date":2217,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcustom-decorators","自定义装饰器","2021-09-19",{"path":2189,"title":2190,"date":2219,"pinned":15},"2023-05-26",{"path":2221,"title":2222,"date":2223,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fdeploy","部署与运维","2023-01-24",{"path":2225,"title":2226,"date":2227,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Ferror","异常处理","2020-01-28",{"path":57,"title":2182,"date":2229,"pinned":15},"2023-09-09",{"path":2231,"title":2232,"date":2233,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fintercept","拦截器 (Interceptors)","2021-04-26",{"path":2235,"title":2236,"date":2237,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Flogger","日志记录","2022-10-10",{"path":2056,"title":2239,"date":2240,"pinned":15},"中间件 (Middleware)","2021-11-26",{"path":2242,"title":2243,"date":2244,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fmodule","模块系统与依赖注入","2022-06-15",{"path":2246,"title":2247,"date":2211,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fpipes","管道 (Pipes) 与数据验证",{"path":2249,"title":2250,"date":2251,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fqueue","队列（Bull）","2021-07-01",{"path":2178,"title":2179,"date":2253,"pinned":15},"2024-12-02",{"path":2255,"title":2256,"date":2257,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fwebsocket","WebSocket（网关）","2022-12-14",0,null,{"path":62,"title":2202,"date":2203,"pinned":15}]