Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#4] 2.03 로그인 기능 구현 #33

Merged
merged 46 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
959e10d
✨ feat: module, controller, service, entity, repository 초기 설정 및 의존성 주…
jinddings Nov 6, 2024
8fb0b1f
✨ feat: 회원 가입 레포지토리 구현
jinddings Nov 6, 2024
d169098
✨ feat: userRepository registerUser 메소드 구현 (#26)
jinddings Nov 6, 2024
643ad3c
Merge branch 'back/main' of https://github.com/boostcampwm-2024/web16…
jinddings Nov 6, 2024
a71bf2c
✨ feat: AuthService 회원가입 로직 구현(#26)
jinddings Nov 6, 2024
92c7549
⚙️ chore: AuthModule을 AppModule에 추가(#26)
jinddings Nov 6, 2024
bf295a6
✨ feat: AuthController 구현 및 AuthService에서 비밀번호 암호화 로직 추가(#26)
jinddings Nov 6, 2024
08312cd
⚙️ chore: merge conflict 해결
sieunie Nov 5, 2024
4390c3c
✨ feat: stock websocket gateway 구현 #6
sieunie Nov 5, 2024
3a777d6
✨ feat: 주가 그래프 웹소켓 구현 #6
sieunie Nov 5, 2024
ad2b3e6
✨ feat: 주가 지수 값 웹소켓 구현 #6
sieunie Nov 5, 2024
5ef98b0
✨ feat: 주가 지수 그래프 및 값 API 구현 #6
sieunie Nov 5, 2024
2796724
📝 docs: 주가 지수 API 명세 추가
sieunie Nov 5, 2024
bad5bb0
⚙️ chore: ws 라이브러리 설치
sieunie Nov 5, 2024
c9a16a1
♻️ refactor: cron 로직 service 단으로 이동
sieunie Nov 5, 2024
7117aac
♻️ refactor: dto 파일 분리
sieunie Nov 6, 2024
c219c6d
✨ feat: 주가 지수 소켓 통신으로 변경
sieunie Nov 6, 2024
335686b
♻️ refactor: return type dto로 변경
sieunie Nov 6, 2024
6aafebe
♻️ refactor: 주가 정보 관련 interface 생성 및 적용 #6
sieunie Nov 6, 2024
226ce86
✨ feat: 가입 요청 유저 정보에 대한 유효성 검사 추가 (#26)
jinddings Nov 6, 2024
60c03e1
🔧 fix: 타입 버그 수정
sieunie Nov 6, 2024
a86bf21
✨ feat: access_token을 발급받는 로직 구현#12
uuuo3o Nov 6, 2024
414b03c
✨ feat: 오늘의 상/하위 종목 조회에 사용할 DTO 구현#12
uuuo3o Nov 6, 2024
2af0a7d
✨ feat: 한국투자 API 요청 및 결과값을 받아오는 로직 구현#12
uuuo3o Nov 6, 2024
f8c7046
✨ feat: 오늘의 상/하위 종목 리스트 API Controller 구현#12
uuuo3o Nov 6, 2024
ba8e2d6
⚙️ chore: TopFiveModule을 AppModule에 추가#12
uuuo3o Nov 6, 2024
eee8101
⚙️ chore:lint 실행시 발생하는 오류 수정
jinddings Nov 6, 2024
4b1ed0e
Merge branch 'back/main' of https://github.com/boostcampwm-2024/web16…
jinddings Nov 6, 2024
7736420
✨ feat: 상/하위 종목 5개만 반환할 수 있도록 수정#12
uuuo3o Nov 6, 2024
5003ca6
✨ feat: UserRepository loginUser 메소드 구현(#4)
jinddings Nov 6, 2024
e2a14e8
➕ add: module layer 추가 #6
sieunie Nov 6, 2024
93141c4
➕ add: 코스피200, KSQ150 지수 정보 추가 #6
sieunie Nov 6, 2024
9e72aff
⚙️ chore: 린트 오류 해결
sieunie Nov 6, 2024
8b956ed
✨ feat: JWT 토큰을 이용한 로그인 기능 구현(#4)
jinddings Nov 6, 2024
3c1ffcb
💡 comment: Console.error 주석 처리#12
uuuo3o Nov 6, 2024
e4ba28a
✨ feat: AuthGuard 를 이용한 토큰 인증 기능 구현(#4)
jinddings Nov 6, 2024
8b121d1
⚙️ chore: lint,test 에러 발생 수정
jinddings Nov 6, 2024
4375e5f
🔧 fix: 주석처리 했던 코드를 logger로 변경#12
uuuo3o Nov 6, 2024
9887ea6
� docs: 로그인 API 명세 추가
jinddings Nov 6, 2024
83d246a
Merge pull request #30 from boostcampwm-2024/feature/api/topfive-#12
uuuo3o Nov 7, 2024
cc860a5
Merge branch 'back/main' into feature/api/stockindex-#6
sieunie Nov 7, 2024
a190aa7
Merge pull request #32 from boostcampwm-2024/feature/api/stockindex-#6
sieunie Nov 7, 2024
e817f4b
Merge branch 'back/main' into feature/api/register-#26
jinddings Nov 7, 2024
e9f5950
Merge pull request #29 from boostcampwm-2024/feature/api/register-#26
jinddings Nov 7, 2024
81cc0ef
⚙️ chore: merge 오류 해결
sieunie Nov 7, 2024
abbc19b
⚙️ chore: merge 오류 해결
sieunie Nov 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,120 changes: 1,078 additions & 42 deletions BE/package-lock.json

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions BE/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,23 @@
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^8.0.1",
"@nestjs/typeorm": "^10.0.2",
"@types/passport-jwt": "^4.0.1",
"axios": "^1.7.7",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"cross-env": "^7.0.3",
"docker": "^1.0.0",
"dotenv": "^16.4.5",
"fastify-swagger": "^5.1.1",
"mysql2": "^3.11.3",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"swagger-ui-express": "^5.0.1",
Expand All @@ -41,6 +49,7 @@
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/bcrypt": "^5.0.2",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
Expand Down
5 changes: 4 additions & 1 deletion BE/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
import { User } from './auth/user.entity';

@Module({
imports: [
Expand All @@ -14,9 +16,10 @@ import { AppService } from './app.service';
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWD,
database: process.env.DB_DATABASE,
entities: [],
entities: [User],
synchronize: true,
}),
AuthModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
38 changes: 38 additions & 0 deletions BE/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
Controller,
Post,
Get,
Body,
Req,
ValidationPipe,
UseGuards,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { AuthCredentialsDto } from './dto/authCredentials.dto';
import { ApiOperation } from '@nestjs/swagger';

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

@ApiOperation({ summary: '회원 가입 API' })
@Post('/signup')
signUp(@Body(ValidationPipe) authCredentialsDto: AuthCredentialsDto) {
return this.authService.signUp(authCredentialsDto);
}
@ApiOperation({ summary: '로그인 API' })
@Get('/login')
loginWithCredentials(
@Body(ValidationPipe) authCredentialsDto: AuthCredentialsDto,
) {
return this.authService.loginUser(authCredentialsDto);
}

@ApiOperation({ summary: 'Token 인증 테스트 API' })
@Get('/test')
@UseGuards(AuthGuard())
test(@Req() req: Request) {
return req;
}
}
26 changes: 26 additions & 0 deletions BE/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { User } from './user.entity';
import { UserRepository } from './user.repository';
import { JwtStrategy } from './jwt.strategy';

@Module({
imports: [
TypeOrmModule.forFeature([User]),
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.register({
secret: 'Juga16',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 얘가 혹시 JWT secret key인가요???? 이거는 노출되면 안될 거 같은데..ㅎㅎㅎㅎ

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정하겠습니다!

signOptions: {
expiresIn: 3600,
},
}),
],
controllers: [AuthController],
providers: [AuthService, UserRepository, JwtStrategy],
exports: [JwtStrategy, PassportModule],
})
export class AuthModule {}
33 changes: 33 additions & 0 deletions BE/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { UserRepository } from './user.repository';
import { AuthCredentialsDto } from './dto/authCredentials.dto';

@Injectable()
export class AuthService {
constructor(
@InjectRepository(UserRepository)
private userRepository: UserRepository,
private jwtService: JwtService,
) {}

async signUp(authCredentialsDto: AuthCredentialsDto): Promise<void> {
return this.userRepository.registerUser(authCredentialsDto);
}

async loginUser(
authCredentialsDto: AuthCredentialsDto,
): Promise<{ accessToken: string }> {
const { email, password } = authCredentialsDto;
const user = await this.userRepository.findOne({ where: { email } });

if (user && (await bcrypt.compare(password, user.password))) {
const payload = { email };
const accessToken = this.jwtService.sign(payload);
return { accessToken };
}
throw new UnauthorizedException('Please check your login credentials');
}
}
27 changes: 27 additions & 0 deletions BE/src/auth/dto/authCredentials.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IsString, Matches, MaxLength, MinLength } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class AuthCredentialsDto {
@ApiProperty({
description: '유저 이메일',
minLength: 4,
maxLength: 20,
type: 'string',
})
@IsString()
@MinLength(4)
@MaxLength(20)
email: string;

@ApiProperty({
description: '유저 비밀번호',
minLength: 4,
maxLength: 20,
type: 'string',
})
@IsString()
@MinLength(4)
@MaxLength(20)
@Matches(/^[a-zA-Z0-9]*$/)
password: string;
}
26 changes: 26 additions & 0 deletions BE/src/auth/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { PassportStrategy } from '@nestjs/passport';
import { InjectRepository } from '@nestjs/typeorm';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UserRepository } from './user.repository';
import { User } from './user.entity';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
@InjectRepository(UserRepository) private userRepository: UserRepository,
) {
super({
secretOrKey: 'Juga16',
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
});
}

async validate(payload) {
const { email } = payload;
const user: User = await this.userRepository.findOne({ where: { email } });
if (!user) throw new UnauthorizedException();

return user;
}
}
19 changes: 19 additions & 0 deletions BE/src/auth/user.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;

@Column()
email: string;

@Column()
password: string;

@Column({ default: false })
tutorial: boolean;

@Column({ default: -1 })
kakaoId: number;
}
21 changes: 21 additions & 0 deletions BE/src/auth/user.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Injectable } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { User } from './user.entity';
import { AuthCredentialsDto } from './dto/authCredentials.dto';

@Injectable()
export class UserRepository extends Repository<User> {
constructor(@InjectDataSource() dataSource: DataSource) {
super(User, dataSource.createEntityManager());
}

async registerUser(authCredentialsDto: AuthCredentialsDto) {
const { email, password } = authCredentialsDto;
const salt: string = await bcrypt.genSalt();
const hashedPassword: string = await bcrypt.hash(password, salt);
const user = this.create({ email, password: hashedPassword });
await this.save(user);
}
}