-
Notifications
You must be signed in to change notification settings - Fork 0
과제 1-2 pgsshiho 과제제출 #1
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: master
Are you sure you want to change the base?
Changes from all commits
d1a4587
00fc3ad
a892f91
7593d43
074cb14
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,53 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Controller; | ||
|
||
import com.gsm._8th.class4.backend.task12.domain.auth.Service.DeleteUserService; | ||
import com.gsm._8th.class4.backend.task12.domain.auth.Service.signinService; | ||
import com.gsm._8th.class4.backend.task12.domain.auth.Service.SignUpService; | ||
import com.gsm._8th.class4.backend.task12.domain.auth.Service.RefreshTokenService; | ||
import com.gsm._8th.class4.backend.task12.domain.auth.dto.UserLoginRequest; | ||
import com.gsm._8th.class4.backend.task12.domain.auth.dto.UserSignupRequest; | ||
import com.gsm._8th.class4.backend.task12.global.security.TokenResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
import java.net.URI; | ||
import java.net.URISyntaxException; | ||
|
||
@RestController | ||
@RequestMapping("/api/v1/auth") // ✅ API 명세서에 맞춰 변경 | ||
@RequiredArgsConstructor | ||
public class newsignUpController { // ✅ 클래스명 변경 | ||
|
||
private final SignUpService authService; | ||
private final DeleteUserService deleteUserService; | ||
private final signinService signinService; | ||
private final RefreshTokenService refreshTokenService; | ||
|
||
// 회원가입 | ||
@PostMapping("/signup") | ||
public ResponseEntity<String> signup(@RequestBody UserSignupRequest request) throws URISyntaxException { | ||
authService.signup(request); | ||
URI location = new URI("http://localhost:8081/api/v1/auth/signin"); | ||
return ResponseEntity.created(location).build(); | ||
} | ||
|
||
// 회원 삭제 | ||
@DeleteMapping("/delete") | ||
public ResponseEntity<String> deleteUser(@RequestBody UserLoginRequest request) { | ||
deleteUserService.deleteUser(request.getUsername()); | ||
return ResponseEntity.ok("계정 삭제 완료"); | ||
} | ||
|
||
// 로그인 | ||
@PostMapping("/signin") // ✅ API 경로 수정 | ||
public ResponseEntity<TokenResponse> login(@RequestBody UserLoginRequest request) { | ||
return ResponseEntity.ok(signinService.login(request.getUsername(), request.getPassword())); | ||
} | ||
|
||
// 토큰 갱신 | ||
@PostMapping("/refresh") // ✅ API 경로 유지 | ||
public ResponseEntity<TokenResponse> refreshToken(@RequestHeader("Refresh-Token") String refreshToken) { | ||
return ResponseEntity.ok(refreshTokenService.refreshToken(refreshToken)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Entity; | ||
|
||
import jakarta.persistence.*; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
@Entity | ||
@Getter | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
@Builder | ||
@Table(name = "new_sign") | ||
public class NewSign { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
private String username; | ||
private String password; | ||
private String role; | ||
private String email; | ||
} | ||
Comment on lines
+1
to
+28
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. Entity 이름이 만약 회원가입이 승인제로 이뤄지는 서비스여서 새 계정 정보를 승인 전까지 임시로 저장하는 테이블이라면 나름 적합할 수도 있겠지만... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Repository; | ||
|
||
import com.gsm._8th.class4.backend.task12.domain.auth.Entity.NewSign; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
import java.util.Optional; | ||
|
||
public interface UserRepository extends JpaRepository<NewSign, Long> { | ||
|
||
Optional<NewSign> findByUsername(String username); | ||
|
||
boolean existsByUsername(String username); | ||
|
||
void deleteByUsername(String username); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Service; | ||
|
||
public interface DeleteUserService { | ||
void deleteUser(String username); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Service; | ||
|
||
|
||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.ControllerAdvice; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
|
||
@ControllerAdvice | ||
public class GlobalExceptionHandler { | ||
|
||
@ExceptionHandler(IllegalArgumentException.class) | ||
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) { | ||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); | ||
} | ||
} | ||
Comment on lines
+1
to
+16
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.
프로젝트 전역적으로 적용되는 클래스이니 만큼 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Service; | ||
|
||
import com.gsm._8th.class4.backend.task12.global.security.TokenResponse; | ||
|
||
public interface RefreshTokenService { | ||
TokenResponse refreshToken(String refreshToken); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Service; | ||
|
||
import com.gsm._8th.class4.backend.task12.global.security.JwtTokenService; | ||
import com.gsm._8th.class4.backend.task12.global.security.TokenResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.Optional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class RefreshTokenServiceImpl implements RefreshTokenService { | ||
private final JwtTokenService jwtTokenService; | ||
|
||
@Override | ||
public TokenResponse refreshToken(String refreshToken) { | ||
String username = Optional.ofNullable(jwtTokenService.getUsernameFromToken(refreshToken)) | ||
.filter(u -> jwtTokenService.validateRefreshToken(u, refreshToken)) | ||
.orElseThrow(() -> new IllegalArgumentException("유효하지 않은 리프레시 토큰입니다.")); | ||
|
||
|
||
jwtTokenService.revokeRefreshToken(username); | ||
return new TokenResponse( | ||
jwtTokenService.createAccessToken(username), | ||
jwtTokenService.createRefreshToken(username) | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Service; | ||
|
||
import com.gsm._8th.class4.backend.task12.domain.auth.dto.UserSignupRequest; | ||
|
||
public interface SignUpService { | ||
void signup(UserSignupRequest request); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Service; | ||
|
||
import com.gsm._8th.class4.backend.task12.domain.auth.Entity.NewSign; | ||
import com.gsm._8th.class4.backend.task12.global.security.JwtTokenService; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import com.gsm._8th.class4.backend.task12.domain.auth.Repository.UserRepository; | ||
import org.springframework.web.bind.annotation.ControllerAdvice; | ||
|
||
import java.util.Optional; | ||
|
||
@Service | ||
@Transactional | ||
@RequiredArgsConstructor | ||
public class deleteUserImpl implements DeleteUserService { | ||
private final UserRepository userRepository; | ||
|
||
@Override | ||
@Transactional | ||
|
||
public void deleteUser(String username) { | ||
userRepository.findByUsername(username) | ||
.ifPresentOrElse( | ||
userRepository::delete, | ||
() -> { throw new IllegalArgumentException("존재하지 않는 사용자입니다."); } | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Service; | ||
|
||
import com.gsm._8th.class4.backend.task12.domain.auth.dto.UserSignupRequest; | ||
import com.gsm._8th.class4.backend.task12.domain.auth.Entity.NewSign; | ||
import com.gsm._8th.class4.backend.task12.domain.auth.Repository.UserRepository; | ||
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 signUpServiceImpl implements SignUpService { | ||
private final UserRepository userRepository; | ||
private final PasswordEncoder passwordEncoder; | ||
|
||
@Override | ||
@Transactional | ||
public void signup(UserSignupRequest request) { | ||
if (userRepository.existsByUsername(request.getUsername())) { | ||
throw new IllegalArgumentException("이미 존재하는 사용자입니다."); | ||
} | ||
|
||
NewSign newUser = NewSign.builder() | ||
.username(request.getUsername()) | ||
.password(passwordEncoder.encode(request.getPassword())) | ||
.email(request.getEmail()) | ||
.role("ROLE_USER") | ||
.build(); | ||
|
||
userRepository.save(newUser); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Service; | ||
|
||
import com.gsm._8th.class4.backend.task12.global.security.TokenResponse; | ||
|
||
public interface signinService { | ||
TokenResponse login(String username, String rawPassword); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.Service; | ||
|
||
import com.gsm._8th.class4.backend.task12.domain.auth.Entity.NewSign; | ||
import com.gsm._8th.class4.backend.task12.domain.auth.Repository.UserRepository; | ||
import com.gsm._8th.class4.backend.task12.global.security.JwtTokenService; | ||
import com.gsm._8th.class4.backend.task12.global.security.TokenResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class signinServiceImpl implements signinService { | ||
private final UserRepository userRepository; | ||
private final JwtTokenService jwtTokenService; | ||
private final PasswordEncoder passwordEncoder; | ||
|
||
@Override | ||
public TokenResponse login(String username, String rawPassword) { | ||
NewSign user = userRepository.findByUsername(username) | ||
.filter(u -> passwordEncoder.matches(rawPassword, u.getPassword())) | ||
.orElseThrow(() -> new IllegalArgumentException("아이디 또는 비밀번호가 틀렸습니다.")); | ||
return new TokenResponse( | ||
jwtTokenService.createAccessToken(username), | ||
jwtTokenService.createRefreshToken(username) | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.dto; | ||
|
||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
@Setter | ||
|
||
public class UserLoginRequest { | ||
private String username; | ||
private String password; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.gsm._8th.class4.backend.task12.domain.auth.dto; | ||
|
||
import jakarta.validation.constraints.Email; | ||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.Size; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
@Setter | ||
public class UserSignupRequest { | ||
|
||
@NotBlank(message = "사용자 이름은 필수 입력값입니다.") | ||
@Size(min = 3, max = 20, message = "사용자 이름은 3~20자로 입력해야 합니다.") | ||
private String username; | ||
|
||
@NotBlank(message = "비밀번호는 필수 입력값입니다.") | ||
@Size(min = 6, message = "비밀번호는 최소 6자 이상이어야 합니다.") | ||
private String password; | ||
|
||
@NotBlank(message = "이메일은 필수 입력값입니다.") | ||
@Email(message = "올바른 이메일 형식을 입력해주세요.") | ||
private String email; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.gsm._8th.class4.backend.task12.global.security; | ||
|
||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.core.userdetails.UserDetailsService; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
import java.io.IOException; | ||
|
||
@RequiredArgsConstructor | ||
public class JwtAuthenticationFilter extends OncePerRequestFilter { | ||
|
||
private final JwtTokenService jwtTokenService; | ||
private final UserDetailsService userDetailsService; // 추가 | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | ||
throws ServletException, IOException { | ||
|
||
String authHeader = request.getHeader("Authorization"); | ||
|
||
// null 체크 + Bearer 형식인지 확인 | ||
if (authHeader == null || !authHeader.startsWith("Bearer ")) { | ||
filterChain.doFilter(request, response); | ||
return; | ||
} | ||
|
||
String token = authHeader.substring(7); | ||
|
||
if (jwtTokenService.validateToken(token)) { | ||
String username = jwtTokenService.getUsernameFromToken(token); | ||
|
||
// UserDetailsService를 활용하여 사용자 정보 로드 | ||
UserDetails userDetails = userDetailsService.loadUserByUsername(username); | ||
|
||
// 인증 객체 생성 | ||
UsernamePasswordAuthenticationToken authentication = | ||
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); | ||
|
||
// Spring Security 컨텍스트에 저장 | ||
SecurityContextHolder.getContext().setAuthentication(authentication); | ||
} | ||
|
||
filterChain.doFilter(request, response); | ||
} | ||
} |
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.
혹시 이 코드는 왜 있는건가요?
API 주도형 개발에서는 이런 URI를 반환할 이유가 거의 없지 않나요?서버에 직접 정적파일을 보관하는 것이 아니라면 불필요해 보입니다
혹시 필요하다면 이유를 알려주세요
진짜로 모름