diff --git a/.env b/.env index 42697dd..a2936b2 100644 --- a/.env +++ b/.env @@ -1,5 +1,6 @@ JWT_SECRET=5270e53a05600cb1f19b287a915b1c792d7552cbaae261afb6dcaf988d0db10f SERVER_HOST='192.144.32.178' SERVER_PORT=3306 +SERVER_USER='root' PASSWORD='lichao1314' DB_NAME='auth_db' \ No newline at end of file diff --git a/package.json b/package.json index fd2ca25..3c4ccf2 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "install": "^0.13.0", "mysql2": "^3.12.0", "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", @@ -48,6 +49,7 @@ "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/node": "^20.3.1", + "@types/passport-jwt": "^4.0.1", "@types/supertest": "^6.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 07ab575..9ab8ba2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,6 +16,7 @@ specifiers: '@types/express': ^4.17.17 '@types/jest': ^29.5.2 '@types/node': ^20.3.1 + '@types/passport-jwt': ^4.0.1 '@types/supertest': ^6.0.0 '@types/uuid': ^10.0.0 '@typescript-eslint/eslint-plugin': ^6.0.0 @@ -29,6 +30,7 @@ specifiers: jest: ^29.5.0 mysql2: ^3.12.0 passport: ^0.7.0 + passport-jwt: ^4.0.1 passport-local: ^1.0.0 prettier: ^3.0.0 reflect-metadata: ^0.1.13 @@ -59,6 +61,7 @@ dependencies: install: 0.13.0 mysql2: 3.12.0 passport: 0.7.0 + passport-jwt: 4.0.1 passport-local: 1.0.0 reflect-metadata: 0.1.14 rxjs: 7.8.1 @@ -72,6 +75,7 @@ devDependencies: '@types/express': 4.17.21 '@types/jest': 29.5.14 '@types/node': 20.17.12 + '@types/passport-jwt': 4.0.1 '@types/supertest': 6.0.2 '@typescript-eslint/eslint-plugin': 6.21.0_wj7xg5aijo4gzz5szz6d7vhele '@typescript-eslint/parser': 6.21.0_6txzh3afdjfsavlpa2fczfkiua @@ -1295,7 +1299,6 @@ packages: resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} dependencies: '@types/node': 20.17.12 - dev: false /@types/methods/1.1.4: resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} @@ -1310,6 +1313,26 @@ packages: dependencies: 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: resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==} dev: true @@ -4544,6 +4567,13 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} 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: resolution: {integrity: sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==} engines: {node: '>= 0.4.0'} diff --git a/src/main.ts b/src/main.ts index ced72d8..34f1701 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,21 +1,28 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as mysql from 'mysql2/promise'; +import { ConfigService } from '@nestjs/config'; async function bootstrap() { + // 启动 Nest 应用 + const app = await NestFactory.create(AppModule); + const configService = app.get(ConfigService); // 获取 ConfigService 实例 // 连接 MySQL const connection = await mysql.createConnection({ - host: '192.144.32.178', - user: 'root', - password: 'lichao1314', + host: configService.get('SERVER_HOST'), + user: configService.get('SERVER_USER'), + password: configService.get('PASSWORD'), }); // 创建数据库(如果不存在) await connection.query('CREATE DATABASE IF NOT EXISTS auth_db'); await connection.end(); - // 启动 Nest 应用 - const app = await NestFactory.create(AppModule); + app.enableCors({ + origin: 'http://localhost:8000', // 只允许该来源访问 + credentials: true, // 允许带有凭证(cookies)的请求 + }); + await app.listen(3030); } bootstrap(); diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 04b19db..f8c733c 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -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'; @Controller('user') @@ -11,7 +11,12 @@ export class UserController { ) { const { username, password, email } = body; 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') @@ -19,15 +24,20 @@ export class UserController { const { username, password } = body; const user = await this.userService.findUserByUsername(username); if (!user) { - throw new Error('User not found'); + throw new UnauthorizedException('User not found'); } const isValidPassword = await this.userService.validatePassword( password, user.password, ); 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, + }; } } diff --git a/src/user/user.module.ts b/src/user/user.module.ts index aa6800a..6f4b8e8 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -1,11 +1,18 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { JwtModule } from '@nestjs/jwt'; import { UserService } from './user.service'; import { User } from './entities/user.entity'; import { UserController } from './user.controller'; @Module({ - imports: [TypeOrmModule.forFeature([User])], + imports: [ + TypeOrmModule.forFeature([User]), + JwtModule.register({ + secret: 'your-secret-key', // 建议从环境变量中读取 + signOptions: { expiresIn: '24h' }, + }), + ], providers: [UserService], exports: [UserService], controllers: [UserController], diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 10f7408..df8a0c7 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; +import { JwtService } from '@nestjs/jwt'; import { User } from './entities/user.entity'; import * as bcrypt from 'bcrypt'; @@ -9,6 +10,7 @@ export class UserService { constructor( @InjectRepository(User) private userRepository: Repository, + private jwtService: JwtService, ) {} async createUser(username: string, password: string, email: string) { @@ -39,4 +41,14 @@ export class UserService { ): Promise { return bcrypt.compare(plainPassword, hashedPassword); } + + async generateToken(user: User) { + const payload = { + sub: user.id, + username: user.username, + }; + return { + access_token: this.jwtService.sign(payload), + }; + } }