Skip to content

Commit

Permalink
Merge branch '인증-구현'
Browse files Browse the repository at this point in the history
  • Loading branch information
publdaze committed Jul 28, 2023
2 parents 69755f1 + f16f4c9 commit 86d5fe3
Show file tree
Hide file tree
Showing 20 changed files with 741 additions and 36 deletions.
387 changes: 354 additions & 33 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/mongoose": "^10.0.0",
"@nestjs/jwt": "^10.1.0",
"@nestjs/mapped-types": "^2.0.2",
"@nestjs/mongoose": "^10.0.1",
"@nestjs/passport": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^7.1.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"mongoose": "^7.3.4",
"mongoose": "^7.4.0",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1"
},
Expand All @@ -40,6 +46,8 @@
"@types/jest": "^29.5.2",
"@types/lodash": "^4.14.195",
"@types/node": "^20.3.1",
"@types/passport-jwt": "^3.0.9",
"@types/passport-local": "^1.0.35",
"@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.59.11",
"@typescript-eslint/parser": "^5.59.11",
Expand Down
7 changes: 6 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { BuildingsModule } from './buildings/buildings.module';
import { FloorsModule } from './buildings/floors/floors.module';
import { MongooseModule } from '@nestjs/mongoose';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';

@Module({
imports: [
Expand All @@ -21,6 +23,9 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
}),
inject: [ConfigService],
}),
UsersModule,
AuthModule,
],
})
export class AppModule {}
export class AppModule {
}
18 changes: 18 additions & 0 deletions src/auth/auth.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';

describe('AuthController', () => {
let controller: AuthController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
}).compile();

controller = module.get<AuthController>(AuthController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
25 changes: 25 additions & 0 deletions src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Body, Controller, Get, Post, Req, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { RolesGuard } from './passport/role.guard';
import { Roles } from '../users/entities/authorities';

@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {
}

@UseGuards(AuthGuard('jwt'), RolesGuard)
@Roles(['ROLE_USER', 'ROLE_CLEANER', 'ROLE_ADMIN'])
@Get('profile')
getProfile(@Req() req) {
console.log(req);
return req.user;
}

@Post('signIn')
async login(@Body() req) {
return this.authService.login(req); // 1
// return req.user;
}
}
29 changes: 29 additions & 0 deletions src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Module } from '@nestjs/common';
import { JwtStrategy } from './passport/jwt.strategy';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthRepository } from './auth.repository';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from '../users/entities/user.entity';

@Module({
imports: [
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
UsersModule,
PassportModule,
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
secret: configService.get('JWT_SECRET_KEY'),
}),
}),
],
providers: [JwtStrategy, AuthService, AuthRepository],
controllers: [AuthController],
})
export class AuthModule {}
26 changes: 26 additions & 0 deletions src/auth/auth.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User, UserDocument } from '../users/entities/user.entity';

@Injectable()
export class AuthRepository {
constructor(
@InjectModel(User.name) private usersModel: Model<UserDocument>,
) {}

async findByLoginIdAndPassword(user: {
loginId: string;
password: string;
}): Promise<UserDocument[]> {
try {
const { loginId, password } = user;
return await this.usersModel.find({
loginId,
password,
});
} catch (err) {
console.log('error...');
}
}
}
18 changes: 18 additions & 0 deletions src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';

describe('AuthService', () => {
let service: AuthService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();

service = module.get<AuthService>(AuthService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
35 changes: 35 additions & 0 deletions src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { AuthRepository } from './auth.repository';

@Injectable()
export class AuthService {
constructor(
private readonly jwtService: JwtService,
private readonly authRepository: AuthRepository,
) {}

async login(user: { loginId: string; password: string }) {
console.log(user);
const userAccounts = this.authRepository.findByLoginIdAndPassword(user);
return userAccounts
.then((res) => {
if (res.length === 0) {
throw `계정 정보가 없습니다. 입력하신 loginId: ${user.loginId}, password: ${user.password}`;
}
const userByFindFirst = res[0];
const payload = {
username: userByFindFirst.username,
email: userByFindFirst.email,
role: userByFindFirst.role,
};
return {
userByFindFirst,
access_token: this.jwtService.sign(payload),
};
})
.catch((err) => {
throw `Error 로그인 in service:, ${err}`;
});
}
}
23 changes: 23 additions & 0 deletions src/auth/passport/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: configService.get('JWT_SECRET_KEY'),
});
}

async validate(payload: any) {
return {
email: payload.email,
username: payload.username,
role: payload.role,
};
}
}
26 changes: 26 additions & 0 deletions src/auth/passport/role.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import { User } from '../../users/entities/user.entity';

@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}

canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const roles = this.reflector.get<string[]>('roles', context.getHandler());

if (!roles) {
// roles가 아니면 true를 리턴하고 진행한다.
return true;
}

const request = context.switchToHttp().getRequest();
// console.log('request + ', request);
const user = request.user as User;

return user && user.role && roles.includes(user.role);
}
}
19 changes: 19 additions & 0 deletions src/users/dtos/create-user.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IsEmail, IsEnum, IsString } from 'class-validator';
import { Authorities, Role } from '../entities/authorities';

export class CreateUserDto {
@IsEmail()
readonly email: string;

@IsString()
readonly loginId: string;

@IsString()
readonly password: string;

@IsString()
readonly username: string;

@IsEnum(Authorities)
readonly role: Role;
}
7 changes: 7 additions & 0 deletions src/users/entities/authorities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { SetMetadata } from '@nestjs/common';

export type Role = 'ROLE_USER' | 'ROLE_ADMIN' | 'ROLE_CLEANER';

export const Authorities: Role[] = ['ROLE_USER', 'ROLE_ADMIN', 'ROLE_CLEANER'];

export const Roles = (roles: Role[]): any => SetMetadata('roles', roles);
31 changes: 31 additions & 0 deletions src/users/entities/user.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import mongoose from 'mongoose';
import { Role } from './authorities';

export type UserDocument = User & Document;

@Schema({ timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } })
export class User {
@Prop()
email: string;

@Prop()
loginId: string;

@Prop()
password: string;

@Prop()
username: string;

@Prop()
role: Role;

@Prop({ default: new Date(), type: mongoose.Schema.Types.Date })
createdAt: Date;

@Prop({ default: new Date(), type: mongoose.Schema.Types.Date })
updatedAt: Date;
}

export const UserSchema = SchemaFactory.createForClass(User);
18 changes: 18 additions & 0 deletions src/users/users.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UsersController } from './users.controller';

describe('UsersController', () => {
let controller: UsersController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
}).compile();

controller = module.get<UsersController>(UsersController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
14 changes: 14 additions & 0 deletions src/users/users.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Body, Controller, Post } from '@nestjs/common';
import { CreateUserDto } from './dtos/create-user.dto';
import { User } from './entities/user.entity';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
constructor(private readonly userService: UsersService) {}

@Post()
async create(@Body() userData: CreateUserDto): Promise<User> {
return await this.userService.create(userData);
}
}
15 changes: 15 additions & 0 deletions src/users/users.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { MongooseModule } from '@nestjs/mongoose';
import { UsersRepository } from './users.repository';
import { User, UserSchema } from './entities/user.entity';

@Module({
imports: [
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
],
controllers: [UsersController],
providers: [UsersService, UsersRepository],
})
export class UsersModule {}
25 changes: 25 additions & 0 deletions src/users/users.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User, UserDocument } from './entities/user.entity';

@Injectable()
export class UsersRepository {
constructor(
@InjectModel(User.name) private usersModel: Model<UserDocument>,
) {}

save(user: {
email: string;
loginId: string;
password: string;
username: string;
}): Promise<UserDocument> {
try {
const result = new this.usersModel(user);
return result.save();
} catch (err) {
console.log('error...');
}
}
}
Loading

0 comments on commit 86d5fe3

Please sign in to comment.