登录注册以及身份验证
NestJS-登录注册
这里我们后续依赖使用到的几个依赖模块如下(可以提前下载好)
javascript
npm install bcrypt // 加密密码
npm install class-validator //装饰器来验证数据的有效性
注册模块
register.dto.ts注册规则
新建一个register.dto.ts存放我们注册部分规则
javascript
// src/modules/user/dto/register.dto.ts
import { IsString, IsEmail, MinLength } from 'class-validator';
export class RegisterDto {
@IsString()
username: string;
@IsString()
@MinLength(3)
password: string;
}
user.service.ts
javascript
import { Injectable ,ConflictException} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import { RegisterDto } from './dto/register.dto';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
// 获取所有用户
async findAll(): Promise<User[]> {
return this.userRepository.find();
}
async register(registerDto: RegisterDto): Promise<User> {
// 检查用户是否已经存在
const existingUser = await this.userRepository.findOne({ where: { username: registerDto.username } });
if (existingUser) {
throw new ConflictException('Username already exists');
}
// 创建新用户
const user = new User();
user.username = registerDto.username;
user.password = registerDto.password;
// 保存到数据库
return this.userRepository.save(user);
}
}
user.controller.ts
更改一下我们使用的注册接口
javascript
import { Controller, Get, Post, Param, Body, Put, Delete } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';
import { RegisterDto } from './dto/register.dto';
@Controller()
export class UserController {
constructor(private readonly userService: UserService) {}
@Get('users')
async findAll(): Promise<User[]> {
return this.userService.findAll();
}
@Post('register')
async register(@Body() registerDto: RegisterDto): Promise<User> {
return this.userService.register(registerDto);
}
}
检测注册模块
接下来我们测试一下我们的数据模块,用户存在的时候,这里给我们的提示
javascript
{
"message": "Username already exists",
"error": "Conflict",
"statusCode": 409
}
👉手动设置请求的返回格式
后续优化看代码,这里只是简单返回一下
javascript
return {
data: [],
message: '用户已存在!',
code: 400,
};
👉再次注册
javascript
//存在用户
{
"code": 400,
"message": "用户已存在!"
}
//新用户注册
{
"message": "恭喜你,您的账号注册成功!",
"code": 200
}
登录模块编写
这里我们直接也是将登录编写进了用户部分,先以实现功能为主
👉用户dto部分
先编写一个用户对应的规则dto部分
javascript
// src/modules/user/dto/login.dto.ts
import { IsString, IsEmail, MinLength } from 'class-validator';
export class LoginDto{
@IsString()
username: string;
@IsString()
@MinLength(3)
password: string;
}
👉user.service.ts之中添加登录方法
javascript
import { LoginDto } from './dto/login.dto';
// 异步登录
async login(loginDto: LoginDto){
// 查询数据库中是否存在同名用户
// 1. 查询数据库中是否存在该用户
const loginUser = await this.userRepository.findOne({
where: { username: loginDto.username },
});
// 2. 如果用户不存在,返回异常信息
if (!loginUser) {
throw new HttpException(
{
code: 404, // 适当的错误码是 404 (用户未找到)
message: '用户不存在!',
},
HttpStatus.NOT_FOUND, // 使用 404 状态码
);
}
// 3. 比较密码是否正确
if (loginDto.password !== loginUser.password) {
throw new HttpException(
{
code: 401, // 未授权错误码
message: '密码错误!',
},
HttpStatus.UNAUTHORIZED, // 使用 401 状态码
);
}
// 4. 返回登录成功信息,可以添加 JWT 或 Session 等信息
return {
message: '登录成功!',
code: 200,
data: {
username: loginUser.username,
userId: loginUser.userId,
// 可以附带其他的用户信息,如权限、token等
},
};
}
👉user.controller.ts之中使用
上面的方法服务写完了,但是我们还没有在控制器之中使用,这个时候需要使用一下
可以先导入我们的dto规则部分
javascript
import { LoginDto } from './dto/login.dto';
post接口调佣
javascript
@Post('login')
// 登录方法
login(@Body() loginDto: LoginDto){
// 调用userService中的login方法
return this.userService.login(loginDto);
}
核查登录模块
👉核查登录
接下来我们尝试调一下我们的登录接口
javascript
http://localhost:8888/api/login
👉登录正确时候
javascript
{
"message": "登录成功!",
"code": 200,
"data": {
"username": "admin",
"userId": 6
}
}
👉登录错误时候
javascript
{
"code": 404,
"message": "用户不存在!"
}
这个时候我们的登录和注册模块功能已经好了
身份验证
JWT认识安装
JWT作用
- 用户身份验证:先解析请求中的 token,验证它是否合法。通过中间件或守卫 (guard) 来实现。
- 使用 JWT 进行认证 :使用 JWT(JSON Web Token)进行用户认证,使用
@nestjs/jwt
包来解析和验证 token。 - 从 token 获取用户信息:验证 token通过,从中提取出用户信息,通过服务返回该用户详细数据
jwt依赖安装
javascript
npm install @nestjs/jwt @nestjs/passport passport passport-jwt
npm install @types/passport-jwt --save-dev
相关包的解释
javascript
@nestjs/jwt:用于生成和验证 JWT
passport-jwt:Passport JWT 策略模块
passport:用于 NestJS 认证的包
@nestjs/passport:NestJS 封装的 Passport 模块
生成auth授权模块
👉 生成一个auth授权模块,生成对应的auth授权模块
javascript
nest g controller auth --no-spec
nest g module auth --no-spec
nest g service auth --no-spec
👉生成文件如下
javascript
auth.controller.ts
auth.module.ts
auth.service.ts
生成token信息
👉文件auth.module.ts搭建
javascript
// src/auth/auth.module.ts
import { Module,forwardRef } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UserModule } from '@/modules/user/user.module';
@Module({
imports: [
forwardRef(() => UserModule), // 使用 forwardRef() 解决循环依赖
JwtModule.register({
secret: 'lintaibai^_^', // 可以放到环境变量中
signOptions: { expiresIn: '24h' }, // 设置 token 过期时间
}),
// UserModule, //导入用户模块
],
providers: [AuthService],
controllers: [AuthController],
})
export class AuthModule {}
👉文件auth.controller.ts搭建
文件位于src=> auth =>auth.controller.ts文件,接下来我们在这个文件里面生成token信息
javascript
// src/auth/auth.controller.ts
import { Controller,Post,Get,Body,Headers} from '@nestjs/common';
import { AuthService } from '@/auth/auth.service';
@Controller()
export class AuthController {
constructor(
private readonly authService: AuthService,
) {}
@Post('login')
async login(@Body() loginDto: { username: string; password: string }) {
console.log('login-loginDto', loginDto);
return this.authService.login(loginDto);
}
}
👉文件auth.service.ts
接下来我们将登录接口迁移进来,在登录的时候给用户返回Token信息
javascript
import { Injectable, HttpException, HttpStatus,Headers } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Code, Repository } from 'typeorm';
import { UserService } from '@/modules/user/user.service'
@Injectable()
export class AuthService {
constructor(
private jwtService: JwtService,
private readonly userService: UserService,
) {}
// 生成 JWT token
async generateToken(user: any): Promise<string>{
return this.jwtService.sign(
{userId: user.userId,username: user.username},
)
}
// 验证 JWT token
async validateToken(token: string){
try {
const decoded = this.jwtService.verify(token);
console.log(decoded,'decoded');
// 根据 decoded 提取用户信息,返回用户对象
return {
user:decoded,
code: 200,
message:'欢迎你的登录!',
}; // 示例
} catch (error) {
throw new HttpException(
{
code: 401, // 适当的错误码是 404 (用户未找到)
message: 'token已经过期!',
},
HttpStatus.UNAUTHORIZED, //
);
}
}
// 验证用户是否存在
async login(LoginDto){
const loginUser = await this.userService.findByUsername(LoginDto.username);
// const user = await this.authService.validateUserCredentials(body.username, body.password);
console.log(loginUser,'loginUser--------------------------------------------');
if (!loginUser) {
throw new HttpException(
{
code: 404, // 适当的错误码是 404 (用户未找到)
message: '用户不存在!',
},
HttpStatus.NOT_FOUND, // 使用 404 状态码
);
}else{
// 验证用户凭证
const token = await this.generateToken(loginUser);
console.log(token,'用户登录token信息');
return {
message: '登录成功!',
code: 200,
token:token,
};
}
}
}
👉登录校验
点击登录,这个时候给我们返回的信息我们就已拿到了
javascript
{
"message": "登录成功!",
"code": 200,
"token": "zMH0.kj_j7Ujg1iNoiPJcPMLkOy12UUNOpL6QMjxE"
}
getInfo获取用户信息
👉auth.controller.ts添加接口
javascript
// 获取用户信息token
@Get('getInfo')
async getInfo(@Headers('authorization') authorization: string) {
return this.authService.getInfo(authorization);
}
👉auth.service.ts添加方法
javascript
//导入相关方法
import { Injectable, HttpException, HttpStatus,Headers } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Code, Repository } from 'typeorm';
import { UserService } from '@/modules/user/user.service'
// 获取用户信息
async getInfo(authHeader: string){
console.log(authHeader,'authHeader');
if (!authHeader) {
throw new HttpException(
{
code: 401, // 未授权错误码
message: '未授权Token',
},
HttpStatus.UNAUTHORIZED, // 使用 401 状态码
);
}
const token = authHeader.split(' ')[1]; // 提取 Bearer token
if (!token) {
throw new HttpException(
{
code: 401, // 未授权错误码
message: 'Token无效',
},
HttpStatus.UNAUTHORIZED, // 使用 401 状态码
);
}else {
// 验证token
const decoded = this.jwtService.verify(token);
if(!decoded){
throw new HttpException(
{
code: 401, // 未授权错误码
message: 'Token无效',
},
HttpStatus.UNAUTHORIZED, // 使用 401 状态码
);
}else {
return {
code: 200,
message: '验证成功,欢迎你的登录!',
data: decoded,
};
}
}
}
👉模块auth.module.ts添加auth.module.ts模块
这里我们需要注意的是,必须在auth.module.ts引入用户部分的模块部分
注意这里的forwardRef 方法,必须使用这个来解决循环依赖
javascript
// src/auth/auth.module.ts
import { Module,forwardRef } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UserModule } from '@/modules/user/user.module';
import { JwtStrategy } from './jwt.strategy'; // 后续会创建的 JWT 策略
// import { UserService } from '@/modules/user/user.service'
@Module({
imports: [
forwardRef(() => UserModule), // 使用 forwardRef() 解决循环依赖
JwtModule.register({
secret: 'lintaibai^_^', // 可以放到环境变量中
signOptions: { expiresIn: '24h' }, // 设置 token 过期时间
}),
// UserModule, //导入用户模块
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
})
export class AuthModule {}
👉请求getInfo用户信息接口
这个时候我们已经可以从接口之中拿到对应的用户信息了
javascript
{
"code": 200,
"message": "验证成功,欢迎你的登录!",
"data": {
"userId": 6,
"username": "admin",
"iat": 1745999430,
"exp": 1746085830
}
}