Skip to content

Commit

Permalink
Merge pull request #8 from Leets-Official/feature-#7
Browse files Browse the repository at this point in the history
#7 Feat: 회원 탈퇴 기능
  • Loading branch information
KoungQ authored Jul 7, 2024
2 parents 97dd986 + 6e9300d commit 7f05676
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
import leets.weeth.domain.user.service.UserService;
import leets.weeth.global.common.response.CommonResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.User;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
Expand All @@ -18,8 +17,14 @@ public class UserController {
private final UserService userService;

@PostMapping("/sign-up")
public CommonResponse<String> signUp(@RequestBody @Valid UserDTO.SignUp requestDto) throws Exception {
public CommonResponse<String> signUp(@RequestBody @Valid UserDTO.SignUp requestDto) {
userService.signUp(requestDto);
return CommonResponse.createSuccess();
}

@DeleteMapping("")
public CommonResponse<String> delete(@AuthenticationPrincipal User user) {
userService.delete(user.getUsername());
return CommonResponse.createSuccess();
}
}
2 changes: 1 addition & 1 deletion src/main/java/leets/weeth/domain/user/dto/UserDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class UserDTO {
@NoArgsConstructor
@AllArgsConstructor
public static class SignUp {
private String username;
private String email;
private String password;
}
}
10 changes: 9 additions & 1 deletion src/main/java/leets/weeth/domain/user/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.persistence.*;
import leets.weeth.domain.user.entity.enums.Role;
import leets.weeth.domain.user.entity.enums.Status;
import leets.weeth.global.common.entity.BaseEntity;
import lombok.*;

Expand All @@ -18,7 +19,7 @@ public class User extends BaseEntity {
@Column(name = "user_id")
private Long id;

private String username;
private String email;

private String password;

Expand All @@ -27,7 +28,14 @@ public class User extends BaseEntity {

private String refreshToken;

@Enumerated(EnumType.STRING)
private Status status;

public void updateRefreshToken(String updatedToken) {
this.refreshToken = updatedToken;
}

public void leave() {
this.status = Status.LEFT;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package leets.weeth.domain.user.entity.enums;

public enum Status {

ACTIVE,
BANNED,
LEFT
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

public interface UserRepository extends JpaRepository<User, Long> {

Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);

Optional<User> findByRefreshToken(String refreshToken);
}
13 changes: 10 additions & 3 deletions src/main/java/leets/weeth/domain/user/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
Expand All @@ -16,18 +17,24 @@ public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;

public void signUp(UserDTO.SignUp requestDto) throws Exception {
if (userRepository.findByUsername(requestDto.getUsername()).isPresent())
public void signUp(UserDTO.SignUp requestDto) {
if (userRepository.findByEmail(requestDto.getEmail()).isPresent())
throw new EntityExistsException("이미 존재하는 아이디입니다.");

// 수정: 아이디 이외 중복 처리

User user = User.builder()
.username(requestDto.getUsername())
.email(requestDto.getEmail())
.password(passwordEncoder.encode(requestDto.getPassword()))
.role(Role.USER)
.build();

userRepository.save(user);
}

@Transactional
public void delete(String email) {
userRepository.findByEmail(email)
.ifPresent(User::leave);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response,
userRepository.findByRefreshToken(refreshToken)
.ifPresent(user -> {
String reIssuedRefreshToken = reIssueRefreshToken(user);
String accessToken = jwtService.createAccessToken(user.getId(), user.getUsername());
String accessToken = jwtService.createAccessToken(user.getId(), user.getEmail());
jwtService.sendAccessAndRefreshToken(response, accessToken, reIssuedRefreshToken);
jwtService.sendAccessToken(response, accessToken);
});
Expand All @@ -71,8 +71,8 @@ public void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpSe
log.info("checkAccessTokenAndAuthentication() 호출");
jwtService.extractAccessToken(request)
.filter(jwtService::isTokenValid)
.ifPresent(accessToken -> jwtService.extractUsername(accessToken)
.ifPresent(username -> userRepository.findByUsername(username)
.ifPresent(accessToken -> jwtService.extractEmail(accessToken)
.ifPresent(email -> userRepository.findByEmail(email)
.ifPresent(this::saveAuthentication)));

filterChain.doFilter(request, response);
Expand All @@ -82,7 +82,7 @@ public void saveAuthentication(User myUser) {
String password = myUser.getPassword();

UserDetails userDetailsUser = org.springframework.security.core.userdetails.User.builder()
.username(myUser.getUsername())
.username(myUser.getEmail())
.password(password)
.roles(myUser.getRole().name())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ public class JwtService {

private static final String ACCESS_TOKEN_SUBJECT = "AccessToken";
private static final String REFRESH_TOKEN_SUBJECT = "RefreshToken";
private static final String USERNAME_CLAIM = "username";
private static final String EMAIL_CLAIM = "email";
private static final String ID_CLAIM = "id";
private static final String BEARER = "Bearer ";

private final UserRepository userRepository;

public String createAccessToken(Long id, String username) {
public String createAccessToken(Long id, String email) {
Date now = new Date();
return JWT.create()
.withSubject(ACCESS_TOKEN_SUBJECT)
.withExpiresAt(new Date(now.getTime() + accessTokenExpirationPeriod))
.withClaim(ID_CLAIM, id)
.withClaim(USERNAME_CLAIM, username)
.withClaim(EMAIL_CLAIM, email)
.sign(Algorithm.HMAC512(key));
}

Expand Down Expand Up @@ -88,12 +88,12 @@ public Optional<String> extractAccessToken(HttpServletRequest request) {
.map(refreshToken -> refreshToken.replace(BEARER, ""));
}

public Optional<String> extractUsername(String accessToken) {
public Optional<String> extractEmail(String accessToken) {
try {
return Optional.ofNullable(JWT.require(Algorithm.HMAC512(key))
.build()
.verify(accessToken)
.getClaim(USERNAME_CLAIM)
.getClaim(EMAIL_CLAIM)
.asString());
} catch (Exception e) {
log.error("액세스 토큰이 유효하지 않습니다.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class CustomJsonUsernamePasswordAuthenticationFilter extends AbstractAuth
private static final String DEFAULT_LOGIN_REQUEST_URL = "/login";
private static final String HTTP_METHOD = "POST";
private static final String CONTENT_TYPE = "application/json";
private static final String USERNAME_KEY = "username";
private static final String EMAIL_KEY = "email";
private static final String PASSWORD_KEY = "password";
private static final AntPathRequestMatcher DEFAULT_LOGIN_PATH_REQUEST_MATCHER =
new AntPathRequestMatcher(DEFAULT_LOGIN_REQUEST_URL, HTTP_METHOD);
Expand All @@ -42,10 +42,10 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ

Map<String, String> usernamePasswordMap = objectMapper.readValue(messageBody, Map.class);

String username = usernamePasswordMap.get(USERNAME_KEY);
String email = usernamePasswordMap.get(EMAIL_KEY);
String password = usernamePasswordMap.get(PASSWORD_KEY);

UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(email, password);

return this.getAuthenticationManager().authenticate(authRequest);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) {
String username = extractUsername(authentication); // 인증 정보에서 Username(username) 추출
Optional<User> optionalUser = userRepository.findByUsername(username);
String email = extractEmail(authentication); // 인증 정보에서 email 추출
Optional<User> optionalUser = userRepository.findByEmail(email);

String accessToken = jwtService.createAccessToken(optionalUser.get().getId(), username); // JwtService의 createAccessToken을 사용하여 AccessToken 발급
String accessToken = jwtService.createAccessToken(optionalUser.get().getId(), email); // JwtService의 createAccessToken을 사용하여 AccessToken 발급
String refreshToken = jwtService.createRefreshToken(); // JwtService의 createRefreshToken을 사용하여 RefreshToken 발급

jwtService.sendAccessAndRefreshToken(response, accessToken, refreshToken); // 응답 헤더에 AccessToken, RefreshToken 실어서 응답
Expand All @@ -41,12 +41,12 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
userRepository.saveAndFlush(user);
});

log.info("로그인에 성공하였습니다. 아이디 : {}", username);
log.info("로그인에 성공하였습니다. 아이디 : {}", email);
log.info("로그인에 성공하였습니다. AccessToken : {}", accessToken);
log.info("발급된 AccessToken 만료 기간 : {}", accessTokenExpiration);
}

private String extractUsername(Authentication authentication) {
private String extractEmail(Authentication authentication) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
return userDetails.getUsername();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ public class LoginService implements UserDetailsService {
private final UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("해당 이메일이 존재하지 않습니다."));

return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.username(user.getEmail())
.password(user.getPassword())
.roles(user.getRole().name())
.build();
Expand Down

0 comments on commit 7f05676

Please sign in to comment.