-
Notifications
You must be signed in to change notification settings - Fork 14
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] 인지원 로그인 #8
base: main
Are you sure you want to change the base?
[BE] 인지원 로그인 #8
Changes from all commits
53b14b6
801a3e1
208e1cf
132efee
c04015d
c7e0753
e83293f
3122026
c61bf8c
086041f
6f59898
11d4f7c
e2bd140
167aab8
ec919f3
fb5a320
7cddbee
031d646
9156a37
417fbea
719894c
d46535d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package leets.attendance.domain.common; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.EntityListeners; | ||
import jakarta.persistence.MappedSuperclass; | ||
import lombok.Getter; | ||
import org.springframework.data.annotation.CreatedDate; | ||
import org.springframework.data.annotation.LastModifiedDate; | ||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
@Getter | ||
@MappedSuperclass | ||
@EntityListeners(AuditingEntityListener.class) | ||
public abstract class BaseTimeEntity { | ||
@CreatedDate | ||
@Column(updatable = false) | ||
private LocalDateTime createdDate; | ||
|
||
@LastModifiedDate | ||
private LocalDateTime modifiedDate; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package leets.attendance.domain.common.dto; | ||
|
||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
|
||
import java.io.Serializable; | ||
|
||
public record ResponseDto<T>(T date) implements Serializable { | ||
public static <T> ResponseEntity<T> ok(T data){ | ||
return ResponseEntity.ok(data); | ||
} | ||
|
||
public static <T> ResponseEntity<T> created(T data){ | ||
return ResponseEntity | ||
.status(HttpStatus.CREATED) | ||
.body(data); | ||
} | ||
|
||
public static <T> ResponseEntity<T> noContent(){ | ||
return ResponseEntity | ||
.status(HttpStatus.NO_CONTENT) | ||
.build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package leets.attendance.domain.user.application; | ||
|
||
import leets.attendance.domain.user.domain.User; | ||
import leets.attendance.domain.user.exception.InvalidPasswordException; | ||
import leets.attendance.domain.user.exception.UserNotFoundException; | ||
import leets.attendance.domain.user.presentation.dto.Request.LoginRequestDto; | ||
import leets.attendance.domain.user.presentation.dto.Response.DuplicationResponseDto; | ||
import leets.attendance.domain.user.presentation.dto.Response.TokenResponseDto; | ||
import leets.attendance.domain.user.repository.UserRepository; | ||
import leets.attendance.domain.user.presentation.dto.Request.RegisterRequestDto; | ||
import leets.attendance.global.jwt.TokenProvider; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class UserService { | ||
|
||
private final UserRepository userRepository; | ||
private final PasswordEncoder passwordEncoder; | ||
private final TokenProvider tokenProvider; | ||
|
||
@Transactional | ||
public User register(RegisterRequestDto registerRequestDto){ | ||
User user = User.builder() | ||
.userId(registerRequestDto.id()) | ||
.userPwd(passwordEncoder.encode(registerRequestDto.password())) | ||
.name(registerRequestDto.name()) | ||
.part(registerRequestDto.part()).build(); | ||
return userRepository.save(user); | ||
|
||
} | ||
|
||
public DuplicationResponseDto checkDuplicateId(String joinId){ | ||
boolean isDuplicate = userRepository.existsByUserId(joinId); | ||
return new DuplicationResponseDto(isDuplicate); //true, false로 중복확인 | ||
} | ||
|
||
public TokenResponseDto login(LoginRequestDto loginRequestDto){ | ||
User user = userRepository.findByUserId(loginRequestDto.id()).orElseThrow(UserNotFoundException::new); | ||
String password = loginRequestDto.password(); | ||
|
||
//복호화 후 체크해야하는데 시간이 없어서.. ^^.. | ||
if(!user.getUserPwd().equals(password)){ | ||
throw new InvalidPasswordException(); | ||
} | ||
|
||
String accessToken = tokenProvider.generateAccessToken(user); | ||
String refreshToken = tokenProvider.generateRefreshToken(user); | ||
return new TokenResponseDto(accessToken, refreshToken); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package leets.attendance.domain.user.domain; | ||
|
||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Getter | ||
@RequiredArgsConstructor | ||
public enum Part { | ||
FE("FE"), | ||
BE("BE"), | ||
DESIGN("DE"); | ||
private final String gender; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package leets.attendance.domain.user.domain; | ||
|
||
import jakarta.persistence.*; | ||
import leets.attendance.domain.common.BaseTimeEntity; | ||
import lombok.*; | ||
|
||
import static jakarta.persistence.GenerationType.IDENTITY; | ||
|
||
@Getter | ||
@Entity | ||
@Table(name = "users") | ||
@Builder | ||
@AllArgsConstructor | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class User extends BaseTimeEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = IDENTITY) | ||
@Column(name = "user_id") | ||
private Long id; | ||
|
||
@Column(nullable = false) | ||
private String userId; | ||
|
||
@Column(nullable = false) | ||
private String userPwd; | ||
|
||
@Column(nullable = false, length = 20) | ||
private String name; | ||
|
||
@Enumerated(EnumType.STRING) | ||
@Column(nullable = false) | ||
private Part part; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package leets.attendance.domain.user.exception; | ||
|
||
import leets.attendance.global.error.dto.ErrorCode; | ||
import leets.attendance.global.error.exception.ServiceException; | ||
|
||
public class InvalidPasswordException extends ServiceException { | ||
public InvalidPasswordException(){ | ||
super(ErrorCode.INVALID_PASSWORD_EXCEPTION); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package leets.attendance.domain.user.exception; | ||
|
||
import leets.attendance.global.error.dto.ErrorCode; | ||
import leets.attendance.global.error.exception.ServiceException; | ||
|
||
public class UserNotFoundException extends ServiceException { | ||
public UserNotFoundException(){ | ||
super(ErrorCode.USER_NOT_FOUND_EXCEPTION); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package leets.attendance.domain.user.presentation; | ||
|
||
import leets.attendance.domain.common.dto.ResponseDto; | ||
import leets.attendance.domain.user.application.UserService; | ||
import leets.attendance.domain.user.domain.User; | ||
import leets.attendance.domain.user.presentation.dto.Request.LoginRequestDto; | ||
import leets.attendance.domain.user.presentation.dto.Request.RegisterRequestDto; | ||
import leets.attendance.domain.user.presentation.dto.Response.DuplicationResponseDto; | ||
import leets.attendance.domain.user.presentation.dto.Response.TokenResponseDto; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
import java.net.URI; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/users") | ||
public class UserController { | ||
private final UserService userService; | ||
@PostMapping("/register") | ||
public ResponseEntity<?> register(@RequestBody RegisterRequestDto registerRequestDto){ | ||
User register = userService.register(registerRequestDto); | ||
return ResponseDto.created(URI.create("/users/" + register.getId())); | ||
} | ||
|
||
@GetMapping("/duplication/{joinId}") | ||
public ResponseEntity<DuplicationResponseDto> checkDuplicateId(@PathVariable String joinId){ | ||
return ResponseDto.ok(userService.checkDuplicateId(joinId)); | ||
} | ||
|
||
@PostMapping("/login") | ||
public ResponseEntity<TokenResponseDto> login(@RequestBody LoginRequestDto loginRequestDto) { | ||
return ResponseDto.ok(userService.login(loginRequestDto)); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package leets.attendance.domain.user.presentation.dto.Request; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
|
||
public record LoginRequestDto( | ||
@NotNull | ||
String id, | ||
@NotNull | ||
String password | ||
) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package leets.attendance.domain.user.presentation.dto.Request; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
import leets.attendance.domain.user.domain.Part; | ||
|
||
public record RegisterRequestDto( | ||
@NotNull | ||
String id, | ||
|
||
@NotNull | ||
String name, | ||
|
||
@NotNull | ||
String password, | ||
|
||
@NotNull | ||
Part part | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Part를 dto에서 enum type으로 받게 되면 "fe" , "be" 처럼 소문자 입력에서 에러가 발생할 수도 있을 것 같습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사실 그 부분은 기획적인 부분도 포함될 거같은데요! 해당 선택 부분이 드롭다운 바라고 생각했어서 자체를 enum으로 가져오도록 구현했었네요 말씀하신 방법도 좋은 것 같습니다:) |
||
) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package leets.attendance.domain.user.presentation.dto.Response; | ||
|
||
public record DuplicationResponseDto( | ||
boolean isDuplicated | ||
) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package leets.attendance.domain.user.presentation.dto.Response; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
|
||
public record TokenResponseDto( | ||
@NotNull | ||
String accessToken, | ||
|
||
@NotNull | ||
String refreshToken | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 서비스는 로그인 세션을 길게 유지할(길어도 일주일에 한번) 필요가 없는 서비스라는 생각이 들었습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네! 습관적으로 refreshtoken을 생각했네요.. ^^..ㅜ 조언 감사합니다:) |
||
) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package leets.attendance.domain.user.repository; | ||
|
||
import leets.attendance.domain.user.domain.User; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
import javax.swing.text.html.Option; | ||
import java.util.Optional; | ||
|
||
public interface UserRepository extends JpaRepository<User, Long> { | ||
boolean existsByUserId(String userId); | ||
|
||
Optional<User> findByUserId(String userId); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package leets.attendance.global.config; | ||
|
||
import leets.attendance.global.jwt.JwtFilter; | ||
import leets.attendance.global.jwt.TokenProvider; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
|
||
@Configuration | ||
@RequiredArgsConstructor | ||
public class SecurityConfig { | ||
private final TokenProvider tokenProvider; | ||
|
||
@Bean | ||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
http | ||
.csrf(csrf -> | ||
csrf.disable()); | ||
return http.build(); | ||
} | ||
|
||
@Bean | ||
public JwtFilter jwtFilter() { | ||
return new JwtFilter(tokenProvider); | ||
} | ||
|
||
@Bean | ||
public PasswordEncoder passwordEncoder(){ | ||
return new BCryptPasswordEncoder(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package leets.attendance.global.error.dto; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import org.springframework.http.HttpStatus; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
public enum ErrorCode { | ||
BAD_REQUEST(HttpStatus.BAD_REQUEST, "BAD_REQUEST", "잘못된 요청입니다."), | ||
NOT_FOUND(HttpStatus.NOT_FOUND, "NOT_FOUND", "리소스를 찾을 수 없습니다."), | ||
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "METHOD_NOT_ALLOWED", "허용되지 않은 HTTP 메서드 입니다."), | ||
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "INTERNAL_SERVER_ERROR", "내부에서 서버 오류가 발생했습니다."), | ||
INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "UNAUTHORIZED","유효하지 않은 토큰입니다."), | ||
EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "UNAUTHORIZED", "만료된 토큰입니다."), | ||
NOT_FOUND_TOKEN_ROLL(HttpStatus.FORBIDDEN, "FORBIDDEN", "토큰의 ROLE을 확인할 수 없습니다."), | ||
USER_NOT_FOUND_EXCEPTION(HttpStatus.NOT_FOUND,"USER_NOT_FOUND_EXCEPTION","사용자를 찾을 수 없습니다."), | ||
INVALID_PASSWORD_EXCEPTION(HttpStatus.UNAUTHORIZED,"INVALID_PASSWORD_EXCEPTION","비밀번호가 일치하지 않습니다."); | ||
|
||
private final HttpStatus httpStatus; | ||
private final String code; | ||
private final String message; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package leets.attendance.global.error.dto; | ||
|
||
import lombok.Getter; | ||
import org.springframework.http.HttpStatus; | ||
|
||
@Getter | ||
public class ErrorResponse { | ||
private final HttpStatus status; | ||
private final String error; | ||
private final String code; | ||
private final String message; | ||
|
||
public ErrorResponse(ErrorCode errorCode) { | ||
this.status = errorCode.getHttpStatus(); | ||
this.error = errorCode.getHttpStatus().name(); | ||
this.code = errorCode.getCode(); | ||
this.message = errorCode.getMessage(); | ||
} | ||
public static ErrorResponse of(ErrorCode errorCode) { | ||
return new ErrorResponse(errorCode); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정적 팩토리 메서드 패턴으로 수정하시는 방향도 고려해보시면 좋을것 같습니다!
다른 분 프로젝트에서 참고할 만한 코드가 있어 첨부합니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋은 것 같습니다! 리팩토링 해볼게요!