9横蜒、Nest.js 中的看守器

什么是看守器(Guard)谅阿?

看守器就是使用 @Injectable 修飾并且實(shí)現(xiàn)了 CanActivate 接口的類。
一般使用看守器來(lái)做接口權(quán)限的驗(yàn)證溉苛,比如驗(yàn)證請(qǐng)求是否包含 token 或者 token 是否過(guò)期镜廉。

首先需要?jiǎng)?chuàng)建一個(gè)基本的看守器 roles.guard.ts

src/users/guards/roles.guard.ts

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class RolesGuard implements CanActivate {

    constructor() { }

    canActivate(
        context: ExecutionContext,
    ): boolean | Promise<boolean> | Observable<boolean> {

        return true;
    }
}

這個(gè)看守器沒有任何邏輯,只是簡(jiǎn)單的返回 true愚战,表示認(rèn)證通過(guò)娇唯。
我們預(yù)期的效果是像下面這樣,在 action 方法上附加一個(gè) 裝飾器 表示當(dāng)前 action 需要認(rèn)證才可以訪問:

    @Get('info')
    @Roles('user')
    async info() {

    }

自定義一個(gè)裝飾器:

src/users/decorators/common.decorator.ts

import { ReflectMetadata } from '@nestjs/common';

export const Roles = (...roles: string[]) => ReflectMetadata('roles', roles);

這個(gè)裝飾器接收一個(gè)字符串?dāng)?shù)組寂玲, 作為需要被認(rèn)證的角色列表塔插,并且將其附加到元數(shù)據(jù)上,以便在看守器中可以通過(guò)反射元數(shù)據(jù)獲取到角色列表然后一一驗(yàn)證拓哟。

src/users/guards/roles.guard.ts

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {

    constructor(
        private readonly reflector: Reflector
    ) { }

    canActivate(
        context: ExecutionContext,
    ): boolean | Promise<boolean> | Observable<boolean> {

        const request = context.switchToHttp().getRequest();       

        const roles = this.reflector.get<string[]>('roles', context.getHandler());

        if (roles && roles.length > 0) {

            // 需要校驗(yàn)用戶權(quán)限
            if(roles.some(item =>  'user' == item)) {
                
                return request.query.token || request.body.token;
                
            }

        }

        return true;
    }
}

修改看守器的邏輯想许,通過(guò)反射我們可以獲取到在裝飾器中定義的 roles 數(shù)組,然后判斷是否有 user 這個(gè)角色需要被驗(yàn)證断序,我們將采用當(dāng)前比較流行的 JWT 驗(yàn)證方式流纹,所以校驗(yàn)此次請(qǐng)求必須包含 token 字段, 否則驗(yàn)證失敗违诗。
如果我們?cè)诳词仄髦蟹祷?false 漱凝, Nest 會(huì)拋出一個(gè) HttpException 異常, 我們也可以拋出自定義的異常诸迟,然后用過(guò)濾器捕獲它茸炒。

我們已經(jīng)準(zhǔn)備好了看守器,現(xiàn)在需要一套完整的用戶體系阵苇,這里推薦大家使用 國(guó)內(nèi)領(lǐng)先的身份認(rèn)證云服務(wù)
Authing 壁公,只需要花 5 分鐘就可以擁有一個(gè)完整的用戶系統(tǒng)。(注:不是打廣告慎玖,現(xiàn)在不都講究 CloudNative 云原生嗎贮尖? 我們的宗旨就是笛粘,能用云服務(wù)搞定的趁怔,絕不自己瞎折騰)
安裝 Authing 的 SDK:

$ npm install authing-js-sdk --save

安裝 官方的 Express 中間件:

$ npm install express-authing --save

目前好像沒有對(duì) TypeScript 的類型支持,沒有類型支持也沒有關(guān)系薪前, Authing的API非常簡(jiǎn)單易用润努,對(duì)人類友好的代碼,才是優(yōu)秀的代碼示括。

$ npm install @types/express-authing --save-dev
npm ERR! code E404
npm ERR! 404 Not Found: @types/express-authing@latest

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/lin/.npm/_logs/2018-08-25T05_05_10_335Z-debug.log

在 main.ts 中使用 Authing 和 RolesGuard:

src/main.ts

import { NestFactory, Reflector } from '@nestjs/core';
import { AppModule } from 'app.module';
import { HttpExceptionFilter } from 'common/filters/http-exception.filter';
import { ApiParamsValidationPipe } from 'common/pipes/api-params-validation.pipe';
import * as Authing from 'express-authing';
import { RolesGuard } from 'users/guards/roles.guard';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  app.useGlobalPipes(new ApiParamsValidationPipe());
  app.useGlobalGuards(new RolesGuard(new Reflector()));

  app.use(Authing({
    clientId: 'xxxxxx',
    secret: 'xxxxx'
  }));

  await app.listen(3000);
}
bootstrap();

這里的 clientId 和 secret 需要去 Authing 官網(wǎng)注冊(cè)铺浇。 為了簡(jiǎn)單這里直接硬編碼了,后面會(huì)介紹如何創(chuàng)建一個(gè) Config 模塊垛膝,將要配置的信息都存放到配置文件中鳍侣。

我們預(yù)期的效果是下面這樣的丁稀,從 token 中解析用戶的id 然后調(diào)用 Authing 的API 獲取用戶的詳細(xì)信息。
還是為了簡(jiǎn)單倚聚,這里用戶的郵箱和密碼都直接硬編碼了线衫,真實(shí)項(xiàng)目中應(yīng)該從 LoginDto 中獲取,并且用類驗(yàn)證器驗(yàn)證 LoginDto 中的 email 和 password 字段惑折。


import { Controller, Get, Post } from '@nestjs/common';
import { Authing, Roles, AuthUser } from './decorators/common.decorator';

@Controller('users')
export class UsersController {

    @Post('login')
    async login(@Authing() authing) {

        try {
            const result  = await authing.login({
                email: 'xxxxxx',
                password: 'xxxxx'
            });

            return result;

        } catch (err) {
            console.log(err);
        }
        
    }

    @Get('info')
    @Roles('user')
    async info(@AuthUser() user, @Authing() authing) {

        try {

            return await authing.user({
                id: user.data.id
            });

        } catch(err) {
            console.log(err);
        }
    }
}

關(guān)鍵點(diǎn)就在于兩個(gè)自定義的路由參數(shù)裝飾器 @AuthUser 和 @Authing :

src/users/decorators/common.decorator.ts

import { ReflectMetadata, createParamDecorator } from '@nestjs/common';

export const Roles = (...roles: string[]) => ReflectMetadata('roles', roles);

export const Authing = createParamDecorator((data, req) => {
    return req.authing;
})

export const AuthUser = createParamDecorator((data, req) => {
    let token = req.query.token || null;
    
    !token && (token = req.body.token);

    return req.authing.decodeToken(token);
})

到此為止我們花了不到5分鐘就在 Nest.js 中集成了一整套用戶體系授账, Authing的功能遠(yuǎn)遠(yuǎn)不止于此,感興趣可以去它的官網(wǎng)了解惨驶,這里只是拋磚引玉白热。

上一篇:8、Nest.js 中的攔截器
下一篇:10粗卜、Nest.js 中的全局模塊和動(dòng)態(tài)模塊

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屋确,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子续扔,更是在濱河造成了極大的恐慌乍恐,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件测砂,死亡現(xiàn)場(chǎng)離奇詭異茵烈,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)砌些,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門呜投,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人存璃,你說(shuō)我怎么就攤上這事仑荐。” “怎么了纵东?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵粘招,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我偎球,道長(zhǎng)洒扎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任衰絮,我火速辦了婚禮袍冷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘猫牡。我一直安慰自己胡诗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著煌恢,像睡著了一般骇陈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瑰抵,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天缩歪,我揣著相機(jī)與錄音,去河邊找鬼谍憔。 笑死匪蝙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的习贫。 我是一名探鬼主播逛球,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼苫昌!你這毒婦竟也來(lái)了颤绕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤祟身,失蹤者是張志新(化名)和其女友劉穎奥务,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袜硫,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氯葬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了婉陷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帚称。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖秽澳,靈堂內(nèi)的尸體忽然破棺而出闯睹,到底是詐尸還是另有隱情,我是刑警寧澤担神,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布楼吃,位于F島的核電站,受9級(jí)特大地震影響妄讯,放射性物質(zhì)發(fā)生泄漏孩锡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一捞挥、第九天 我趴在偏房一處隱蔽的房頂上張望浮创。 院中可真熱鬧,春花似錦砌函、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)垦沉。三九已至,卻和暖如春仍劈,著一層夾襖步出監(jiān)牢的瞬間厕倍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工贩疙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留讹弯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓这溅,卻偏偏與公主長(zhǎng)得像组民,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悲靴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容