我已经根据文档在 NestJS 中创建了一个登录系统,但我对授权方法有疑问。根据文档,用户登录后会收到一个token,每次需要发送到API进行授权。
我的问题是:这真的是正确的方法吗?使用 HttpOnly cookie 来存储令牌不是更好吗?在 NestJS 的上下文中应该如何实现? 验证服务:
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService
) {}
async signIn(
username: string,
pass: string,
): Promise<{ access_token: string }> {
const user = await this.usersService.findOne(username);
if (user?.password !== pass) {
throw new UnauthorizedException();
}
const payload = { sub: user.userId, username: user.username };
return {
access_token: await this.jwtService.signAsync(payload),
};
}
}
身份验证控制器
import {
Body,
Controller,
Get,
HttpCode,
HttpStatus,
Post,
Request,
UseGuards
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthGuard } from './auth.guard';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@HttpCode(HttpStatus.OK)
@Post('login')
signIn(@Body() signInDto: Record<string, any>) {
return this.authService.signIn(signInDto.username, signInDto.password);
}
@UseGuards(AuthGuard)
@Get('profile')
getProfile(@Request() req) {
return req.user;
}
}
授权守卫
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { jwtConstants } from './constants';
import { Request } from 'express';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException();
}
try {
const payload = await this.jwtService.verifyAsync(
token,
{
secret: jwtConstants.secret
}
);
request['user'] = payload;
} catch {
throw new UnauthorizedException();
}
return true;
}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}
您认为哪种方法更好?我应该遵循文档中的内容并将令牌存储在 React 中,在需要时发送它,还是使用 HttpOnly cookies?
您的项目首选的身份验证方法完全取决于您的项目的要求。基于令牌和 cookie 的方法都有各自的优点和缺点。 Nest.js 作为一个框架使您能够实现这两种方法。
基于 Cookie 的身份验证
这是一种有状态的方法。
服务器对客户端进行身份验证,向客户端发送签名的 cookie 并打开会话。客户端将 cookie 与每个后续身份验证请求一起发送。
服务器通过这种方式维护会话信息。客户端注销后会话将被刷新。
基于 Cookie 的身份验证不需要在客户端进行设置。浏览器默认处理该问题。您还可以将 cookie 设置为 HttpOnly,这使得客户端 javascript 无法访问它们,从而确保安全。
Cookie 被严格锁定到特定域。您不能在域之间使用 cookie,因为会话位于服务器上。
基于令牌的身份验证
这是一种无状态的方法。
服务器对客户端进行身份验证,并将签名令牌发送回客户端,其中包含散列密钥。客户端将令牌附加在每个请求的标头中,由服务器解析和验证以确保完整性。
这需要在客户端进行设置来管理令牌。客户端负责存储和更新有效令牌。
结论
HTTP 是无状态协议。基于令牌的方法确保客户端的完整性,并确保无状态方法。基于 Cookie 的身份验证会导致客户端和服务器之间有状态会话。
令牌可以跨域共享,确保与特定于域的 cookie 不同,适合微服务架构。
基于令牌的方法会带来更灵活的方法,尽管它需要您编写一些手动代码。