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