Skip to content

Commit

Permalink
fix : 비밀번호 재설정 로직 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
U-Geon committed Jul 25, 2024
1 parent c7d0a1b commit 898ed26
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 33 deletions.
23 changes: 23 additions & 0 deletions src/main/java/bootcamp/wssrs/domain/File/entity/File.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package bootcamp.wssrs.domain.File.entity;

import bootcamp.wssrs.domain.Notice.entity.Notice;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class File {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String url;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "notice_id")
private Notice notice;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
Expand All @@ -24,16 +25,16 @@ public class MemberController {

// 로그인
@PostMapping("/login")
@Operation(summary = "로그인", description = "학번, 비밀번호 입력")
public ResponseEntity<TokenDTO> login(@Parameter(required = true, description = "학번, 비밀번호")
@Operation(summary = "로그인", description = "이메일, 비밀번호 입력")
public ResponseEntity<TokenDTO> login(@Parameter(required = true, description = "이메일, 비밀번호")
@RequestBody @Valid LoginRequestDTO requestDTO) {
return ResponseEntity.ok(memberService.login(requestDTO));
}

// 회원가입
@PostMapping("/sign-up")
@Operation(summary = "회원 가입", description = "학번, 이름, 비밀번호 입력")
public ResponseEntity<Member> signup(@Parameter(required = true, description = "학번, 이름, 비밀번호")
@Operation(summary = "회원 가입", description = "이메일, 학번, 이름, 비밀번호 입력해서 회원가입")
public ResponseEntity<Member> signup(@Parameter(required = true, description = "이메일, 학번, 이름, 비밀번호")
@RequestBody @Valid SignupRequestDTO requestDTO) {
return ResponseEntity.ok(memberService.signUp(requestDTO));
}
Expand All @@ -48,7 +49,7 @@ public ResponseEntity<TokenDTO> findMember(@RequestBody @Valid FindMemberRequest

// 비밀번호 재설정
@PostMapping("/pw/update")
@Operation(summary = "비밀번호 재설정")
@Operation(summary = "비밀번호 재설정", description = "header : access token 필요")
public ResponseEntity<PasswordUpdateResponseDTO> updatePassword(
@Parameter(required = true, description = "새 비밀번호 입력")
@RequestBody @Valid PasswordUpdateRequestDTO requestDTO,
Expand All @@ -58,7 +59,7 @@ public ResponseEntity<PasswordUpdateResponseDTO> updatePassword(

// access token 재발급
@PostMapping("/refresh")
@Operation(summary = "Access Token 재발급", description = "Refresh Token을 Body에 담아 전송")
@Operation(summary = "Access Token 재발급", description = "header: access token 필요 + body: refresh token")
public ResponseEntity<ReissueAccessTokenResponseDTO> reissueAccessToken(
@Parameter(required = true, description = "Refresh Token")
@RequestBody @Valid ReissueAccessTokenRequestDTO requestDTO,
Expand All @@ -68,9 +69,8 @@ public ResponseEntity<ReissueAccessTokenResponseDTO> reissueAccessToken(

// logout
@GetMapping("/logout")
@Operation(summary = "로그아웃")
public void logout(@AuthenticationPrincipal Member member,
@RequestBody @Valid TokenDTO tokenDTO) {
memberService.logout(member.getStudentId(), tokenDTO);
@Operation(summary = "로그아웃", description = "액세스 토큰 + 리프레시 토큰 제거")
public void logout(@RequestBody @Valid TokenDTO tokenDTO) {
memberService.logout(tokenDTO);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public class LoadUserService implements UserDetailsService {

// jwt filter
@Override
public Member loadUserByUsername(String studentId) throws UsernameNotFoundException {
return memberRepository.findByEmail(studentId)
public Member loadUserByUsername(String email) throws UsernameNotFoundException {
return memberRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("가입되어 있지 않은 유저입니다."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
Expand All @@ -28,6 +29,7 @@
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class MemberService {

private final MemberRepository memberRepository;
Expand All @@ -48,7 +50,7 @@ public TokenDTO login(LoginRequestDTO requestDTO) {
String refreshToken = jwtProvider.createRefreshToken();

// refresh token 저장.
if(redisService.getRefreshToken(email).equals(refreshToken)) {
if(redisService.getRefreshToken(email) != null) {
redisService.deleteRefreshToken(email);
}
redisService.saveRefreshToken(email, refreshToken);
Expand Down Expand Up @@ -84,7 +86,6 @@ public TokenDTO findMember(FindMemberRequestDTO requestDTO) {
@Transactional
public PasswordUpdateResponseDTO updatePassword(String email, PasswordUpdateRequestDTO requestDTO) {
memberRepository.findByEmail(email)
.filter(member -> passwordEncoder.matches(requestDTO.getNewPassword(), member.getPassword())) // 암호화된 비밀번호와 일치 여부 확인
.map(member -> {
member.setPassword(requestDTO.getNewPassword(), passwordEncoder); // 새 비밀번호를 암호화하도록 수정
return new PasswordUpdateResponseDTO(requestDTO.getNewPassword());
Expand All @@ -109,14 +110,13 @@ public ReissueAccessTokenResponseDTO reissueAccessToken(String refreshToken, Mem

// 로그아웃
@Transactional
public void logout(String studentId, TokenDTO tokenDTO) {
public void logout(TokenDTO tokenDTO) {

String accessToken = tokenDTO.getAccessToken();
String studentId = jwtProvider.getEmailFromAccessToken(accessToken);

// redis에서 refresh Token 제거
if (redisService.getRefreshToken(studentId) != null) {
redisService.deleteRefreshToken(studentId);
}
redisService.deleteRefreshToken(studentId);

// 해당 엑세스 토큰의 남은 유효시간을 얻어서 Access Token blacklist에 등록하여 만료시키기
Long tokenExpiration = jwtProvider.getTokenExpiration(accessToken);
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/bootcamp/wssrs/domain/recruit/entity/Recruit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package bootcamp.wssrs.domain.recruit.entity;


import bootcamp.wssrs.domain.Notice.entity.Notice;
import bootcamp.wssrs.domain.member.entity.Member;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Recruit {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "notice_id")
private Notice notice;
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti
.csrf(AbstractHttpConfigurer::disable) // csrf 비활성화 -> cookie를 사용하지 않으면 꺼도 된다. (cookie를 사용할 경우 httpOnly(XSS 방어), sameSite(CSRF 방어)로 방어해야 한다.)
.formLogin(AbstractHttpConfigurer::disable) // security 기본 로그인 비활성화
.httpBasic(AbstractHttpConfigurer::disable) // REST API이므로 basic auth 사용 x
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.cors(cors -> cors.configurationSource(corsConfigurationSource())) // cors 설정
// 특정 URL에 대한 권한 설정.
.authorizeHttpRequests(authz -> authz
.requestMatchers("/swagger", "/swagger-ui/index.html", "/swagger-ui/**", "/api-docs", "/api-docs/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/", "/api/auth/login", "/api/auth/sign-up").permitAll() // 특정 url에 대한 인가 요청 허용
.requestMatchers("/", "/api/auth/*", "/api/auth/pw/*").permitAll() // 특정 url에 대한 인가 요청 허용
.requestMatchers("/api/user/*").hasRole("USER") // USER 권한일 때 요청 가능.
.requestMatchers("/api/admin/*").hasRole("ADMIN") // ADMIN 권한일 때 요청.
.anyRequest().authenticated() // 그 외 요청은 인증 필요.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,10 @@ protected void doFilterInternal(HttpServletRequest request,
log.info("Authentication filter 작동.");
log.info("accessToken: {}", accessToken);

if(accessToken == null) {
filterChain.doFilter(request, response);
return ;
}

try {
if(jwtProvider.validate(accessToken)) {
String studentId = jwtProvider.getStudentIdFromAccessToken(accessToken);
Authentication authentication = getAuthentication(studentId);
String email = jwtProvider.getEmailFromAccessToken(accessToken);
Authentication authentication = getAuthentication(email);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (TokenExpiredException e) {
Expand All @@ -69,8 +64,8 @@ private String resolveToken(HttpServletRequest request) {
}

// 인증 정보 가져오기
private Authentication getAuthentication(String studentId) {
Member userDetails = loadUserService.loadUserByUsername(studentId);
private Authentication getAuthentication(String email) {
Member userDetails = loadUserService.loadUserByUsername(email);

return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/bootcamp/wssrs/global/jwt/JwtProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public JwtProvider(@Value("${jwt.secret-key}") String secretKey, RedisService re
this.redisService = redisService;
}

public String getStudentIdFromAccessToken(String token) {
return JWT.decode(token).getClaim("studentId").asString();
public String getEmailFromAccessToken(String token) {
return JWT.decode(token).getClaim("email").asString();
}

public boolean validate(String token) {
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/bootcamp/wssrs/global/jwt/TokenDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class TokenDTO {
private String accessToken;
Expand Down

0 comments on commit 898ed26

Please sign in to comment.