diff --git a/apps/backend/package.json b/apps/backend/package.json index ab9b8f3..f5223f0 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -28,9 +28,7 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^4.0.2", "@nestjs/core": "^10.0.0", - "@nestjs/jwt": "^11.0.0", "@nestjs/mapped-types": "*", - "@nestjs/passport": "^11.0.5", "@nestjs/platform-express": "^10.0.0", "@nestjs/throttler": "^6.4.0", "@nestjs/typeorm": "^11.0.0", @@ -40,8 +38,6 @@ "class-validator": "^0.14.2", "cookie-parser": "^1.4.7", "jsonwebtoken": "^9.0.2", - "passport": "^0.7.0", - "passport-jwt": "^4.0.1", "pg": "^8.15.6", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", @@ -56,7 +52,6 @@ "@types/express": "^5.0.0", "@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": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", diff --git a/apps/backend/pnpm-lock.yaml b/apps/backend/pnpm-lock.yaml index 2e5f696..7527d50 100644 --- a/apps/backend/pnpm-lock.yaml +++ b/apps/backend/pnpm-lock.yaml @@ -32,15 +32,9 @@ importers: '@nestjs/core': specifier: ^10.0.0 version: 10.4.17(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/jwt': - specifier: ^11.0.0 - version: 11.0.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)) '@nestjs/mapped-types': specifier: '*' version: 2.1.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2) - '@nestjs/passport': - specifier: ^11.0.5 - version: 11.0.5(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) '@nestjs/platform-express': specifier: ^10.0.0 version: 10.4.17(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.17) @@ -68,12 +62,6 @@ importers: jsonwebtoken: specifier: ^9.0.2 version: 9.0.2 - passport: - specifier: ^0.7.0 - version: 0.7.0 - passport-jwt: - specifier: ^4.0.1 - version: 4.0.1 pg: specifier: ^8.15.6 version: 8.15.6 @@ -111,9 +99,6 @@ importers: '@types/node': specifier: ^20.3.1 version: 20.17.31 - '@types/passport-jwt': - specifier: ^4.0.1 - version: 4.0.1 '@types/supertest': specifier: ^6.0.0 version: 6.0.3 @@ -606,11 +591,6 @@ packages: '@nestjs/websockets': optional: true - '@nestjs/jwt@11.0.0': - resolution: {integrity: sha512-v7YRsW3Xi8HNTsO+jeHSEEqelX37TVWgwt+BcxtkG/OfXJEOs6GZdbdza200d6KqId1pJQZ6UPj1F0M6E+mxaA==} - peerDependencies: - '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - '@nestjs/mapped-types@2.1.0': resolution: {integrity: sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==} peerDependencies: @@ -624,12 +604,6 @@ packages: class-validator: optional: true - '@nestjs/passport@11.0.5': - resolution: {integrity: sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - passport: ^0.5.0 || ^0.6.0 || ^0.7.0 - '@nestjs/platform-express@10.4.17': resolution: {integrity: sha512-ovn4Wxney3QGBrqNPv0QLcCuH5QoAi6pb/GNWAz6B/NmBjZbs9/zl4a2beGDA2SaYre9w43YbfmHTm17PneP9w==} peerDependencies: @@ -798,9 +772,6 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/jsonwebtoken@9.0.7': - resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==} - '@types/methods@1.1.4': resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} @@ -816,15 +787,6 @@ packages: '@types/node@22.15.14': resolution: {integrity: sha512-BL1eyu/XWsFGTtDWOYULQEs4KR0qdtYfCxYAUYRoB7JP7h9ETYLgQTww6kH8Sj2C0pFGgrpM0XKv6/kbIzYJ1g==} - '@types/passport-jwt@4.0.1': - resolution: {integrity: sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==} - - '@types/passport-strategy@0.2.38': - resolution: {integrity: sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==} - - '@types/passport@1.0.17': - resolution: {integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==} - '@types/qs@6.9.18': resolution: {integrity: sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==} @@ -2531,17 +2493,6 @@ 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==} - - passport-strategy@1.0.0: - resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==} - engines: {node: '>= 0.4.0'} - - passport@0.7.0: - resolution: {integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==} - engines: {node: '>= 0.4.0'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2574,9 +2525,6 @@ packages: pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} - pause@0.0.1: - resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} - peek-readable@7.0.0: resolution: {integrity: sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==} engines: {node: '>=18'} @@ -4135,12 +4083,6 @@ snapshots: transitivePeerDependencies: - encoding - '@nestjs/jwt@11.0.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))': - dependencies: - '@nestjs/common': 10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@types/jsonwebtoken': 9.0.7 - jsonwebtoken: 9.0.2 - '@nestjs/mapped-types@2.1.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)': dependencies: '@nestjs/common': 10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -4149,11 +4091,6 @@ snapshots: class-transformer: 0.5.1 class-validator: 0.14.2 - '@nestjs/passport@11.0.5(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)': - dependencies: - '@nestjs/common': 10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) - passport: 0.7.0 - '@nestjs/platform-express@10.4.17(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.17)': dependencies: '@nestjs/common': 10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -4357,10 +4294,6 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/jsonwebtoken@9.0.7': - dependencies: - '@types/node': 20.17.31 - '@types/methods@1.1.4': {} '@types/mime@1.3.5': {} @@ -4375,20 +4308,6 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/passport-jwt@4.0.1': - dependencies: - '@types/jsonwebtoken': 9.0.7 - '@types/passport-strategy': 0.2.38 - - '@types/passport-strategy@0.2.38': - dependencies: - '@types/express': 5.0.1 - '@types/passport': 1.0.17 - - '@types/passport@1.0.17': - dependencies: - '@types/express': 5.0.1 - '@types/qs@6.9.18': {} '@types/range-parser@1.2.7': {} @@ -6423,19 +6342,6 @@ snapshots: parseurl@1.3.3: {} - passport-jwt@4.0.1: - dependencies: - jsonwebtoken: 9.0.2 - passport-strategy: 1.0.0 - - passport-strategy@1.0.0: {} - - passport@0.7.0: - dependencies: - passport-strategy: 1.0.0 - pause: 0.0.1 - utils-merge: 1.0.1 - path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -6459,8 +6365,6 @@ snapshots: dependencies: through: 2.3.8 - pause@0.0.1: {} - peek-readable@7.0.0: {} pg-cloudflare@1.2.5: diff --git a/apps/backend/src/admin/controller/admin-user.controller.ts b/apps/backend/src/admin/controller/admin-user.controller.ts index d25d5f4..ec16f77 100644 --- a/apps/backend/src/admin/controller/admin-user.controller.ts +++ b/apps/backend/src/admin/controller/admin-user.controller.ts @@ -19,13 +19,13 @@ import { RemoveUserDto } from '../dto/admin-user/remove.dto'; import { RolesGuard } from 'src/common/guard/roles.guard'; import { Roles } from 'src/common/decorators/role.decorator'; import { Role } from 'src/auth/role.enum'; -import { AuthGuard } from '@nestjs/passport'; +import { AuthGuard } from 'src/auth/guards/auth.guard'; @Controller('admin/user') -@UseGuards(AuthGuard('jwt'), RolesGuard) +@UseGuards(AuthGuard, RolesGuard) @Roles(Role.Admin) export class AdminUserController { - constructor(private readonly userService: UserService) {} + constructor(private readonly userService: UserService) { } @Get() async list(@Query() listDto: ListDto) { diff --git a/apps/backend/src/admin/controller/web/admin-web-blog.controller.ts b/apps/backend/src/admin/controller/web/admin-web-blog.controller.ts index eb0db21..de14170 100644 --- a/apps/backend/src/admin/controller/web/admin-web-blog.controller.ts +++ b/apps/backend/src/admin/controller/web/admin-web-blog.controller.ts @@ -9,20 +9,20 @@ import { Put, UseGuards, } from '@nestjs/common'; -import { AuthGuard } from '@nestjs/passport'; import { CreateBlogDto } from 'src/admin/dto/admin-web/create-blog.dto'; import { SetBlogPasswordDto } from 'src/admin/dto/admin-web/set-blog-password.dto'; import { UpdateBlogDto } from 'src/admin/dto/admin-web/update-blog.dto'; +import { AuthGuard } from 'src/auth/guards/auth.guard'; import { Role } from 'src/auth/role.enum'; import { BlogService } from 'src/blog/blog.service'; import { Roles } from 'src/common/decorators/role.decorator'; import { RolesGuard } from 'src/common/guard/roles.guard'; @Controller('/admin/web/blog') -@UseGuards(AuthGuard('jwt'), RolesGuard) +@UseGuards(AuthGuard, RolesGuard) @Roles(Role.Admin) export class AdminWebBlogController { - constructor(private readonly adminWebBlogService: BlogService) {} + constructor(private readonly adminWebBlogService: BlogService) { } @Get() async list() { diff --git a/apps/backend/src/admin/controller/web/admin-web-resource.controller.ts b/apps/backend/src/admin/controller/web/admin-web-resource.controller.ts index ee130e4..f601f66 100644 --- a/apps/backend/src/admin/controller/web/admin-web-resource.controller.ts +++ b/apps/backend/src/admin/controller/web/admin-web-resource.controller.ts @@ -9,18 +9,18 @@ import { Put, UseGuards, } from '@nestjs/common'; -import { AuthGuard } from '@nestjs/passport'; import { CreateResourceDto } from 'src/admin/dto/admin-web/create-resource.dto'; +import { AuthGuard } from 'src/auth/guards/auth.guard'; import { Role } from 'src/auth/role.enum'; import { Roles } from 'src/common/decorators/role.decorator'; import { RolesGuard } from 'src/common/guard/roles.guard'; import { ResourceService } from 'src/resource/resource.service'; @Controller('/admin/web/resource') -@UseGuards(AuthGuard('jwt'), RolesGuard) +@UseGuards(AuthGuard, RolesGuard) @Roles(Role.Admin) export class AdminWebResourceController { - constructor(private readonly resourceService: ResourceService) {} + constructor(private readonly resourceService: ResourceService) { } @Get() async list() { diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index 9084e46..112cfd0 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -7,7 +7,6 @@ import { UserModule } from './user/user.module'; import { AuthModule } from './auth/auth.module'; import { VerificationModule } from './verification/verification.module'; import { NotificationModule } from './notification/notification.module'; -import { PassportModule } from '@nestjs/passport'; import { ResourceModule } from './resource/resource.module'; import { BlogModule } from './blog/blog.module'; import { AdminModule } from './admin/admin.module'; @@ -28,7 +27,6 @@ import { ThrottlerModule } from '@nestjs/throttler'; entities: [], synchronize: process.env.NODE_ENV !== 'production', // Set to false in production }), - PassportModule.register({ defaultStrategy: 'jwt' }), ThrottlerModule.forRoot({ throttlers: [ { diff --git a/apps/backend/src/auth/auth.controller.ts b/apps/backend/src/auth/auth.controller.ts index 5a84b27..413257b 100644 --- a/apps/backend/src/auth/auth.controller.ts +++ b/apps/backend/src/auth/auth.controller.ts @@ -10,11 +10,11 @@ import { } from '@nestjs/common'; import { LoginByPasswordDto } from './dto/login.dto'; import { AuthService } from './auth.service'; -import { AuthGuard } from '@nestjs/passport'; import { UserSessionService } from 'src/user/services/user-session.service'; import { Throttle, ThrottlerGuard } from '@nestjs/throttler'; import { Response } from 'express'; import { UserService } from 'src/user/user.service'; +import { AuthGuard } from './guards/auth.guard'; @Controller('auth') export class AuthController { @@ -40,8 +40,8 @@ export class AuthController { // } // } - private setUserToken(res: Response, token: string) { - res.cookie('token', token, { + private setUserSession(res: Response, sessionId: string) { + res.cookie('session', sessionId, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', @@ -56,11 +56,10 @@ export class AuthController { @Res({ passthrough: true }) res: Response, ) { const { identifier, password } = loginDto; - const loginRes = await this.authService.loginWithPassword(identifier, password); - const { userId, token } = loginRes; - this.setUserToken(res, token); + const session = await this.authService.loginWithPassword(identifier, password); + this.setUserSession(res, session.sessionId); return { - user: await this.userService.findById(userId), + user: await this.userService.findById(session.userId), }; } @@ -84,7 +83,7 @@ export class AuthController { throw new NotImplementedException(); } - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard) @Post('logout') async logout(@Request() req) { const { userId, sessionId } = req.user; diff --git a/apps/backend/src/auth/auth.module.ts b/apps/backend/src/auth/auth.module.ts index 6c13af9..3eeaf9a 100644 --- a/apps/backend/src/auth/auth.module.ts +++ b/apps/backend/src/auth/auth.module.ts @@ -2,35 +2,22 @@ import { forwardRef, Module } from '@nestjs/common'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; import { UserModule } from 'src/user/user.module'; -import { JwtModule } from '@nestjs/jwt'; import { TypeOrmModule } from '@nestjs/typeorm'; import { UserSession } from 'src/user/entities/user-session.entity'; -import { PassportModule } from '@nestjs/passport'; -import { JwtStrategy } from './strategies/jwt.strategy'; -import { ConfigModule, ConfigService } from '@nestjs/config'; +import { ConfigModule } from '@nestjs/config'; import { VerificationModule } from 'src/verification/verification.module'; -import { OptionalAuthGuard } from './strategies/OptionalAuthGuard'; +import { AuthGuard } from './guards/auth.guard'; +import { OptionalAuthGuard } from './guards/optional-auth.guard'; @Module({ imports: [ ConfigModule, forwardRef(() => UserModule), TypeOrmModule.forFeature([UserSession]), - PassportModule.register({ defaultStrategy: 'jwt' }), - JwtModule.registerAsync({ - imports: [ConfigModule], - inject: [ConfigService], - useFactory: (configService: ConfigService) => ({ - secret: configService.get('JWT_SECRET', 'tone-page'), - signOptions: { - expiresIn: configService.get('JWT_EXPIRES_IN', '1d'), - }, - }), - }), VerificationModule, ], controllers: [AuthController], - providers: [AuthService, JwtStrategy, OptionalAuthGuard], - exports: [PassportModule, JwtStrategy, AuthService, OptionalAuthGuard], + providers: [AuthService, AuthGuard, OptionalAuthGuard], + exports: [AuthService, AuthGuard, OptionalAuthGuard], }) -export class AuthModule {} +export class AuthModule { } diff --git a/apps/backend/src/auth/auth.service.ts b/apps/backend/src/auth/auth.service.ts index 71cf3fc..710ed05 100644 --- a/apps/backend/src/auth/auth.service.ts +++ b/apps/backend/src/auth/auth.service.ts @@ -1,8 +1,6 @@ import { createHash } from 'crypto'; import { BadRequestException, Injectable } from '@nestjs/common'; import { UserService } from 'src/user/user.service'; -import { User } from 'src/user/entities/user.entity'; -import { JwtService } from '@nestjs/jwt'; import { UserSessionService } from 'src/user/services/user-session.service'; import { VerificationService } from 'src/verification/verification.service'; import { BusinessException } from 'src/common/exceptions/business.exception'; @@ -12,7 +10,6 @@ import { ErrorCode } from 'src/common/constants/error-codes'; export class AuthService { constructor( private readonly userService: UserService, - private readonly jwtService: JwtService, private readonly userSessionService: UserSessionService, private readonly verificationService: VerificationService, ) { } @@ -49,11 +46,9 @@ export class AuthService { }); } - // 登录成功,颁发token - return { - token: await this.generateToken(user), - userId: user.userId, - }; + const { userId } = user; + + return this.userSessionService.createSession(userId); } async loginWithPhone(data: { phone: string; code: string; }) { @@ -93,9 +88,8 @@ export class AuthService { throw new BadRequestException('请求失败,请稍后再试'); } - // 登录,颁发token return { - token: await this.generateToken(user), + userId: user.userId }; } @@ -103,18 +97,4 @@ export class AuthService { return createHash('sha256').update(`${password}${salt}`).digest('hex'); } - private async generateToken(user: User) { - // 存储 - const sessionRes = await this.userSessionService.createSession( - user.userId, - ); - - const payload = { - userId: user.userId, - sessionId: sessionRes.sessionId, - }; - - // 颁发token - return this.jwtService.sign(payload); - } } diff --git a/apps/backend/src/auth/guards/auth.guard.ts b/apps/backend/src/auth/guards/auth.guard.ts new file mode 100644 index 0000000..3ff4336 --- /dev/null +++ b/apps/backend/src/auth/guards/auth.guard.ts @@ -0,0 +1,38 @@ +// auth.guard.ts +import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { Request } from 'express'; +import { UserSessionService } from 'src/user/services/user-session.service'; +import { UserService } from 'src/user/user.service'; + +@Injectable() +export class AuthGuard implements CanActivate { + constructor( + private userService: UserService, + private userSessionService: UserSessionService, + ) { } + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + + // 从 Cookie 读取 session + const sessionId = request.cookies?.['session']; + if (!sessionId) { + throw new UnauthorizedException('登陆凭证无效,请重新登陆'); + } + + // 验证 session + const session = await this.userSessionService.getSession(sessionId); + if (!session) { + throw new UnauthorizedException('登陆凭证无效,请重新登陆'); + } + + // 附加 user 到 req + const user = await this.userService.findOne({ userId: session.userId }); + if (!user) { + throw new UnauthorizedException('用户不存在'); + } + + (request as any).user = { ...user, sessionId }; + return true; + } +} \ No newline at end of file diff --git a/apps/backend/src/auth/guards/optional-auth.guard.ts b/apps/backend/src/auth/guards/optional-auth.guard.ts new file mode 100644 index 0000000..88b04b4 --- /dev/null +++ b/apps/backend/src/auth/guards/optional-auth.guard.ts @@ -0,0 +1,16 @@ +import { ExecutionContext, Injectable } from "@nestjs/common"; +import { AuthGuard } from "./auth.guard"; + +@Injectable() +export class OptionalAuthGuard extends AuthGuard { + async canActivate(context: ExecutionContext): Promise { + try { + return await super.canActivate(context); + } catch (error) { + // 验证失败时,req.user = null,但允许继续 + const request = context.switchToHttp().getRequest(); + request.user = null; + return true; + } + } +} \ No newline at end of file diff --git a/apps/backend/src/auth/strategies/OptionalAuthGuard.ts b/apps/backend/src/auth/strategies/OptionalAuthGuard.ts deleted file mode 100644 index 9609201..0000000 --- a/apps/backend/src/auth/strategies/OptionalAuthGuard.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; -import { AuthGuard } from '@nestjs/passport'; - -@Injectable() -export class OptionalAuthGuard extends AuthGuard('jwt') implements CanActivate { - async canActivate(context: ExecutionContext): Promise { - try { - await super.canActivate(context); - return true; - } catch (error) { - console.error('OptionalAuthGuard error:', error); - return true; // 如果验证失败,仍然允许访问 - } - } - - handleRequest( - err: any, - user: any, - // info: any, - // context: ExecutionContext, - // status?: any, - ): TUser { - if (err || !user) { - return null; // 如果没有用户信息,返回null - } - return user; // 如果有用户信息,返回用户对象 - } -} diff --git a/apps/backend/src/auth/strategies/jwt.strategy.ts b/apps/backend/src/auth/strategies/jwt.strategy.ts deleted file mode 100644 index 6ea59eb..0000000 --- a/apps/backend/src/auth/strategies/jwt.strategy.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { - BadRequestException, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { PassportStrategy } from '@nestjs/passport'; -import { ExtractJwt, Strategy } from 'passport-jwt'; -import { UserSessionService } from 'src/user/services/user-session.service'; -import { UserService } from 'src/user/user.service'; - -@Injectable() -export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { - constructor( - private readonly userService: UserService, - private readonly userSessionService: UserSessionService, - private readonly configService: ConfigService, - ) { - super({ - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), - ignoreExpiration: false, - secretOrKey: configService.get('JWT_SECRET', 'tone-page'), - }); - } - - async validate(payload: any) { - const { userId, sessionId } = payload ?? {}; - - await this.userSessionService.isSessionValid( - userId, - sessionId, - ).catch((e) => { - throw new UnauthorizedException(`${e}`); - }); - - const user = await this.userService.findOne({ userId }); - if (!user) { - throw new BadRequestException('用户不存在'); - } - - return { - ...user, - sessionId, - }; - } -} diff --git a/apps/backend/src/blog/blog.controller.ts b/apps/backend/src/blog/blog.controller.ts index 32e4b51..fd26674 100644 --- a/apps/backend/src/blog/blog.controller.ts +++ b/apps/backend/src/blog/blog.controller.ts @@ -11,11 +11,11 @@ import { UseGuards, } from '@nestjs/common'; import { BlogService } from './blog.service'; -import { OptionalAuthGuard } from 'src/auth/strategies/OptionalAuthGuard'; import { UserService } from 'src/user/user.service'; import { createBlogCommentDto } from './dto/create.blogcomment.dto'; import { Throttle, ThrottlerGuard } from '@nestjs/throttler'; import { BlogPermission } from './blog.permission.enum'; +import { OptionalAuthGuard } from 'src/auth/guards/optional-auth.guard'; @Controller('blog') export class BlogController { diff --git a/apps/backend/src/oss/oss.controller.ts b/apps/backend/src/oss/oss.controller.ts index ac22da3..cee3148 100644 --- a/apps/backend/src/oss/oss.controller.ts +++ b/apps/backend/src/oss/oss.controller.ts @@ -1,12 +1,12 @@ import { Controller, Get, Request, UseGuards } from '@nestjs/common'; import { OssService } from './oss.service'; -import { AuthGuard } from '@nestjs/passport'; +import { AuthGuard } from 'src/auth/guards/auth.guard'; @Controller('oss') export class OssController { - constructor(private readonly ossService: OssService) {} + constructor(private readonly ossService: OssService) { } - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard) @Get('sts') async getStsToken(@Request() req) { const { userId } = req.user; diff --git a/apps/backend/src/oss/oss.module.ts b/apps/backend/src/oss/oss.module.ts index 6d3ae18..d1b6bfd 100644 --- a/apps/backend/src/oss/oss.module.ts +++ b/apps/backend/src/oss/oss.module.ts @@ -1,9 +1,12 @@ import { Module } from '@nestjs/common'; import { OssService } from './oss.service'; import { OssController } from './oss.controller'; +import { AuthModule } from 'src/auth/auth.module'; +import { UserModule } from 'src/user/user.module'; @Module({ providers: [OssService], controllers: [OssController], + imports: [AuthModule, UserModule], }) -export class OssModule {} +export class OssModule { } diff --git a/apps/backend/src/user/services/user-session.service.ts b/apps/backend/src/user/services/user-session.service.ts index b61d855..63f296c 100644 --- a/apps/backend/src/user/services/user-session.service.ts +++ b/apps/backend/src/user/services/user-session.service.ts @@ -17,32 +17,19 @@ export class UserSessionService { return this.userSessionRepository.save(session); } - /** - * @throws string 无效原因 - */ - async isSessionValid(userId: string, sessionId: string): Promise { + async getSession(sessionId: string) { const session = await this.userSessionRepository.findOne({ where: { - userId, sessionId, }, - withDeleted: true, }); - if (session === null) { - throw '登陆凭证无效'; - } - - if (session.deletedAt !== null) { - throw session.disabledReason || '登陆凭证无效'; - } - - return null; + return session; } - async invalidateSession(userId: string, sessionId: string, reason?: string): Promise { + async invalidateSession(sessionId: string, reason?: string): Promise { await this.userSessionRepository.update( - { userId, sessionId, deletedAt: null }, + { sessionId, deletedAt: null }, { deletedAt: new Date(), disabledReason: reason || null, diff --git a/apps/backend/src/user/user.controller.ts b/apps/backend/src/user/user.controller.ts index 4991321..4647fba 100644 --- a/apps/backend/src/user/user.controller.ts +++ b/apps/backend/src/user/user.controller.ts @@ -1,24 +1,24 @@ import { Body, Controller, Get, Put, Request, UseGuards } from '@nestjs/common'; import { UserService } from './user.service'; -import { AuthGuard } from '@nestjs/passport'; import { UpdateUserPasswordDto } from './dto/update-user-password.dto'; import { AuthService } from 'src/auth/auth.service'; +import { AuthGuard } from 'src/auth/guards/auth.guard'; @Controller('user') export class UserController { constructor( private readonly userService: UserService, private readonly authService: AuthService, - ) {} + ) { } - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard) @Get('me') async getMe(@Request() req) { const { user } = req; - return this.userService.findOne({ userId: user.userId }); + return this.userService.findById(user.userId); } - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard) @Put('password') async update(@Request() req, @Body() dto: UpdateUserPasswordDto) { return this.userService.setPassword(req.user.userId, dto.password);