From f7a25b2d394bc5405062ca5d3b7712cc6601fb52 Mon Sep 17 00:00:00 2001 From: Rimi Date: Mon, 11 Nov 2024 23:06:42 +0900 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B[Fix]=20Refresh=20Token=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=83=88=EB=A1=9C=20=EB=A7=8C=EB=93=A4?= =?UTF-8?q?=EC=96=B4=EC=84=9C=20=EC=B2=98=EB=A6=AC=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/gamegoo/domain/member/QMember.java | 2 - .../gamegoo/domain/member/QRefreshToken.java | 61 +++++++++++++++++++ .../apiPayload/code/status/ErrorStatus.java | 2 +- .../com/gamegoo/config/SecurityConfig.java | 4 +- .../com/gamegoo/domain/member/Member.java | 7 --- .../gamegoo/domain/member/RefreshToken.java | 30 +++++++++ .../java/com/gamegoo/filter/LoginFilter.java | 23 +++++-- .../repository/member/MemberRepository.java | 2 - .../member/RefreshTokenRepository.java | 13 ++++ .../gamegoo/service/member/AuthService.java | 23 ++++--- 10 files changed, 139 insertions(+), 28 deletions(-) create mode 100644 src/main/generated/com/gamegoo/domain/member/QRefreshToken.java create mode 100644 src/main/java/com/gamegoo/domain/member/RefreshToken.java create mode 100644 src/main/java/com/gamegoo/repository/member/RefreshTokenRepository.java diff --git a/src/main/generated/com/gamegoo/domain/member/QMember.java b/src/main/generated/com/gamegoo/domain/member/QMember.java index e48a0edd..bdc6b171 100644 --- a/src/main/generated/com/gamegoo/domain/member/QMember.java +++ b/src/main/generated/com/gamegoo/domain/member/QMember.java @@ -67,8 +67,6 @@ public class QMember extends EntityPathBase { public final NumberPath rank = createNumber("rank", Integer.class); - public final StringPath refreshToken = createString("refreshToken"); - public final ListPath reportList = this.createList("reportList", com.gamegoo.domain.report.Report.class, com.gamegoo.domain.report.QReport.class, PathInits.DIRECT2); public final NumberPath subPosition = createNumber("subPosition", Integer.class); diff --git a/src/main/generated/com/gamegoo/domain/member/QRefreshToken.java b/src/main/generated/com/gamegoo/domain/member/QRefreshToken.java new file mode 100644 index 00000000..4de4db9d --- /dev/null +++ b/src/main/generated/com/gamegoo/domain/member/QRefreshToken.java @@ -0,0 +1,61 @@ +package com.gamegoo.domain.member; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QRefreshToken is a Querydsl query type for RefreshToken + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QRefreshToken extends EntityPathBase { + + private static final long serialVersionUID = -263126930L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QRefreshToken refreshToken1 = new QRefreshToken("refreshToken1"); + + public final com.gamegoo.domain.common.QBaseDateTimeEntity _super = new com.gamegoo.domain.common.QBaseDateTimeEntity(this); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath id = createNumber("id", Long.class); + + public final QMember member; + + public final StringPath refreshToken = createString("refreshToken"); + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QRefreshToken(String variable) { + this(RefreshToken.class, forVariable(variable), INITS); + } + + public QRefreshToken(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QRefreshToken(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QRefreshToken(PathMetadata metadata, PathInits inits) { + this(RefreshToken.class, metadata, inits); + } + + public QRefreshToken(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.member = inits.isInitialized("member") ? new QMember(forProperty("member")) : null; + } + +} + diff --git a/src/main/java/com/gamegoo/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/gamegoo/apiPayload/code/status/ErrorStatus.java index 3f35cbf4..80c4ba52 100644 --- a/src/main/java/com/gamegoo/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/gamegoo/apiPayload/code/status/ErrorStatus.java @@ -34,7 +34,7 @@ public enum ErrorStatus implements BaseErrorCode { TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "JWT400", "jwt 토큰이 만료되었습니다."), INVALID_TOKEN(HttpStatus.BAD_REQUEST, "JWT401", "유효하지 않은 jwt 토큰입니다."), TOKEN_NULL(HttpStatus.NOT_FOUND, "JWT402", "JWT 토큰이 없습니다."), - REFRESHTOKEN_NULL(HttpStatus.NOT_FOUND,"JWT403","Refresh Token이 없습니다."), + REFRESHTOKEN_NULL(HttpStatus.NOT_FOUND,"JWT403","Refresh Token이 틀렸습니다."), // GameStyle 관련 에러 GAMESTYLE_NOT_FOUND(HttpStatus.NOT_FOUND, "GAMESTYLE400", "해당 게임 스타일을 찾을 수 없습니다."), diff --git a/src/main/java/com/gamegoo/config/SecurityConfig.java b/src/main/java/com/gamegoo/config/SecurityConfig.java index fe3d34f9..3bb21fe8 100644 --- a/src/main/java/com/gamegoo/config/SecurityConfig.java +++ b/src/main/java/com/gamegoo/config/SecurityConfig.java @@ -7,6 +7,7 @@ import com.gamegoo.filter.LoggingFilter; import com.gamegoo.filter.LoginFilter; import com.gamegoo.repository.member.MemberRepository; +import com.gamegoo.repository.member.RefreshTokenRepository; import com.gamegoo.security.CustomUserDetailService; import com.gamegoo.util.JWTUtil; import java.util.Arrays; @@ -36,6 +37,7 @@ public class SecurityConfig { private final JWTUtil jwtUtil; private final CustomUserDetailService customUserDetailService; private final MemberRepository memberRepository; + private final RefreshTokenRepository refreshTokenRepository; @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) @@ -80,7 +82,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .addFilterAfter(new LoggingFilter(jwtUtil), JWTExceptionHandlerFilter.class) .addFilterAt( new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil, - memberRepository), UsernamePasswordAuthenticationFilter.class) + memberRepository, refreshTokenRepository), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(jwtFilter(), LoginFilter.class) .sessionManagement((session) -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); diff --git a/src/main/java/com/gamegoo/domain/member/Member.java b/src/main/java/com/gamegoo/domain/member/Member.java index de727ff2..3d3b6c23 100644 --- a/src/main/java/com/gamegoo/domain/member/Member.java +++ b/src/main/java/com/gamegoo/domain/member/Member.java @@ -77,9 +77,6 @@ public class Member extends BaseDateTimeEntity { @Column(name = "want_position") private Integer wantPosition = 0; - @Column(name = "refresh_token") - private String refreshToken; - @Column(name = "mike") private Boolean mike = false; @@ -164,10 +161,6 @@ public void updatePassword(String password) { this.password = password; } - public void updateRefreshToken(String refreshToken) { - this.refreshToken = refreshToken; - } - public void setMannerScore(int mannerScore) { this.mannerScore = mannerScore; } public void setMannerLevel(int mannerLevel) { this.mannerLevel = mannerLevel; diff --git a/src/main/java/com/gamegoo/domain/member/RefreshToken.java b/src/main/java/com/gamegoo/domain/member/RefreshToken.java new file mode 100644 index 00000000..a3e03c79 --- /dev/null +++ b/src/main/java/com/gamegoo/domain/member/RefreshToken.java @@ -0,0 +1,30 @@ +package com.gamegoo.domain.member; + +import com.gamegoo.domain.common.BaseDateTimeEntity; +import lombok.*; + +import javax.persistence.*; + +@Entity +@Table(name = "RefreshToken") +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class RefreshToken extends BaseDateTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="refresh_token_id") + private Long id; + + @Column(name = "refresh_token") + private String refreshToken; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + public void updateRefreshToken(String refreshToken){ + this.refreshToken = refreshToken; + } +} diff --git a/src/main/java/com/gamegoo/filter/LoginFilter.java b/src/main/java/com/gamegoo/filter/LoginFilter.java index 4b075bd8..32ec7f22 100644 --- a/src/main/java/com/gamegoo/filter/LoginFilter.java +++ b/src/main/java/com/gamegoo/filter/LoginFilter.java @@ -4,8 +4,10 @@ import com.gamegoo.apiPayload.ApiResponse; import com.gamegoo.apiPayload.code.status.ErrorStatus; import com.gamegoo.domain.member.Member; +import com.gamegoo.domain.member.RefreshToken; import com.gamegoo.dto.member.MemberResponse; import com.gamegoo.repository.member.MemberRepository; +import com.gamegoo.repository.member.RefreshTokenRepository; import com.gamegoo.security.CustomUserDetails; import com.gamegoo.util.JWTUtil; import java.io.IOException; @@ -31,12 +33,14 @@ public class LoginFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; private final JWTUtil jwtUtil; private final MemberRepository memberRepository; + private final RefreshTokenRepository refreshTokenRepository; public LoginFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil, - MemberRepository memberRepository) { + MemberRepository memberRepository, RefreshTokenRepository refreshTokenRepository) { this.authenticationManager = authenticationManager; this.jwtUtil = jwtUtil; this.memberRepository = memberRepository; + this.refreshTokenRepository = refreshTokenRepository; this.setRequiresAuthenticationRequestMatcher( new AntPathRequestMatcher("/v1/member/login", "POST")); this.setUsernameParameter("email"); @@ -77,11 +81,20 @@ protected void successfulAuthentication(HttpServletRequest request, String access_token = jwtUtil.createJwtWithId(id, 60 * 60 * 1000L); // 1시간 String refresh_token = jwtUtil.createJwt(60 * 60 * 24 * 30 * 1000L); // 30일 - // refresh token DB에 저장하기 Member member = memberRepository.findById(id) - .orElseThrow(); - member.updateRefreshToken(refresh_token); - memberRepository.save(member); + .orElseThrow(); + + // 이전에 있던 refreshToken 전부 지우기 + refreshTokenRepository.findByMember(member).stream() + .forEach(refreshTokenRepository::delete); + + // refresh token DB에 저장하기 + RefreshToken newRefreshToken = RefreshToken.builder() + .member(member) + .refreshToken(refresh_token) + .build(); + + refreshTokenRepository.save(newRefreshToken); // 해당 유저 이름 불러오기 String gameuserName = member.getGameName(); diff --git a/src/main/java/com/gamegoo/repository/member/MemberRepository.java b/src/main/java/com/gamegoo/repository/member/MemberRepository.java index bff8f6b3..d5d50b67 100644 --- a/src/main/java/com/gamegoo/repository/member/MemberRepository.java +++ b/src/main/java/com/gamegoo/repository/member/MemberRepository.java @@ -16,8 +16,6 @@ public interface MemberRepository extends JpaRepository { Optional findById(Long id); - Optional findByRefreshToken(String refresh_token); - @Query("SELECT m FROM Member m INNER JOIN Block b ON m.id = b.blockedMember.id WHERE b.blockerMember.id = :blockerId AND b.isDeleted = false ORDER BY b.createdAt DESC") Page findBlockedMembersByBlockerIdAndNotDeleted(@Param("blockerId") Long blockerId, Pageable pageable); diff --git a/src/main/java/com/gamegoo/repository/member/RefreshTokenRepository.java b/src/main/java/com/gamegoo/repository/member/RefreshTokenRepository.java new file mode 100644 index 00000000..54da5f67 --- /dev/null +++ b/src/main/java/com/gamegoo/repository/member/RefreshTokenRepository.java @@ -0,0 +1,13 @@ +package com.gamegoo.repository.member; + +import com.gamegoo.domain.member.Member; +import com.gamegoo.domain.member.RefreshToken; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface RefreshTokenRepository extends JpaRepository { + Optional findByMember(Member member); + + Optional findByRefreshToken(String refreshToken); +} diff --git a/src/main/java/com/gamegoo/service/member/AuthService.java b/src/main/java/com/gamegoo/service/member/AuthService.java index 942e3443..01adc12d 100644 --- a/src/main/java/com/gamegoo/service/member/AuthService.java +++ b/src/main/java/com/gamegoo/service/member/AuthService.java @@ -7,11 +7,9 @@ import com.gamegoo.domain.champion.MemberChampion; import com.gamegoo.domain.member.LoginType; import com.gamegoo.domain.member.Member; +import com.gamegoo.domain.member.RefreshToken; import com.gamegoo.dto.member.MemberResponse; -import com.gamegoo.repository.member.ChampionRepository; -import com.gamegoo.repository.member.EmailVerifyRecordRepository; -import com.gamegoo.repository.member.MemberChampionRepository; -import com.gamegoo.repository.member.MemberRepository; +import com.gamegoo.repository.member.*; import com.gamegoo.util.CodeGeneratorUtil; import com.gamegoo.util.JWTUtil; import com.gamegoo.util.RiotUtil; @@ -39,6 +37,7 @@ public class AuthService { private final ChampionRepository championRepository; private final MemberChampionRepository memberChampionRepository; private final EmailVerifyRecordRepository emailVerifyRecordRepository; + private final RefreshTokenRepository refreshTokenRepository; private final BCryptPasswordEncoder bCryptPasswordEncoder; private final JavaMailSender javaMailSender; private final JWTUtil jwtUtil; @@ -201,8 +200,10 @@ public void sendEmailVerification(String email) { @Transactional public MemberResponse.RefreshTokenResponseDTO verifyRefreshToken(String refresh_token) { // refresh Token 검증하기 - Member member = memberRepository.findByRefreshToken(refresh_token) - .orElseThrow(() -> new MemberHandler(ErrorStatus.REFRESHTOKEN_NULL)); + RefreshToken refreshToken = refreshTokenRepository.findByRefreshToken(refresh_token) + .orElseThrow(() -> new MemberHandler(ErrorStatus.REFRESHTOKEN_NULL)); + + Member member = refreshToken.getMember(); // refresh 토큰에서 id 가져오기 Long id = member.getId(); @@ -212,8 +213,8 @@ public MemberResponse.RefreshTokenResponseDTO verifyRefreshToken(String refresh_ String new_refresh_token = jwtUtil.createJwt(60 * 60 * 24 * 30 * 1000L); // 30일 // refresh token 저장하기 - member.updateRefreshToken(new_refresh_token); - memberRepository.save(member); + refreshToken.updateRefreshToken(new_refresh_token); + refreshTokenRepository.save(refreshToken); return new MemberResponse.RefreshTokenResponseDTO(id, access_token, new_refresh_token); } @@ -442,8 +443,10 @@ public void deleteRefreshToken(Long id) { Member member = memberRepository.findById(id) .orElseThrow(() -> new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND)); - member.updateRefreshToken(null); - memberRepository.save(member); + RefreshToken refreshToken = refreshTokenRepository.findByMember(member) + .orElseThrow(() -> new MemberHandler(ErrorStatus.REFRESHTOKEN_NULL)); + + refreshTokenRepository.delete(refreshToken); }