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

feat : redis 연동 및 로그인 #19

Merged
merged 23 commits into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
56363d6
feat : redis setting
GaBaljaintheroom Jun 5, 2024
67e951a
feat : accessToken 만료 시 토큰 재발급
GaBaljaintheroom Jun 5, 2024
1b4b902
feat : user entity 수정
GaBaljaintheroom Jun 5, 2024
4df94b2
feat : jwt 관련 의존성 및 코드 user-api 모듈로 이동
GaBaljaintheroom Jun 5, 2024
569689b
feat : 로그인 후 토큰 발급
GaBaljaintheroom Jun 5, 2024
8b85938
refactor : redis 연결 오류 수정
GaBaljaintheroom Jun 5, 2024
8ea3bbd
refactor : pr 리뷰 후 수정
GaBaljaintheroom Jun 7, 2024
7bb9e27
refactor : SocialCredentials 수정
GaBaljaintheroom Jun 9, 2024
d9314ad
refactor : BaseEntity persistable 추
GaBaljaintheroom Jun 9, 2024
f0cc889
refactor : common-api 모듈 추가
GaBaljaintheroom Jun 9, 2024
754672e
refactor : 도메인 모듈 import 추가
GaBaljaintheroom Jun 9, 2024
5c51539
refactor : Infrastructure 의존성 수정
GaBaljaintheroom Jun 9, 2024
b644a49
refactor : 코드 리뷰 후 수정
GaBaljaintheroom Jun 11, 2024
684290e
refactor : BaseEntity isDeleted 필드 추가
GaBaljaintheroom Jun 14, 2024
07e7ce4
refactor : User 엔티티 기본값 부여
GaBaljaintheroom Jun 14, 2024
04a3461
refactor : Infrastructure 모듈 분리
GaBaljaintheroom Jun 14, 2024
b9776e1
refactor : login dto 분리
GaBaljaintheroom Jun 14, 2024
bf45cff
refactor : refreshToken 비교 로직 추가
GaBaljaintheroom Jun 14, 2024
9bae617
refactor : common-api 모듈 의존성 삭제
GaBaljaintheroom Jun 14, 2024
0942e58
refactor : jwtfilter 로그아웃 된 토큰인지 검증
GaBaljaintheroom Jun 14, 2024
708cb42
refactor : existAccessToken 메서드 타입 수정
GaBaljaintheroom Jun 14, 2024
ff5a191
refactor : PR 리뷰 후 수정
GaBaljaintheroom Jun 15, 2024
6365594
refactor : UserRoleApiType 매게변수 타입 수정
GaBaljaintheroom Jun 15, 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
12 changes: 4 additions & 8 deletions app/api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ allprojects {
//swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'

implementation 'org.springframework.data:spring-data-commons:3.3.0'
devmizz marked this conversation as resolved.
Show resolved Hide resolved


implementation "org.springframework.boot:spring-boot-starter-web"
implementation 'org.springframework.boot:spring-boot-starter-validation'
testImplementation "org.springframework.boot:spring-boot-starter-test"
Expand All @@ -14,12 +17,5 @@ allprojects {

dependencies {
implementation project(":app:api:user-api")

// spring-security
implementation 'org.springframework.boot:spring-boot-starter-security'

// jwt
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5'
implementation project(":app:api:common-api")
devmizz marked this conversation as resolved.
Show resolved Hide resolved
}
14 changes: 14 additions & 0 deletions app/api/common-api/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
bootJar.enabled = false
jar.enabled = true

dependencies {
implementation project(":app:domain:user-domain")
devmizz marked this conversation as resolved.
Show resolved Hide resolved

// spring-security
implementation 'org.springframework.boot:spring-boot-starter-security'

// jwt
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.example.config;

import org.example.property.TokenProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(TokenProperty.class)
@ComponentScan(basePackages = "org.example")
public class CommonApiConfig {

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected void doFilterInternal(
FilterChain filterChain
) throws ServletException, IOException {
if (request.getHeader("Refresh") != null) {
TokenParam token = refreshTokenProcessor.process(request, response);
TokenParam token = refreshTokenProcessor.reissueToken(request);
response.getWriter().write(new ObjectMapper().writeValueAsString(token));
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.example.repository;

import org.springframework.stereotype.Component;

@Component
public interface RedisRepository {

void save(String userId, String refreshToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Map;
import java.util.UUID;
import lombok.Builder;
import org.example.vo.UserRole;
import org.example.vo.UserRoleApiType;

@Builder
Expand All @@ -26,4 +27,8 @@ public static UserParam fromPayload(Object payload) {
.role(UserRoleApiType.valueOf(claim.get("role")))
.build();
}

public static UserParam as(UUID userId, UserRole userRole) {
devmizz marked this conversation as resolved.
Show resolved Hide resolved
return new UserParam(userId, UserRoleApiType.valueOf(userRole.name()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.example.security.token;

import jakarta.servlet.http.HttpServletRequest;
import java.util.Date;
import lombok.RequiredArgsConstructor;
import org.example.repository.RedisRepository;
import org.example.security.dto.TokenParam;
import org.example.security.dto.UserParam;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class RefreshTokenProcessor {

private final JWTHandler jwtHandler;
private final JWTGenerator jwtGenerator;
private final RedisRepository redisRepository;

public TokenParam reissueToken(HttpServletRequest request) {
String refreshToken = jwtHandler.extractRefreshToken(request);
UserParam userParam = jwtHandler.extractUserFrom(refreshToken);
TokenParam newTokenParam = jwtGenerator.generate(userParam, new Date());

redisRepository.save(userParam.userId().toString(), newTokenParam.refreshToken());
return newTokenParam;
}
}
7 changes: 1 addition & 6 deletions app/api/src/main/java/org/example/config/ApiConfig.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
package org.example.config;

import org.example.property.TokenProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(UserApiConfig.class)
@EnableConfigurationProperties(TokenProperty.class)
@ComponentScan(basePackages = "org.example")
@Import({UserApiConfig.class, CommonApiConfig.class})
public class ApiConfig {

}

This file was deleted.

2 changes: 2 additions & 0 deletions app/api/user-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ jar.enabled = true
dependencies {
implementation project(":app:domain")
implementation project(":app:domain:user-domain")

implementation project(":app:api:common-api")
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.example.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.example.dto.request.SignUpRequest;
import org.example.entity.User;
import org.example.security.dto.TokenParam;
import org.example.service.UserService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -20,13 +22,13 @@ public class UserController {
private final UserService userService;

@PostMapping("/sign-up")
@Operation(summary = "유저 회원가입", description = "사용자는 회원가입을 할 수 있다.", tags = {"user"})
public ResponseEntity<String> signUp(@Valid @RequestBody SignUpRequest request) {
@Tag(name = "user")
@Operation(summary = "유저 로그인", description = "사용자는 소셜 로그인을 할 수 있다.")
public ResponseEntity<TokenParam> signUp(@Valid @RequestBody SignUpRequest request) {
final User createdUser = request.toUser();
final User user = userService.signUp(createdUser);
final String nickName = userService.findNickname(user);
TokenParam tokenParam = userService.signUp(createdUser);

return ResponseEntity.ok(nickName + "사용자 생성 성공!");
return ResponseEntity.ok(tokenParam);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,25 @@

import jakarta.validation.constraints.NotNull;
import org.example.entity.User;
import org.example.vo.SocialLoginType;

public record SignUpRequest(
devmizz marked this conversation as resolved.
Show resolved Hide resolved

@NotNull(message = "닉네임은 필수 입력값입니다.")
String nickname
String nickname,

@NotNull(message = "소셜 타입은 필수 입력값입니다.")
SocialLoginType socialLoginType,

@NotNull(message = "소셜 식별자는 필수 입력값입니다.")
String socialIdentifier
) {

public User toUser() {
return new User(nickname);
return User.builder()
.nickname(nickname)
.socialLoginType(socialLoginType)
.socialIdentifier(socialIdentifier)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.example.repository;

import org.springframework.stereotype.Component;

@Component
public interface RedisRepository {

void save(String userId, String refreshToken);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package org.example.service;

import java.util.Date;
import lombok.RequiredArgsConstructor;
import org.example.entity.User;
import org.example.repository.RedisRepository;
import org.example.security.dto.TokenParam;
import org.example.security.dto.UserParam;
import org.example.security.token.JWTGenerator;
import org.example.usecase.UserUseCase;
import org.springframework.stereotype.Service;

Expand All @@ -10,9 +15,17 @@
public class UserService {

private final UserUseCase userUseCase;
private final JWTGenerator jwtGenerator;
private final RedisRepository redisRepository;

public User signUp(final User user) {
return userUseCase.save(user);
public TokenParam signUp(final User user) {
devmizz marked this conversation as resolved.
Show resolved Hide resolved
User createdUser = userUseCase.save(user);
UserParam userParam = UserParam.as(createdUser.getId(), createdUser.getUserRole());
TokenParam tokenParam = jwtGenerator.generate(userParam, new Date());

redisRepository.save(userParam.userId().toString(), tokenParam.refreshToken());
devmizz marked this conversation as resolved.
Show resolved Hide resolved

return tokenParam;
}

public String findNickname(final User user) {
Expand Down
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ allprojects {
dependencies {
implementation "org.springframework.boot:spring-boot-starter-web"
implementation project(":app:api")
implementation project(":app:domain")
implementation project(":app:infrastructure")

// docker-compose
developmentOnly 'org.springframework.boot:spring-boot-docker-compose'
Expand Down
10 changes: 10 additions & 0 deletions app/domain/src/main/java/org/example/config/DomainConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.example.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "org.example")
public class DomainConfig {

}
10 changes: 8 additions & 2 deletions app/domain/src/main/java/org/example/entity/BaseEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.domain.Persistable;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@Getter
@EntityListeners(value = {AuditingEntityListener.class})
@MappedSuperclass
public abstract class BaseEntity {
public abstract class BaseEntity implements Persistable<UUID> {

@Id
@Column(name = "id")
@Column(name = "id", updatable = false, nullable = false)
private UUID id = UlidCreator.getMonotonicUlid().toUuid();

@CreatedDate
Expand All @@ -28,4 +29,9 @@ public abstract class BaseEntity {
@LastModifiedDate
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;

@Override
public boolean isNew() {
devmizz marked this conversation as resolved.
Show resolved Hide resolved
return id != null;
}
}
3 changes: 3 additions & 0 deletions app/domain/user-domain/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ bootJar.enabled = false
jar.enabled = true

dependencies {
// hypersistence utils
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.6'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.4'
implementation project(":app:domain")
}
devmizz marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package org.example.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Table;
import java.time.LocalDate;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.example.entity.credential.SocialCredentials;
import org.example.vo.SocialLoginType;
import org.example.vo.UserGender;
import org.example.vo.UserRole;

@Entity
@Getter
Expand All @@ -16,7 +25,27 @@ public class User extends BaseEntity {
@Column(name = "nickname", nullable = false)
private String nickname;

public User(String nickname) {
@Column(name = "birth")
private LocalDate birth;
devmizz marked this conversation as resolved.
Show resolved Hide resolved

@Column(name = "fcm_token")
private String fcmToken;

@Embedded
private SocialCredentials socialCredentials = new SocialCredentials();
devmizz marked this conversation as resolved.
Show resolved Hide resolved

@Column(name = "gender")
@Enumerated(value = EnumType.STRING)
private UserGender userGender;

@Column(name = "role", nullable = false)
@Enumerated(value = EnumType.STRING)
private UserRole userRole;

@Builder
private User(String nickname, SocialLoginType socialLoginType, String socialIdentifier) {
this.nickname = nickname;
this.socialCredentials.put(socialLoginType, socialIdentifier);
this.userRole = UserRole.USER;
}
}
Loading