Mysql+Prisma+Nest 实现JWT登录注册
一 prisma准备
创建项目
nest new login_project
创建一个database
使用prisma连接数据库
npm install prisma --save-dev //安装prisma
npx prisma init --datasource-provider mysql //初始化prisma
修改.env文件
DATABASE_URL="mysql://root:root123@localhost:3306/login_project_db"
创建一个user表
- 主键 用户id
- phone 用户账号
- password用户密码
- 创建时间
- 更新时间
// prisma\schema.prisma
model User {
id Int @id @default(autoincrement())
phone String @unique
password String
createTime DateTime @default(now())
updateTime DateTime @updatedAt()
}
生成表结构
npx prisma migrate dev --name creat_user
看一下数据库
可以了,接下来把prisma注册成全局模块,这样别的模块想使用直接注入service里面就可以了
创建一个prisma模块和prisma服务
nest g mo prisma --no-spec //创建模块
nest g s prisma --no-spec //创建服务
在prisma服务里面继承PrismaClient
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient { } // 继承PrismaClient
然后把prisma定义成全局模块,记得一定要暴露出prisma服务
import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService] // 导出 PrismaService 以便在其他模块中使用
})
export class PrismaModule { }
欧克差不多了,接下来别的service要使用直接注入就好,就可以直接使用了
二 Nest登录注册准备
初始化配置
nest g mo auth --no-spec //创建一个登录注册的模块
nest g s auth --no-spec //创建服务
nest g co auth --no-spec //创建控制器
--no-spec 就是不要测试文件
// src\auth\auth.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
@Controller('auth')
export class AuthController {
constructor(
private readonly authService: AuthService
) { }
// 登录
@Post('login')
login(@Body() loginDto: LoginDto) {
return this.authService.login(loginDto);
}
// 注册
@Post('register')
register(@Body() registerDto: RegisterDto) {
return this.authService.register(registerDto);
}
}
// src\auth\auth.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
@Injectable()
export class AuthService {
constructor(
private readonly prisma: PrismaService
) { }
login(loginDto: LoginDto) {
return loginDto
}
register(registerDto: RegisterDto) {
return registerDto
}
}
dto文件是验证前端传递过来的参数的,这里不验证
// src\auth\dto\login.dto.ts
export class LoginDto {
phone: string;
password: string;
}
// src\auth\dto\register.dto.ts
export class RegisterDto {
phone: string;
password: string;
}
使用Apifox试一下
入口文件监听的是3000端口
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
试一下接口
接口完善
- 要给用户密码加密
npm i argon2 //加密
使用方法
import { hash, verify } from "argon2"
- hash 加密
- verify 验证
接口完善
- register 是把用户信息存到数据库里
- login 是根据 phone和 password 取匹配是否有这个 user
完善注册
- 拿到前端数据
- 判断数据库手机号有没有一样的,有返回错误 没有继续
- 存储数据到数据库
async register(registerDto: RegisterDto) {
// 验证用户是否存在
const user = await this.prisma.user.findFirst({
where: {
phone: registerDto.phone
}
})
// 存在则抛出异常
if (user) {
throw new HttpException('用户已存在', 200)
}
// 不存在则创建用户
const newUser = await this.prisma.user.create({
data: {
phone: registerDto.phone,
password: await hash(registerDto.password),
}
})
return newUser
}
第一次注册的情况
注册相同的手机号肯定就不行了
完善登录
- 登录拿到前端传递过来的数据
- 查询数据库有没有对应手机号 有继续 没有失败
- 对比传递过来的密码是否验证通过 通过给成功 对比失败 密码输入错误
async login(loginDto: LoginDto) {
const user = await this.prisma.user.findFirst({
where: {
phone: loginDto.phone,
}
})
if (user) {
const isPasswordCorrect = await verify(user.password, loginDto.password)
if (isPasswordCorrect) {
return user
} else {
throw new HttpException('密码错误', 200)
}
} else {
throw new HttpException('用户不存在', 200)
}
return loginDto
}
登录没有数据库对应手机号的
密码不对的
通过的
JWT配置
-
登录或者注册成功,我们都应该返回token
-
前端存一份,以后请求别的接口的时候,带上,
- 验证通过就可以请求接口数据
- 验证失败就抛出错误,不返回数据
npm install @nestjs/jwt
AppModule 里引入 JwtModule
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PrismaModule } from './prisma/prisma.module';
import { AuthModule } from './auth/auth.module';
import { JwtModule } from '@nestjs/jwt';
@Module({
imports: [PrismaModule, AuthModule,
JwtModule.register({ // 注册jwt模块
global: true, // 全局使用
secret: 'dehua', // 秘钥
signOptions: { // 签名配置
expiresIn: '7d', // 过期时间
},
})],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
global:true 声明为全局模块,这样就不用每个模块都引入它了,指定加密密钥,token 过期时间
在我们的auth.service 注入 JwtService
import { BadRequestException, HttpException, Injectable } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
import { hash, verify } from "argon2"
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(
private readonly prisma: PrismaService,
private readonly jwtService: JwtService,
) { }
async login(loginDto: LoginDto) {
const user = await this.prisma.user.findFirst({
where: {
phone: loginDto.phone,
}
})
if (user) {
const isPasswordCorrect = await verify(user.password, loginDto.password)
if (isPasswordCorrect) {
const token = await this.jwtService.signAsync({
id: user.id,
phone: user.phone
})
return {
code: 200,
data: token,
mag: "登录成功"
}
} else {
throw new HttpException('密码错误', 200)
}
} else {
throw new HttpException('用户不存在', 200)
}
return loginDto
}
async register(registerDto: RegisterDto) {
// 验证用户是否存在
const user = await this.prisma.user.findFirst({
where: {
phone: registerDto.phone
}
})
// 存在则抛出异常
if (user) {
throw new HttpException('用户已存在', 200)
}
// 不存在则创建用户
const newUser = await this.prisma.user.create({
data: {
phone: registerDto.phone,
password: await hash(registerDto.password),
}
})
const token = await this.jwtService.signAsync({
id: newUser.id,
phone: newUser.phone
})
return {
code: 200,
data: token,
mag: "登录成功"
}
}
}
这样返回的就是token了
接下来前端存储一份,请求头上携带就可以了
接口要token验证通过之后,才可以返回数据
我们写测试接口试一下
- 创建一个完整模块资源 rest api 增删改查都有
nest g res article
现在都可以查,不管有没有token,不管验证通过没有
我们现在创建一个Guard(守卫)来限制,满足才可以访问接口
nest g guard login --no-spec --flat
实现 jwt 校验的逻辑
import { JwtService } from '@nestjs/jwt';
import { CanActivate, ExecutionContext, Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { Request } from 'express';
import { Observable } from 'rxjs';
interface JwtUserData {
userId: number;
username: string;
}
declare module 'express' {
interface Request {
user: JwtUserData
}
}
@Injectable()
export class LoginGuard implements CanActivate {
@Inject(JwtService)
private jwtService: JwtService;
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request: Request = context.switchToHttp().getRequest();
const authorization = request.header('authorization') || '';
const bearer = authorization.split(' ');
if (!bearer || bearer.length < 2) {
throw new UnauthorizedException('登录 token 错误');
}
const token = bearer[1];
try {
const info = this.jwtService.verify(token);
(request as any).user = info
return true;
} catch (e) {
throw new UnauthorizedException('登录 token 失效,请重新登录');
}
}
}
取出 authorization 的 header,验证 token 是否有效,token 有效返回 true,无效的话就返回 UnauthorizedException
应用一下需要验证的接口
//article.controller.ts
@Post()
@UseGuards(LoginGuard)
create(@Body() createArticleDto: CreateArticleDto) {
return this.articleService.create(createArticleDto);
}
没有带token的情况
带token,随便输入的token
正确的token
怎么获得当前token用户的信息呢
以上就是本次文章的全部内容,创作不易,如有帮助,记得点赞收藏,感谢您的观看。