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

[BE] be -> develop #58

Merged
merged 36 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d6b401f
fix : ddl-auto create로 변경
kssumin Oct 4, 2023
5222248
feat : develop db 환경 세팅
kssumin Oct 4, 2023
4c709ca
feat : docker file 생성
kssumin Oct 4, 2023
311b07c
feat : docker-compose 파일 생성
kssumin Oct 4, 2023
6e6710e
fix : create database sql문 삭제
capDoYeonLee Oct 4, 2023
a70eddf
Merge pull request #49 from JNU-econovation/feat/be-33-docker-image
capDoYeonLee Oct 4, 2023
90528d2
fix : active on profile 설정 추가
kssumin Oct 5, 2023
52dbac4
Merge pull request #52 from JNU-econovation/fix/be-51-yml-profile
kssumin Oct 5, 2023
a355277
fix : docker-compose.yml 포트 수정
Oct 6, 2023
ab0b48d
fix : dev, local시 validTime 변경 및 refreshtoken secretkey 제거
kssumin Oct 6, 2023
bafb410
fix : table명 별도로 지정
kssumin Oct 6, 2023
5d16678
feat : nickname, email unique index 추가
kssumin Oct 6, 2023
d23b1eb
fix : usercontroller -> authcontroller로 클래스명 변경
kssumin Oct 6, 2023
4f89d4b
feat : email, nickname에 index 추가
kssumin Oct 6, 2023
b5b9125
fix : refreshtoken시 userrole 제거 및 refreshtoken, accessToken secretkey 동일
kssumin Oct 6, 2023
660a566
Merge pull request #54 from JNU-econovation/fix/be-53-dockercompose
kssumin Oct 6, 2023
8c8422f
fix : access, refresh token validTime 별도로 설정
kssumin Oct 6, 2023
95fc9ae
feat : 토큰 만료시간이 지났을 경우 예외처리
kssumin Oct 6, 2023
9c69160
feat : 토큰이 만료되었는지 validation
kssumin Oct 6, 2023
f349cd0
test : 토큰 생성 테스트 코드
kssumin Oct 6, 2023
628e040
test : 토큰에서 사용자 정보 가져오기 테스트 코드
kssumin Oct 6, 2023
47d47d3
feat : cookie 에서 refresh token 추출 구현
kssumin Oct 6, 2023
54e2c3c
test : cookie extractor 테스트 코드 작성
kssumin Oct 6, 2023
d7884cf
fix : request header에 cookie자체가 존재하지 않을 때 예외처리
kssumin Oct 6, 2023
af2d5e7
test/fix : header에 cookie가 존재하지 않을 때 예외발생으로 name 변경
kssumin Oct 6, 2023
ede4c5e
test/feat : refresh token cookie가 존재하지 않을 경우 예외 테스트
kssumin Oct 6, 2023
345ef66
feat : userid, token에 해당하는 entity가져오기 repository 구현
kssumin Oct 6, 2023
8e20c5c
fix : table 명 변경
kssumin Oct 6, 2023
1374f34
feat : token 생성 service 구현
kssumin Oct 6, 2023
8e3fe64
feat : authorization exception 구현
kssumin Oct 6, 2023
45b373a
fix : createTokenService로 token 생성 책임 할당
kssumin Oct 6, 2023
22d69d3
feat : token, user 정보 일치 시 새로운 token 발급 구현
kssumin Oct 6, 2023
0ec36d5
fix : token 재발급 시 기존 토큰 삭제
kssumin Oct 6, 2023
ea3f125
fix : security 설정에 cors 설정
kssumin Oct 6, 2023
426dc8e
Merge pull request #55 from JNU-econovation/feat/be-35-reissue
kssumin Oct 6, 2023
6ee7855
Merge pull request #57 from JNU-econovation/fix/be-56-cors
kssumin Oct 6, 2023
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
25 changes: 0 additions & 25 deletions .github/workflows/main.yml

This file was deleted.

4 changes: 4 additions & 0 deletions be/overflow/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM openjdk:17-oracle
ARG JAR_FILE=/build/libs/overflow-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
35 changes: 35 additions & 0 deletions be/overflow/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
version: '3.1'
services:
mysql:
container_name: overflow-mysql-dev
image: mysql/mysql-server:8.0.27
environment:
- MYSQL_DATABASE=overflow
- MYSQL_ROOT_HOST=%
- MYSQL_ROOT_PASSWORD=root
command: [ "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci", "--lower_case_table_names=1", "--max_connections=2048", "--wait_timeout=3600" ]
ports:
- "13307:3306"
volumes: #볼륨 지정
- ./resources/develop-environment/mysql-init.d:/docker-entrypoint-initdb.d
networks: #사용할 네트워크 지정
- overflow-network
backend:
build:
context: .
dockerfile: Dockerfile
container_name: overflow-app-dev
ports:
- "8080:8080"
depends_on:
- mysql
restart: always
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://overflow-mysql-dev:3306/overflow?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false&allowPublicKeyRetrieval=true
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: root
SPRING_PROFILES_ACTIVE: dev
networks: #사용할 네트워크 지정
- overflow-network
networks:
overflow-network:
13 changes: 13 additions & 0 deletions be/overflow/resources/develop-environment/mysql-init.d/00_init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CREATE
USER 'overflow-local'@'localhost' IDENTIFIED BY 'root';
CREATE
USER 'overflow-local'@'%' IDENTIFIED BY 'root';

GRANT ALL PRIVILEGES ON *.* TO
'overflow-local'@'localhost';
GRANT ALL PRIVILEGES ON *.* TO
'overflow-local'@'%';

-- CREATE
-- DATABASE overflow DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
create table auth_info_tb
(
auth_info_id BIGINT not null auto_increment,
user_id BIGINT not null,
auth_info_type varchar(255) not null,
auth_info_token varchar(255) not null,
created_date datetime not null,
deleted bit not null,
updated_date datetime not null,
primary key (auth_info_id)
) ENGINE = InnoDB;

create table user_tb
(
user_id BIGINT not null auto_increment,
user_email varchar(255) not null,
user_nickname varchar(255) not null,
user_password varchar(255) not null,
created_date datetime not null,
deleted bit not null,
updated_date datetime not null,
primary key (user_id)
) ENGINE = InnoDB;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.econovation.overflow.auth.domain.exception;

import com.econovation.overflow.common.exception.BusinessException;
import org.springframework.http.HttpStatus;

public class AuthorizationException extends BusinessException {

public AuthorizationException(String message) {
super(message, HttpStatus.NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.econovation.overflow.auth.domain.service;

import com.econovation.overflow.auth.domain.dto.converter.TokenConverter;
import com.econovation.overflow.auth.domain.dto.response.TokenResponse;
import com.econovation.overflow.auth.persistence.converter.AuthInfoEntityConverter;
import com.econovation.overflow.auth.persistence.entity.AuthInfoEntity;
import com.econovation.overflow.auth.persistence.repository.AuthInfoRepository;
import com.econovation.overflow.security.authority.UserRole;
import com.econovation.overflow.security.token.TokenProvider;
import com.econovation.overflow.security.token.TokenResolver;
import java.util.Collections;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CreateTokenService {
private final TokenProvider tokenProvider;
private final TokenConverter tokenConverter;
private final TokenResolver tokenResolver;
private final AuthInfoRepository authInfoRepository;
private final AuthInfoEntityConverter authInfoEntityConverter;

@Transactional
public TokenResponse execute(final Long userId) {
String accessToken =
tokenProvider.createAccessToken(userId, Collections.singletonList(UserRole.USER));
String refreshToken = tokenProvider.createRefreshToken(userId);

saveToken(userId, refreshToken);

return tokenConverter.from(
accessToken, tokenResolver.getExpiredDate(accessToken), refreshToken);
}

private void saveToken(Long userId, String token) {
AuthInfoEntity authInfoEntity = authInfoEntityConverter.from(userId, token);
authInfoRepository.save(authInfoEntity);
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package com.econovation.overflow.auth.domain.usecase;

import com.econovation.overflow.auth.domain.dto.converter.TokenConverter;
import com.econovation.overflow.auth.domain.dto.request.LoginUserRequest;
import com.econovation.overflow.auth.domain.dto.response.TokenResponse;
import com.econovation.overflow.auth.domain.exception.NotFoundEmailException;
import com.econovation.overflow.auth.domain.exception.NotFoundPasswordException;
import com.econovation.overflow.auth.domain.service.SaveTokenService;
import com.econovation.overflow.auth.domain.service.CreateTokenService;
import com.econovation.overflow.auth.persistence.entity.UserEntity;
import com.econovation.overflow.auth.persistence.repository.UserRepository;
import com.econovation.overflow.security.authority.UserRole;
import com.econovation.overflow.security.token.TokenProvider;
import com.econovation.overflow.security.token.TokenResolver;
import java.util.Collections;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
Expand All @@ -22,12 +17,9 @@
@Transactional(readOnly = true)
public class LoginUserUseCase {

private final TokenProvider tokenProvider;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final TokenConverter tokenConverter;
private final TokenResolver tokenResolver;
private final SaveTokenService saveTokenService;
private final CreateTokenService createTokenService;

@Transactional
public TokenResponse execute(final LoginUserRequest request) {
Expand All @@ -38,17 +30,7 @@ public TokenResponse execute(final LoginUserRequest request) {

validPassword(request.getPassword(), userEntity);

String accessToken =
tokenProvider.createAccessToken(
userEntity.getId(), Collections.singletonList(UserRole.USER));
String refreshToken =
tokenProvider.createRefreshToken(
userEntity.getId(), Collections.singletonList(UserRole.USER));

saveTokenService.execute(userEntity.getId(), refreshToken);

return tokenConverter.from(
accessToken, tokenResolver.getExpiredDate(accessToken), refreshToken);
return createTokenService.execute(userEntity.getId());
}

private void validPassword(final String requestPassword, final UserEntity userEntity) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.econovation.overflow.auth.domain.usecase;

import com.econovation.overflow.auth.domain.dto.response.TokenResponse;
import com.econovation.overflow.auth.domain.exception.AuthorizationException;
import com.econovation.overflow.auth.domain.service.CreateTokenService;
import com.econovation.overflow.auth.persistence.entity.AuthInfoEntity;
import com.econovation.overflow.auth.persistence.repository.AuthInfoRepository;
import com.econovation.overflow.security.token.TokenResolver;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class ReissueUseCase {
private final CreateTokenService createTokenService;
private final TokenResolver tokenResolver;
private final AuthInfoRepository authInfoRepository;

@Transactional
public TokenResponse execute(final String token) {
Long userId = tokenResolver.getUserInfo(token);

AuthInfoEntity authInfoEntity = authInfoRepository
.findByUserIdAndToken(userId, token)
.orElseThrow(() -> new AuthorizationException("잘못된 토큰 입니다"));

authInfoRepository.delete(authInfoEntity);
return createTokenService.execute(userId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

import static com.econovation.overflow.auth.persistence.entity.AuthInfoEntity.ENTITY_PREFIX;

import com.econovation.overflow.common.BaseEntity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand All @@ -24,7 +24,8 @@
@ToString
@SuperBuilder(toBuilder = true)
@Entity(name = ENTITY_PREFIX + "_entity")
public class AuthInfoEntity extends BaseEntity {
@Table(name = ENTITY_PREFIX + "_tb")
public class AuthInfoEntity {

public static final String ENTITY_PREFIX = "auth_info";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
Expand All @@ -21,6 +23,12 @@
@ToString
@SuperBuilder(toBuilder = true)
@Entity(name = ENTITY_PREFIX + "_entity")
@Table(
name = ENTITY_PREFIX + "_tb",
indexes = {
@Index(name = "idx_nickname", columnList = ENTITY_PREFIX + "_nickname", unique = true),
@Index(name = "idx_email", columnList = ENTITY_PREFIX + "_email", unique = true)
})
public class UserEntity extends BaseEntity {

public static final String ENTITY_PREFIX = "user";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.econovation.overflow.auth.persistence.repository;

import com.econovation.overflow.auth.persistence.entity.AuthInfoEntity;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AuthInfoRepository extends JpaRepository<AuthInfoEntity, Long> {}
public interface AuthInfoRepository extends JpaRepository<AuthInfoEntity, Long> {
Optional<AuthInfoEntity> findByUserIdAndToken(Long userId, String token);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import com.econovation.overflow.auth.domain.dto.request.LoginUserRequest;
import com.econovation.overflow.auth.domain.dto.response.TokenResponse;
import com.econovation.overflow.auth.domain.usecase.LoginUserUseCase;
import com.econovation.overflow.auth.domain.usecase.ReissueUseCase;
import com.econovation.overflow.auth.web.support.CookieExtractor;
import com.econovation.overflow.common.support.respnose.ApiResponse;
import com.econovation.overflow.common.support.respnose.ApiResponseBody.SuccessBody;
import com.econovation.overflow.common.support.respnose.ApiResponseGenerator;
import com.econovation.overflow.common.support.respnose.MessageCode;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand All @@ -19,10 +22,12 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/auth")
public class LoginController {
public class AuthController {
private static final String REFRESH_TOKEN = "refreshToken";
private static final int REFRESH_TOKEN_EXPIRATION = 7 * 24 * 60 * 60;
private final LoginUserUseCase loginUserUseCase;
private final ReissueUseCase reissueUseCase;
private final CookieExtractor cookieExtractor;

@PostMapping("/login")
public ApiResponse<SuccessBody<TokenResponse>> signIn(
Expand All @@ -33,6 +38,17 @@ public ApiResponse<SuccessBody<TokenResponse>> signIn(
tokenResponse, HttpStatus.OK, MessageCode.CREATE, cookie.toString());
}

@PostMapping("/reissue")
public ApiResponse<SuccessBody<TokenResponse>> reissue(HttpServletRequest request) {

final String token = cookieExtractor.extract(request);
final TokenResponse tokenResponse = reissueUseCase.execute(token);
final ResponseCookie cookie = putTokenInCookie(tokenResponse);

return ApiResponseGenerator.success(
tokenResponse, HttpStatus.OK, MessageCode.CREATE, cookie.toString());
}

private ResponseCookie putTokenInCookie(final TokenResponse tokenResponse) {
return ResponseCookie.from(REFRESH_TOKEN, tokenResponse.getRefreshToken())
.maxAge(REFRESH_TOKEN_EXPIRATION)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.econovation.overflow.auth.web.support;

import com.econovation.overflow.auth.domain.exception.AuthorizationException;
import java.util.Arrays;
import java.util.Optional;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;

@Component
public class CookieExtractor implements TokenExtractor {
private static final String REFRESH_KEY = "refreshToken";

@Override
public String extract(HttpServletRequest request) {
Cookie[] cookies = getCookies(request);
Optional<Cookie> tokenCookie =
Arrays.stream(cookies).filter(cookie -> cookie.getName().equals(REFRESH_KEY)).findAny();

Cookie refreshCookie =
tokenCookie.orElseThrow(() -> new AuthorizationException("토큰이 존재하지 않습니다."));

return getValue(refreshCookie);
}

private Cookie[] getCookies(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies == null) {
throw new AuthorizationException("토큰이 존재하지 않습니다");
}
return cookies;
}

private String getValue(Cookie cookie) {
String token = cookie.getValue();
validNullToken(token);
return token;
}

private void validNullToken(String token) {
if (token == null) {
throw new AuthorizationException("토큰이 존재하지 않습니다");
}
}
}
Loading