-
Notifications
You must be signed in to change notification settings - Fork 0
[FEAT] 9주차 미션_맹덕 #43
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
base: mengduck
Are you sure you want to change the base?
[FEAT] 9주차 미션_맹덕 #43
Changes from all commits
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,12 @@ | ||
| package org.example.umc10thyongjae.domain.auth.dto.request; | ||
|
|
||
| import jakarta.validation.constraints.NotBlank; | ||
|
|
||
| public record LoginRequestDto( | ||
| @NotBlank(message = "아이디 입력은 필수입니다.") | ||
| String id, | ||
|
|
||
| @NotBlank(message = "비밀번호 입력은 필수입니다.") | ||
| String pwd | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package org.example.umc10thyongjae.domain.auth.dto.response; | ||
|
|
||
| public record LoginResponseDto( | ||
|
Contributor
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. 맹덕이는 로그인 결과 반환으로 accessToken만 반환하도록 설계 했구나. 나는 acccessToken에다가 userId까지 반환하도록 설계를 했어. 프론트에서 로그인한뒤 바로 마이페이지로 보내는 것 같은 상황을 생각해보면 유저식별자가 필요할거라고 생각했기 때문이야. 스터디때 서로 의견 한번 얘기해보면 좋겠다 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. userId 도 필요하면 내려주고~ 무엇보다 만료시간은 웬만하면 같이 내려주는게 좋아 맹덕! |
||
| String accessToken | ||
| ) { | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,9 @@ | ||
| package org.example.umc10thyongjae.domain.auth.service; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import org.example.umc10thyongjae.domain.auth.dto.request.LoginRequestDto; | ||
| import org.example.umc10thyongjae.domain.auth.dto.request.SignUpRequestDto; | ||
| import org.example.umc10thyongjae.domain.auth.dto.response.LoginResponseDto; | ||
| import org.example.umc10thyongjae.domain.auth.dto.response.UserInfoResponseDto; | ||
| import org.example.umc10thyongjae.domain.auth.entity.FoodPreference; | ||
| import org.example.umc10thyongjae.domain.auth.entity.Term; | ||
|
|
@@ -15,7 +17,10 @@ | |
| import org.example.umc10thyongjae.domain.auth.repository.UserRepository; | ||
| import org.example.umc10thyongjae.domain.auth.repository.UserTermRepository; | ||
| import org.example.umc10thyongjae.global.apiPayload.exception.AlreadyRegisterUserException; | ||
| import org.example.umc10thyongjae.global.apiPayload.exception.LoginUnavailableException; | ||
| import org.example.umc10thyongjae.global.apiPayload.exception.NotDataFoundException; | ||
| import org.example.umc10thyongjae.global.security.entity.AuthUser; | ||
| import org.example.umc10thyongjae.global.security.util.JwtUtil; | ||
| import org.springframework.security.crypto.password.PasswordEncoder; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
@@ -35,6 +40,7 @@ public class AuthService { | |
| private final FoodPreferenceRepository foodPreferenceRepository; | ||
| private final UserFoodPreferenceRepository userFoodPreferenceRepository; | ||
| private final PasswordEncoder passwordEncoder; | ||
| private final JwtUtil jwtUtil; | ||
|
|
||
| @Transactional | ||
| public Long signUp(SignUpRequestDto dto) { | ||
|
|
@@ -63,6 +69,20 @@ public Long signUp(SignUpRequestDto dto) { | |
| return user.getId(); | ||
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
|
Contributor
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. 로그인 서비스에서는 DB작업을 조회만 하니까 readOnly=true로 선언해주는거 되게 좋은것 같다. 나도 반영해봐야지 |
||
| public LoginResponseDto login(LoginRequestDto dto) { | ||
| User user = authRepository.findByLoginId(dto.id()) | ||
| .orElseThrow(LoginUnavailableException::new); | ||
|
|
||
| if (!passwordEncoder.matches(dto.pwd(), user.getPwd())) { | ||
| throw new LoginUnavailableException(); | ||
| } | ||
|
|
||
| String accessToken = jwtUtil.createAccessToken(new AuthUser(user)); | ||
|
|
||
| return new LoginResponseDto(accessToken); | ||
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
| public UserInfoResponseDto getUserInfo(long userId) { | ||
| User entity = authRepository.findById(userId).orElseThrow( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |
| public enum GeneralErrorCode implements BaseErrorCode { | ||
| BAD_REQUEST(HttpStatus.BAD_REQUEST, "BAD_REQUEST", "잘못된 요청입니다. "), | ||
| UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "UNAUTHORIZED", "인증되지 않았습니다."), | ||
| LOGIN_UNAVAILABLE(HttpStatus.UNAUTHORIZED, "LOGIN_UNAVAILABLE", "아이디 또는 비밀번호가 일치하지 않습니다."), | ||
|
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. 👍 |
||
| FORBIDDEN(HttpStatus.FORBIDDEN, "FORBIDDEN", "접근이 금지되었습니다."), | ||
| NOT_FOUND(HttpStatus.NOT_FOUND, "NOT_FOUND", "해당 리소스를 찾을 수 없습니다."), | ||
| ALREADY_REGISTER_USER(HttpStatus.CONFLICT, "ALREADY_REGISTER_USER", "이미 가입된 사용자입니다."), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package org.example.umc10thyongjae.global.apiPayload.exception; | ||
|
|
||
| import org.example.umc10thyongjae.global.apiPayload.code.GeneralErrorCode; | ||
|
|
||
| public class LoginUnavailableException extends CommonException { | ||
|
|
||
| public LoginUnavailableException() { | ||
| super(GeneralErrorCode.LOGIN_UNAVAILABLE); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package org.example.umc10thyongjae.global.security.entity; | ||
|
|
||
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.example.umc10thyongjae.domain.auth.entity.User; | ||
| import org.jspecify.annotations.Nullable; | ||
| import org.springframework.security.core.GrantedAuthority; | ||
| import org.springframework.security.core.userdetails.UserDetails; | ||
|
|
||
| import java.util.Collection; | ||
| import java.util.List; | ||
|
|
||
| @Getter | ||
| @RequiredArgsConstructor | ||
| public class AuthUser implements UserDetails { | ||
| private final User user; | ||
|
|
||
| @Override | ||
| public Collection<? extends GrantedAuthority> getAuthorities() { | ||
| return List.of(); | ||
| } | ||
|
|
||
| @Override | ||
| public @Nullable String getPassword() { | ||
| return user.getPwd(); | ||
| } | ||
|
|
||
| @Override | ||
| public String getUsername() { | ||
| return user.getLoginId(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| package org.example.umc10thyongjae.global.security.filter; | ||
|
|
||
| import jakarta.servlet.FilterChain; | ||
| import jakarta.servlet.ServletException; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import lombok.NonNull; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.example.umc10thyongjae.domain.auth.service.CustomUserDetailsService; | ||
| import org.example.umc10thyongjae.global.apiPayload.ApiResponse; | ||
| import org.example.umc10thyongjae.global.apiPayload.code.BaseErrorCode; | ||
| import org.example.umc10thyongjae.global.apiPayload.code.GeneralErrorCode; | ||
| import org.example.umc10thyongjae.global.security.util.JwtUtil; | ||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.security.core.userdetails.UserDetails; | ||
| import org.springframework.web.filter.OncePerRequestFilter; | ||
| import tools.jackson.databind.ObjectMapper; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| @RequiredArgsConstructor | ||
| public class JwtAuthFilter extends OncePerRequestFilter { | ||
|
|
||
| private final JwtUtil jwtUtil; | ||
| private final CustomUserDetailsService customUserDetailsService; | ||
|
|
||
| @Override | ||
| protected void doFilterInternal( | ||
| @NonNull HttpServletRequest request, | ||
| @NonNull HttpServletResponse response, | ||
| @NonNull FilterChain filterChain | ||
| ) throws ServletException, IOException { | ||
|
|
||
| try { | ||
| // 토큰 가져오기 | ||
| String token = request.getHeader("Authorization"); | ||
| // token이 없거나 Bearer가 아니면 넘기기 | ||
| if (token == null || !token.startsWith("Bearer ")) { | ||
| filterChain.doFilter(request, response); | ||
| return; | ||
| } | ||
| // Bearer이면 추출 | ||
| token = token.replace("Bearer ", ""); | ||
|
Contributor
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. 여기서 toen.replace를 사용하면 문자열 안에 있는 Bearer이 모두 삭제되기 때문에 혹시 모를 상황을 대비해 token.substring(7,token.length())를 이용해서 앞의 7글자만 제거하는 방식도 있더라
Contributor
Author
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. 이거 되게 신박한 생각이네 ... 반영해볼게 땡큐!! |
||
| // AccessToken 검증하기: 올바른 토큰이면 | ||
| if (jwtUtil.isValid(token)) { | ||
| // 토큰에서 이메일 추출 | ||
| String email = jwtUtil.getEmail(token); | ||
| // 인증 객체 생성: 이메일로 찾아온 뒤, 인증 객체 생성 | ||
| UserDetails user = customUserDetailsService.loadUserByUsername(email); | ||
| Authentication auth = new UsernamePasswordAuthenticationToken( | ||
| user, | ||
| null, | ||
| user.getAuthorities() | ||
| ); | ||
| // 인증 완료 후 SecurityContextHolder에 넣기 | ||
| SecurityContextHolder.getContext().setAuthentication(auth); | ||
| } | ||
| filterChain.doFilter(request, response); | ||
| } catch (Exception e) { | ||
| ObjectMapper mapper = new ObjectMapper(); | ||
| BaseErrorCode code = GeneralErrorCode.UNAUTHORIZED; | ||
|
|
||
| response.setContentType("application/json;charset=UTF-8"); | ||
| response.setStatus(code.getStatus().value()); | ||
|
|
||
| ApiResponse<Void> errorResponse = ApiResponse.onFailure(code,null); | ||
|
|
||
| mapper.writeValue(response.getOutputStream(), errorResponse); | ||
| } | ||
| } | ||
| } | ||
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.
회원가입 응답을 Void로 둘지, userId 같은 식별자를 내려줄지는 다음 클라이언트 플로우 기준도 생각해보면 좋을듯!
예를 들어 가입 직후 로그인 화면으로 보내는 구조라면 Void도 충분하고,
가입 직후 온보딩/프로필 설정으로 이어지는 구조라면 userId나 accessToken 같은 응답이 필요할 수도 있겠지?
서비스에서 userId를 반환하는데 컨트롤러에서 반환하지 않도록 바꿔줘서 한번 얘기해본다!