import { ExecutionContext, HttpException, CanActivate } from "@nestjs/common" import { FieldParser } from "./field" import { JwtParameter, JwtDecoder, JwtPayloadUpdated, JwtSignOptions} from "cymaple-utils" import { Request } from "express" type JwtOptions = { secretKey:string, signOptions:JwtSignOptions } export interface JwtGuardPayloadHandler { (payload:JwtPayloadUpdated,request:Request):void | Promise } export interface JwtGuardTokenHandler { (token:string):string | Promise } export interface GenerateTokenStrategy { ():string } export class JwtGuard implements CanActivate { private payloadHandlerList:JwtGuardPayloadHandler [] = [] private tokenHandlerList:JwtGuardTokenHandler[] = [] private request:Request private payload:JwtPayloadUpdated private token:string = "" private allowAnonymousState:boolean = false private anonymousToken:string = "" public constructor( private readonly fieldParser:FieldParser, private readonly jwtOptions:JwtOptions, ) {} public async canActivate(ctx:ExecutionContext):Promise { this.request = ctx.switchToHttp().getRequest() this.token = this.parseToken(this.request) await this.useTokenHandlers() this.authToken(this.token) await this.usePayloadHandlers() return true } private parseToken(request:Request):string { this.fieldParser.parse(request); let token = this.fieldParser.getFieldValue() if ( !token ) { if ( !this.allowAnonymousState ) throw new HttpException("令牌获取失败",400) token = this.anonymousToken } return token; } private authToken(token:string):void { let parameter = new JwtParameter(this.jwtOptions.secretKey,this.jwtOptions.signOptions) try { var jwtDecoder = new JwtDecoder(token,parameter) this.payload = jwtDecoder.getUpdated() as JwtPayloadUpdated } catch(e) { throw new HttpException("令牌验证失败",400) } } private async useTokenHandlers():Promise { if ( this.tokenHandlerList.length === 0 ) return for ( let handler of this.tokenHandlerList.values() ) { this.token = await handler(this.token) } } private async usePayloadHandlers():Promise { if ( this.payloadHandlerList.length === 0 ) return for (let handle of this.payloadHandlerList.values()) { await handle(this.payload,this.request); } } public allowAnonymous(strategy:GenerateTokenStrategy) { this.allowAnonymousState = true this.anonymousToken = strategy(); } public addPayloadHandler(handler:JwtGuardPayloadHandler):this { this.payloadHandlerList.push(handler) return this; } public addTokenHandler(handler:JwtGuardTokenHandler):this { this.tokenHandlerList.push(handler) return this; } }