feat: 添加token相关代码

This commit is contained in:
lichao 2025-01-13 19:51:26 +08:00
parent d2f6d3ebcc
commit 946be43558
7 changed files with 81 additions and 12 deletions

1
.env
View File

@ -1,5 +1,6 @@
JWT_SECRET=5270e53a05600cb1f19b287a915b1c792d7552cbaae261afb6dcaf988d0db10f JWT_SECRET=5270e53a05600cb1f19b287a915b1c792d7552cbaae261afb6dcaf988d0db10f
SERVER_HOST='192.144.32.178' SERVER_HOST='192.144.32.178'
SERVER_PORT=3306 SERVER_PORT=3306
SERVER_USER='root'
PASSWORD='lichao1314' PASSWORD='lichao1314'
DB_NAME='auth_db' DB_NAME='auth_db'

View File

@ -35,6 +35,7 @@
"install": "^0.13.0", "install": "^0.13.0",
"mysql2": "^3.12.0", "mysql2": "^3.12.0",
"passport": "^0.7.0", "passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
@ -48,6 +49,7 @@
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/jest": "^29.5.2", "@types/jest": "^29.5.2",
"@types/node": "^20.3.1", "@types/node": "^20.3.1",
"@types/passport-jwt": "^4.0.1",
"@types/supertest": "^6.0.0", "@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0", "@typescript-eslint/parser": "^6.0.0",

View File

@ -16,6 +16,7 @@ specifiers:
'@types/express': ^4.17.17 '@types/express': ^4.17.17
'@types/jest': ^29.5.2 '@types/jest': ^29.5.2
'@types/node': ^20.3.1 '@types/node': ^20.3.1
'@types/passport-jwt': ^4.0.1
'@types/supertest': ^6.0.0 '@types/supertest': ^6.0.0
'@types/uuid': ^10.0.0 '@types/uuid': ^10.0.0
'@typescript-eslint/eslint-plugin': ^6.0.0 '@typescript-eslint/eslint-plugin': ^6.0.0
@ -29,6 +30,7 @@ specifiers:
jest: ^29.5.0 jest: ^29.5.0
mysql2: ^3.12.0 mysql2: ^3.12.0
passport: ^0.7.0 passport: ^0.7.0
passport-jwt: ^4.0.1
passport-local: ^1.0.0 passport-local: ^1.0.0
prettier: ^3.0.0 prettier: ^3.0.0
reflect-metadata: ^0.1.13 reflect-metadata: ^0.1.13
@ -59,6 +61,7 @@ dependencies:
install: 0.13.0 install: 0.13.0
mysql2: 3.12.0 mysql2: 3.12.0
passport: 0.7.0 passport: 0.7.0
passport-jwt: 4.0.1
passport-local: 1.0.0 passport-local: 1.0.0
reflect-metadata: 0.1.14 reflect-metadata: 0.1.14
rxjs: 7.8.1 rxjs: 7.8.1
@ -72,6 +75,7 @@ devDependencies:
'@types/express': 4.17.21 '@types/express': 4.17.21
'@types/jest': 29.5.14 '@types/jest': 29.5.14
'@types/node': 20.17.12 '@types/node': 20.17.12
'@types/passport-jwt': 4.0.1
'@types/supertest': 6.0.2 '@types/supertest': 6.0.2
'@typescript-eslint/eslint-plugin': 6.21.0_wj7xg5aijo4gzz5szz6d7vhele '@typescript-eslint/eslint-plugin': 6.21.0_wj7xg5aijo4gzz5szz6d7vhele
'@typescript-eslint/parser': 6.21.0_6txzh3afdjfsavlpa2fczfkiua '@typescript-eslint/parser': 6.21.0_6txzh3afdjfsavlpa2fczfkiua
@ -1295,7 +1299,6 @@ packages:
resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==}
dependencies: dependencies:
'@types/node': 20.17.12 '@types/node': 20.17.12
dev: false
/@types/methods/1.1.4: /@types/methods/1.1.4:
resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==}
@ -1310,6 +1313,26 @@ packages:
dependencies: dependencies:
undici-types: 6.19.8 undici-types: 6.19.8
/@types/passport-jwt/4.0.1:
resolution: {integrity: sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==}
dependencies:
'@types/jsonwebtoken': 9.0.5
'@types/passport-strategy': 0.2.38
dev: true
/@types/passport-strategy/0.2.38:
resolution: {integrity: sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==}
dependencies:
'@types/express': 4.17.21
'@types/passport': 1.0.17
dev: true
/@types/passport/1.0.17:
resolution: {integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==}
dependencies:
'@types/express': 4.17.21
dev: true
/@types/qs/6.9.17: /@types/qs/6.9.17:
resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==} resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==}
dev: true dev: true
@ -4544,6 +4567,13 @@ packages:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
/passport-jwt/4.0.1:
resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==}
dependencies:
jsonwebtoken: 9.0.2
passport-strategy: 1.0.0
dev: false
/passport-local/1.0.0: /passport-local/1.0.0:
resolution: {integrity: sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==} resolution: {integrity: sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==}
engines: {node: '>= 0.4.0'} engines: {node: '>= 0.4.0'}

View File

@ -1,21 +1,28 @@
import { NestFactory } from '@nestjs/core'; import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import * as mysql from 'mysql2/promise'; import * as mysql from 'mysql2/promise';
import { ConfigService } from '@nestjs/config';
async function bootstrap() { async function bootstrap() {
// 启动 Nest 应用
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService); // 获取 ConfigService 实例
// 连接 MySQL // 连接 MySQL
const connection = await mysql.createConnection({ const connection = await mysql.createConnection({
host: '192.144.32.178', host: configService.get('SERVER_HOST'),
user: 'root', user: configService.get('SERVER_USER'),
password: 'lichao1314', password: configService.get('PASSWORD'),
}); });
// 创建数据库(如果不存在) // 创建数据库(如果不存在)
await connection.query('CREATE DATABASE IF NOT EXISTS auth_db'); await connection.query('CREATE DATABASE IF NOT EXISTS auth_db');
await connection.end(); await connection.end();
// 启动 Nest 应用 app.enableCors({
const app = await NestFactory.create(AppModule); origin: 'http://localhost:8000', // 只允许该来源访问
credentials: true, // 允许带有凭证cookies的请求
});
await app.listen(3030); await app.listen(3030);
} }
bootstrap(); bootstrap();

View File

@ -1,4 +1,4 @@
import { Controller, Post, Body } from '@nestjs/common'; import { Controller, Post, Body, UnauthorizedException } from '@nestjs/common';
import { UserService } from '../user/user.service'; import { UserService } from '../user/user.service';
@Controller('user') @Controller('user')
@ -11,7 +11,12 @@ export class UserController {
) { ) {
const { username, password, email } = body; const { username, password, email } = body;
const user = await this.userService.createUser(username, password, email); const user = await this.userService.createUser(username, password, email);
return { message: 'User registered successfully', user }; const token = await this.userService.generateToken(user);
return {
message: 'User registered successfully',
user,
...token,
};
} }
@Post('login') @Post('login')
@ -19,15 +24,20 @@ export class UserController {
const { username, password } = body; const { username, password } = body;
const user = await this.userService.findUserByUsername(username); const user = await this.userService.findUserByUsername(username);
if (!user) { if (!user) {
throw new Error('User not found'); throw new UnauthorizedException('User not found');
} }
const isValidPassword = await this.userService.validatePassword( const isValidPassword = await this.userService.validatePassword(
password, password,
user.password, user.password,
); );
if (!isValidPassword) { if (!isValidPassword) {
throw new Error('Invalid password'); throw new UnauthorizedException('Invalid password');
} }
return { message: 'Login successful', user }; const token = await this.userService.generateToken(user);
return {
message: 'Login successful',
user,
...token,
};
} }
} }

View File

@ -1,11 +1,18 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { JwtModule } from '@nestjs/jwt';
import { UserService } from './user.service'; import { UserService } from './user.service';
import { User } from './entities/user.entity'; import { User } from './entities/user.entity';
import { UserController } from './user.controller'; import { UserController } from './user.controller';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([User])], imports: [
TypeOrmModule.forFeature([User]),
JwtModule.register({
secret: 'your-secret-key', // 建议从环境变量中读取
signOptions: { expiresIn: '24h' },
}),
],
providers: [UserService], providers: [UserService],
exports: [UserService], exports: [UserService],
controllers: [UserController], controllers: [UserController],

View File

@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { JwtService } from '@nestjs/jwt';
import { User } from './entities/user.entity'; import { User } from './entities/user.entity';
import * as bcrypt from 'bcrypt'; import * as bcrypt from 'bcrypt';
@ -9,6 +10,7 @@ export class UserService {
constructor( constructor(
@InjectRepository(User) @InjectRepository(User)
private userRepository: Repository<User>, private userRepository: Repository<User>,
private jwtService: JwtService,
) {} ) {}
async createUser(username: string, password: string, email: string) { async createUser(username: string, password: string, email: string) {
@ -39,4 +41,14 @@ export class UserService {
): Promise<boolean> { ): Promise<boolean> {
return bcrypt.compare(plainPassword, hashedPassword); return bcrypt.compare(plainPassword, hashedPassword);
} }
async generateToken(user: User) {
const payload = {
sub: user.id,
username: user.username,
};
return {
access_token: this.jwtService.sign(payload),
};
}
} }