refactor: 重构后端鉴权方式
This commit is contained in:
@@ -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",
|
||||
|
||||
96
apps/backend/pnpm-lock.yaml
generated
96
apps/backend/pnpm-lock.yaml
generated
@@ -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:
|
||||
|
||||
@@ -19,10 +19,10 @@ 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) { }
|
||||
|
||||
@@ -9,17 +9,17 @@ 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) { }
|
||||
|
||||
@@ -9,15 +9,15 @@ 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) { }
|
||||
|
||||
@@ -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: [
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<string>('JWT_SECRET', 'tone-page'),
|
||||
signOptions: {
|
||||
expiresIn: configService.get<string>('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 { }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
38
apps/backend/src/auth/guards/auth.guard.ts
Normal file
38
apps/backend/src/auth/guards/auth.guard.ts
Normal file
@@ -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<boolean> {
|
||||
const request = context.switchToHttp().getRequest<Request>();
|
||||
|
||||
// 从 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;
|
||||
}
|
||||
}
|
||||
16
apps/backend/src/auth/guards/optional-auth.guard.ts
Normal file
16
apps/backend/src/auth/guards/optional-auth.guard.ts
Normal file
@@ -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<boolean> {
|
||||
try {
|
||||
return await super.canActivate(context);
|
||||
} catch (error) {
|
||||
// 验证失败时,req.user = null,但允许继续
|
||||
const request = context.switchToHttp().getRequest();
|
||||
request.user = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<boolean> {
|
||||
try {
|
||||
await super.canActivate(context);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('OptionalAuthGuard error:', error);
|
||||
return true; // 如果验证失败,仍然允许访问
|
||||
}
|
||||
}
|
||||
|
||||
handleRequest<TUser = any>(
|
||||
err: any,
|
||||
user: any,
|
||||
// info: any,
|
||||
// context: ExecutionContext,
|
||||
// status?: any,
|
||||
): TUser {
|
||||
if (err || !user) {
|
||||
return null; // 如果没有用户信息,返回null
|
||||
}
|
||||
return user; // 如果有用户信息,返回用户对象
|
||||
}
|
||||
}
|
||||
@@ -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<string>('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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) { }
|
||||
|
||||
@UseGuards(AuthGuard('jwt'))
|
||||
@UseGuards(AuthGuard)
|
||||
@Get('sts')
|
||||
async getStsToken(@Request() req) {
|
||||
const { userId } = req.user;
|
||||
|
||||
@@ -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 { }
|
||||
|
||||
@@ -17,32 +17,19 @@ export class UserSessionService {
|
||||
return this.userSessionRepository.save(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws string 无效原因
|
||||
*/
|
||||
async isSessionValid(userId: string, sessionId: string): Promise<void> {
|
||||
async getSession(sessionId: string) {
|
||||
const session = await this.userSessionRepository.findOne({
|
||||
where: {
|
||||
userId,
|
||||
sessionId,
|
||||
},
|
||||
withDeleted: true,
|
||||
});
|
||||
|
||||
if (session === null) {
|
||||
throw '登陆凭证无效';
|
||||
return session;
|
||||
}
|
||||
|
||||
if (session.deletedAt !== null) {
|
||||
throw session.disabledReason || '登陆凭证无效';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async invalidateSession(userId: string, sessionId: string, reason?: string): Promise<void> {
|
||||
async invalidateSession(sessionId: string, reason?: string): Promise<void> {
|
||||
await this.userSessionRepository.update(
|
||||
{ userId, sessionId, deletedAt: null },
|
||||
{ sessionId, deletedAt: null },
|
||||
{
|
||||
deletedAt: new Date(),
|
||||
disabledReason: reason || null,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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 {
|
||||
@@ -11,14 +11,14 @@ export class UserController {
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user