请帮助我找到如何优化我的代码。 我需要限制登录用户的数据。为此,我需要从 Request 中的 JWT 令牌获取 UUID。但我不喜欢我的方法,因为我有重复的代码:
const jwt = request.headers.authorization.replace('Bearer ', '');
const json = this.jwtService.decode(jwt, { json: true }) as { uuid: string };
有人知道我该如何优化吗?
这是我的控制器的代码。
import { Controller, Get, Put, Body, Param, UseGuards, Req } from '@nestjs/common';
import { SettingService } from '../services';
import { AuthGuard } from '@nestjs/passport';
import { ResultInterface } from '../interfaces';
import { Request } from 'express';
import { JwtService } from '@nestjs/jwt';
@Controller('settings')
export class SettingController {
/**
* @param service
* @param jwtService
*/
constructor(private readonly service: SettingService,
private readonly jwtService: JwtService) {
}
@UseGuards(AuthGuard('jwt'))
@Get()
async findAll(@Req() request: Request): Promise<ResultInterface> {
const jwt = request.headers.authorization.replace('Bearer ', '');
const json = this.jwtService.decode(jwt, { json: true }) as { uuid: string };
const data = await this.service.findAll(json.uuid);
return { rows: data };
}
@UseGuards(AuthGuard('jwt'))
@Get(':id')
async findOne(@Param('id') id: number, @Req() request: Request): Promise<ResultInterface> {
const jwt = request.headers.authorization.replace('Bearer ', '');
const json = this.jwtService.decode(jwt, { json: true }) as { uuid: string };
const data = await this.service.findOneById(id, json.uuid);
return { row: data };
}
@UseGuards(AuthGuard('jwt'))
@Put()
update(@Body() data: any, @Req() request: Request): Promise<any> {
const jwt = request.headers.authorization.replace('Bearer ', '');
const json = this.jwtService.decode(jwt, { json: true }) as { uuid: string };
return this.service.update(data, json.uuid);
}
}
为了使您的代码更具可读性和透明性,您可以创建一个 @AuthUser() 装饰器并在所有控制器中重用它。当您使用 AuthGuard('jwt') 时,您已经在解码令牌,如果您再次使用 jwt.decode 函数,您将双重解码令牌。
我在下面附上了返回我所有订单的用户装饰器和订单控制器函数的示例。
用户.decorator.ts
import { createParamDecorator } from '@nestjs/common';
export const AuthUser = createParamDecorator((data, req) => {
return req.user;
});
order.controller.ts
@ApiOperation({ title: 'Get my orders' })
@Get('/me')
@UseGuards(AuthGuard('jwt'))
async findMyOrders(@AuthUser() user: any): Promise<Order[]> {
return this.orderService.findbyUserId(user._id);
}
你可以创建一个 JWTUtil 来为你做这件事......也许像这样?
@Injectable()
export class JWTUtil {
constructor(private readonly jwtService: JWTService) {}
decode(auth: string): {uuid: string}{
const jwt = auth.replace('Bearer ', '');
return this.jwtService.decode(jwt, { json: true }) as { uuid: string };
}
}
然后像这样使用它:
@Controller('settings')
export class SettingController {
constructor(
private readonly jwtUtil: JWTUtil,
private readonly service: SettingService,
) {}
@Get()
@UseGuards(AuthGuard('jwt'))
async findAll(@Headers('Authorization') auth: string): Promise<ResultInterface> {
const json = await this.jwtUtil.decode(auth);
const data = await this.service.findAll(json.uuid);
//....
}
}
另请注意,您可以从控制器直接访问
Authorization
标头。而不是穿过 Request
对象。
您可以使用 AuthGuard 来处理请求并将用户信息传递给每个经过身份验证的请求。
const USER_ID_HEADER_NAME = 'x-user-id';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
return super.canActivate(context);
}
handleRequest<TUser = any>(
err: any,
user: any,
info: any,
context: ExecutionContext,
): TUser {
if (err || !user) {
throw err || new UnauthorizedException();
}
const request = context.switchToHttp().getRequest();
request.headers[USER_ID_HEADER_NAME] = user.id;
return user;
}
}
控制器可以评估
USER_ID_HEADER_NAME
来获取用户信息。
您正在使用
request.headers.authorization...
使用这个很简单;
async findAll(@Req() request: Request): Promise<ResultInterface> {
const authToken = request.headers['authorization'].split(' ')[1];
}
对于任何使用 Passport-jwt 的人,如果您有一个看起来像这样的
jwt.strategy.ts
:
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET,
});
}
validate(payload: any) {
// What this function returns is what req.user will be set to everywhere
return { user_id: payload.sub };
}
}
你让
validate()
方法返回的就是 req.user
最终的结果。因此,如果您按照 nestjs 的 Passport 教程中所述全局应用其
JwtAuthGuard
,您可以像 user_id
中那样获得 user.controller.ts
,其中 .getUsername()
是数据库查找:
import { Controller, Get, Request } from '@nestjs/common';
import { UsersService } from 'src/users/users.service';
@Controller('api/user')
export class UserController {
constructor(private readonly usersService: UsersService) {}
@Get('username')
username(@Request() req) {
return this.usersService.getUsername(req.user.user_id);
}
}