From 8f822a0992d96002fb4a55f05f25f75b3ba62254 Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Sat, 6 Jan 2024 01:01:09 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20Entity=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 13 +++++++++-- .../yanabada/domain/member/entity/Member.java | 22 +++++++++++++++---- .../domain/member/entity/ProviderType.java | 5 +++++ .../domain/member/entity/RoleType.java | 5 +++++ 4 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/ProviderType.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/RoleType.java diff --git a/build.gradle b/build.gradle index 9a29c538..b406c3f2 100644 --- a/build.gradle +++ b/build.gradle @@ -25,14 +25,23 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' - // implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-security' runtimeOnly 'com.h2database:h2' runtimeOnly 'com.mysql:mysql-connector-j' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'io.rest-assured:rest-assured:5.3.2' testImplementation 'org.springframework.boot:spring-boot-starter-test' - // testImplementation 'org.springframework.security:spring-security-test' + testImplementation 'org.springframework.security:spring-security-test' + + // jjwt + implementation 'io.jsonwebtoken:jjwt-api:0.11.2' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.2' + + //oauth +// implementation("org.springframework.boot:spring-boot-starter-oauth2-client") + } tasks.named('test') { diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java index c5d59d18..bec619c4 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java @@ -1,15 +1,29 @@ package kr.co.fastcampus.yanabada.domain.member.entity; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import jakarta.persistence.*; import kr.co.fastcampus.yanabada.common.baseentity.BaseEntity; +import lombok.Getter; +import lombok.NoArgsConstructor; +@Getter +@NoArgsConstructor @Entity public class Member extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private String email; + private String memberName; + private String nickName; + private String password; + private String phoneNumber; + private String imageUrl; + private Integer point; + @Enumerated(EnumType.STRING) + private RoleType roleType; + @Enumerated(EnumType.STRING) + private ProviderType providerType; + private String deviceKey; + } diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/ProviderType.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/ProviderType.java new file mode 100644 index 00000000..4f850424 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/ProviderType.java @@ -0,0 +1,5 @@ +package kr.co.fastcampus.yanabada.domain.member.entity; + +public enum ProviderType { + EMAIL, KAKAO +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/RoleType.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/RoleType.java new file mode 100644 index 00000000..f2dce932 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/RoleType.java @@ -0,0 +1,5 @@ +package kr.co.fastcampus.yanabada.domain.member.entity; + +public enum RoleType { + ROLE_USER, ROLE_ADMIN +} From ad4828d05ce208adf8d8d709acc47c0c9696a074 Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Sat, 6 Jan 2024 01:21:50 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20SecurityConfig=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/SecurityConfig.java | 75 +++++++++++++++++++ .../member/controller/MemberController.java | 13 ++++ .../member/repository/MemberRepository.java | 2 + 3 files changed, 90 insertions(+) create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java new file mode 100644 index 00000000..3a91c030 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java @@ -0,0 +1,75 @@ +package kr.co.fastcampus.yanabada.common.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; +import java.util.List; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + +// private final JwtAuthFilter jwtAuthFilter; + + private static final String[] PERMIT_PATHS = { + "/", + "/**" + }; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + + http.httpBasic(AbstractHttpConfigurer::disable); + http.cors(cors -> cors.configurationSource(corsConfigurationSource())); + http.csrf(AbstractHttpConfigurer::disable); + http.sessionManagement( + session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ); + + http.authorizeHttpRequests(authorize -> authorize + .requestMatchers(PERMIT_PATHS).permitAll() + .anyRequest().authenticated() + ); + + //todo: oauth 설정 예정 +// .oauth2Login() // OAuth2 로그인 설정시작 +// .userInfoEndpoint().userService(customOAuth2UserService) // OAuth2 로그인시 사용자 정보를 가져오는 엔드포인트와 사용자 서비스를 설정 +// .and() +// .failureHandler(oAuth2LoginFailureHandler) // OAuth2 로그인 실패시 처리할 핸들러를 지정해준다. +// .successHandler(oAuth2LoginSuccessHandler); // OAuth2 로그인 성공시 처리할 핸들러를 지정해준다. + + + // JWT 인증 필터를 UsernamePasswordAuthenticationFilter 앞에 추가한다. +// http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + @Bean + CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(List.of("http://localhost:5173", "https://api.weplanplans.site", "https://weplanplans.vercel.app", "https://dev-weplanplans.vercel.app", "http://localhost:8080")); // TODO: 5173 open + configuration.setAllowedMethods(List.of("*")); + configuration.setAllowedHeaders(List.of("*")); + configuration.addExposedHeader("Authorization"); + configuration.setAllowCredentials(true); //todo: 쿠키를 포함한 크로스 도메인 요청을 허용? 확인필요 + configuration.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java new file mode 100644 index 00000000..5647ad04 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java @@ -0,0 +1,13 @@ +package kr.co.fastcampus.yanabada.domain.member.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class MemberController { + + @GetMapping("/test") + public String test() { + return "test"; + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java index 6c40ef32..ea879897 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java @@ -9,4 +9,6 @@ public interface MemberRepository extends JpaRepository { default Member getMember(Long memberId) { return findById(memberId).orElseThrow(MemberNotFoundException::new); } + + } From 9209da24d07119862da716a7203f85b6987d7254 Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Sat, 6 Jan 2024 02:12:14 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20JWT=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=20=EC=9C=A0=ED=8B=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/SecurityConfig.java | 9 +- .../yanabada/common/jwt/JwtAuthFilter.java | 82 +++++++++++++++ .../yanabada/common/jwt/JwtConstant.java | 9 ++ .../common/jwt/JwtExceptionFilter.java | 35 +++++++ .../yanabada/common/jwt/JwtProvider.java | 99 +++++++++++++++++++ .../yanabada/common/jwt/TokenInfoDTO.java | 7 ++ .../common/security/PrincipalDetails.java | 62 ++++++++++++ .../security/PrincipalDetailsService.java | 27 +++++ .../member/repository/MemberRepository.java | 10 ++ src/main/resources/application.yml | 5 +- 10 files changed, 341 insertions(+), 4 deletions(-) create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtAuthFilter.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtConstant.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtExceptionFilter.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtProvider.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/jwt/TokenInfoDTO.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetails.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetailsService.java diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java index 3a91c030..050ce6e0 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java @@ -1,5 +1,7 @@ package kr.co.fastcampus.yanabada.common.config; +import kr.co.fastcampus.yanabada.common.jwt.JwtAuthFilter; +import kr.co.fastcampus.yanabada.common.jwt.JwtExceptionFilter; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -21,7 +23,8 @@ @RequiredArgsConstructor public class SecurityConfig { -// private final JwtAuthFilter jwtAuthFilter; + private final JwtAuthFilter jwtAuthFilter; + private final JwtExceptionFilter jwtExceptionFilter; private static final String[] PERMIT_PATHS = { "/", @@ -51,8 +54,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // .successHandler(oAuth2LoginSuccessHandler); // OAuth2 로그인 성공시 처리할 핸들러를 지정해준다. - // JWT 인증 필터를 UsernamePasswordAuthenticationFilter 앞에 추가한다. -// http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); + http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(jwtExceptionFilter, JwtAuthFilter.class); return http.build(); } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtAuthFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtAuthFilter.java new file mode 100644 index 00000000..7847747d --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtAuthFilter.java @@ -0,0 +1,82 @@ +package kr.co.fastcampus.yanabada.common.jwt; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import kr.co.fastcampus.yanabada.common.security.PrincipalDetails; +import kr.co.fastcampus.yanabada.domain.member.entity.Member; +import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; +import kr.co.fastcampus.yanabada.domain.member.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +import static kr.co.fastcampus.yanabada.common.jwt.JwtConstant.AUTHORIZATION_HEADER; +import static kr.co.fastcampus.yanabada.common.jwt.JwtConstant.BEARER_PREFIX; + +@Slf4j +@Component +@RequiredArgsConstructor +public class JwtAuthFilter extends OncePerRequestFilter { + + private final JwtProvider jwtProvider; + private final MemberRepository memberRepository; + + @Override + protected void doFilterInternal( + HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain + ) throws ServletException, IOException { + + String token = extractTokenFromRequest(request); + + // 토큰 검사 생략(모두 허용 URL의 경우 토큰 검사 통과) + if (!StringUtils.hasText(token)) { + doFilter(request, response, filterChain); + return; + } + + // AccessToken을 검증하고, 만료되었을경우 예외를 발생시킨다. + if (!jwtProvider.verifyToken(token)) { + throw new RuntimeException("Access Token 만료!"); //todo: Custom Ex + } + + // AccessToken의 값이 있고, 유효한 경우에 진행한다. + if (jwtProvider.verifyToken(token)) { + String email = jwtProvider.getEmail(token); + ProviderType provider = jwtProvider.getProvider(token); + Member findMember = memberRepository.getMember(email, provider); + + PrincipalDetails principalDetails = PrincipalDetails.of(findMember); + + // SecurityContext에 인증 객체를 등록해준다. + Authentication auth = getAuthentication(principalDetails); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + filterChain.doFilter(request, response); + } + + public Authentication getAuthentication(PrincipalDetails principal) { + return new UsernamePasswordAuthenticationToken( + principal, "", principal.getAuthorities() + ); + } + + private String extractTokenFromRequest(HttpServletRequest request) { + String token = request.getHeader(AUTHORIZATION_HEADER); + if (StringUtils.hasText(token) && token.startsWith(BEARER_PREFIX)) { + return token.substring(7); + } + return null; + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtConstant.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtConstant.java new file mode 100644 index 00000000..5f193022 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtConstant.java @@ -0,0 +1,9 @@ +package kr.co.fastcampus.yanabada.common.jwt; + +public class JwtConstant { + public static final String BEARER_TYPE = "Bearer"; + public static final String BEARER_PREFIX = "Bearer "; + public static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30; // 30분 1000 * 60 * 30 + public static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 7; // 7일 1000 * 60 * 60 * 24 * 7 + public static final String AUTHORIZATION_HEADER = "Authorization"; +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtExceptionFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtExceptionFilter.java new file mode 100644 index 00000000..29a49e18 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtExceptionFilter.java @@ -0,0 +1,35 @@ +package kr.co.fastcampus.yanabada.common.jwt; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@Component +@RequiredArgsConstructor +public class JwtExceptionFilter extends OncePerRequestFilter { + + private final ObjectMapper objectMapper; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + /* ControllerAdvice와 같은 ExHandler 역할 수행 */ + try { + filterChain.doFilter(request, response); + } catch (RuntimeException e) { + response.setStatus(401); + response.setContentType(APPLICATION_JSON_VALUE); + response.setCharacterEncoding("UTF-8"); +// objectMapper.writeValue(response.getWriter(), StatusResponseDto.addStatus(401)); + //todo: response body 채울 예정 + } + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtProvider.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtProvider.java new file mode 100644 index 00000000..311d247e --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtProvider.java @@ -0,0 +1,99 @@ +package kr.co.fastcampus.yanabada.common.jwt; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; +import kr.co.fastcampus.yanabada.domain.member.entity.RoleType; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.management.relation.Role; +import java.security.Key; +import java.util.Date; + +@Slf4j +@Component +@RequiredArgsConstructor +public class JwtProvider { + +// private final RefreshTokenService tokenService; + @Value("${jwt.secretKey}") + private String secretKeyPlain; + private Key secretKey; + + @PostConstruct + protected void init() { + log.info("JwtProvider.init() : secretKeyPlain={}", secretKeyPlain); + byte[] keyBytes = Decoders.BASE64URL.decode(secretKeyPlain); + this.secretKey = Keys.hmacShaKeyFor(keyBytes); + } + + public TokenInfoDTO generateTokenInfo(String email, RoleType role, ProviderType provider) { + String accessToken = generateAccessToken(email, role, provider); + String refreshToken = generateRefreshToken(email, role, provider); + +// tokenService.saveTokenInfo(email, refreshToken, accessToken); todo: 토큰을 redis에 저장 + return new TokenInfoDTO(accessToken, refreshToken); + } + + private String generateAccessToken(String email, RoleType role, ProviderType provider) { + return generateToken(email, role, provider, JwtConstant.ACCESS_TOKEN_EXPIRE_TIME); + } + + private String generateRefreshToken(String email, RoleType role, ProviderType provider) { + return generateToken(email, role, provider, JwtConstant.REFRESH_TOKEN_EXPIRE_TIME); + } + + private String generateToken( + String email, RoleType role, + ProviderType provider, long tokenExpireTime + ) { + Claims claims = Jwts.claims().setSubject(email); + claims.put("role", role); + claims.put("provider", provider); + + Date now = new Date(); + + return Jwts.builder() + .setClaims(claims) // Payload를 구성하는 속성들을 정의한다. + .setIssuedAt(now) // 발행일자를 넣는다. + .setExpiration(new Date(now.getTime() + tokenExpireTime)) // 토큰의 만료일시를 설정한다. + .signWith(secretKey) // 지정된 서명 알고리즘과 비밀 키를 사용하여 토큰을 서명한다. + .compact(); + } + + public boolean verifyToken(String token) { + try { + Jws claims = + Jwts.parserBuilder().setSigningKey(secretKey) + .build().parseClaimsJws(token); + // 토큰의 만료 시간과 현재 시간비교 + return claims.getBody() + .getExpiration() + .after(new Date()); + } catch (Exception e) { + return false; + } + } + + public String getEmail(String token) { + return Jwts.parserBuilder().setSigningKey(secretKey) + .build().parseClaimsJws(token).getBody().getSubject(); + } + + public RoleType getRole(String token) { + return Jwts.parserBuilder().setSigningKey(secretKey) + .build().parseClaimsJws(token).getBody().get("role", RoleType.class); + } + + public ProviderType getProvider(String token) { + return Jwts.parserBuilder().setSigningKey(secretKey) + .build().parseClaimsJws(token).getBody().get("role", ProviderType.class); + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/TokenInfoDTO.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/TokenInfoDTO.java new file mode 100644 index 00000000..757ccdf2 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/TokenInfoDTO.java @@ -0,0 +1,7 @@ +package kr.co.fastcampus.yanabada.common.jwt; + +public record TokenInfoDTO ( + String accessToken, + String refreshToken +) { +} \ No newline at end of file diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetails.java b/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetails.java new file mode 100644 index 00000000..05721d65 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetails.java @@ -0,0 +1,62 @@ +package kr.co.fastcampus.yanabada.common.security; + + +import kr.co.fastcampus.yanabada.domain.member.entity.Member; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; + +import static kr.co.fastcampus.yanabada.domain.member.entity.RoleType.ROLE_USER; + +public record PrincipalDetails( + String email, + String password, + String memberName +) implements UserDetails { + + public static PrincipalDetails of(Member member) { + return new PrincipalDetails(member.getEmail(), + member.getPassword(), + member.getMemberName() + ); + } + + @Override + public Collection getAuthorities() { + // TODO: 역할 관련 협의 필요 + return List.of(new SimpleGrantedAuthority(ROLE_USER.name())); + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getUsername() { + return email; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetailsService.java b/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetailsService.java new file mode 100644 index 00000000..01699028 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetailsService.java @@ -0,0 +1,27 @@ +package kr.co.fastcampus.yanabada.common.security; + +import kr.co.fastcampus.yanabada.domain.member.entity.Member; +import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; +import kr.co.fastcampus.yanabada.domain.member.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import static kr.co.fastcampus.yanabada.domain.member.entity.ProviderType.EMAIL; + +@Slf4j +@Service +@RequiredArgsConstructor +public class PrincipalDetailsService implements UserDetailsService { + + private final MemberRepository memberRepository; + + @Override + public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { + Member member = memberRepository.getMember(email, EMAIL); + return PrincipalDetails.of(member); + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java index ea879897..7b04a735 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java @@ -2,13 +2,23 @@ import kr.co.fastcampus.yanabada.common.exception.MemberNotFoundException; import kr.co.fastcampus.yanabada.domain.member.entity.Member; +import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface MemberRepository extends JpaRepository { default Member getMember(Long memberId) { return findById(memberId).orElseThrow(MemberNotFoundException::new); } + default Member getMember(String email, ProviderType providerType) { + return findMemberByEmailAndProviderType(email, providerType) + .orElseThrow(MemberNotFoundException::new); + } + + Optional findMemberByEmailAndProviderType(String email, ProviderType providerType); + } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 67d461bc..64f3e212 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -15,4 +15,7 @@ spring: properties: hibernate: show_sql: true - format_sql: true \ No newline at end of file + format_sql: true + +jwt: + secretKey: yanabadaSecretKeyyanabadaSecretKeyyanabadaSecretKey \ No newline at end of file From 21cea721c46526a575a2a7f19a551a27b31a93a6 Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Sun, 7 Jan 2024 01:53:26 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20Redis=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20=EB=B0=8F=20RedisService=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 ++ docker-compose.yml | 8 ++++ .../yanabada/common/config/RedisConfig.java | 43 +++++++++++++++++++ .../common/config/SecurityConfig.java | 15 +++++-- .../jwt/{ => constant}/JwtConstant.java | 2 +- .../common/jwt/{ => dto}/TokenInfoDTO.java | 2 +- .../jwt/{ => filter}/JwtAuthFilter.java | 13 ++++-- .../jwt/{ => filter}/JwtExceptionFilter.java | 2 +- .../common/jwt/{ => util}/JwtProvider.java | 8 +++- .../yanabada/common/redis/RedisUtils.java | 28 ++++++++++++ .../member/controller/MemberController.java | 42 ++++++++++++++++++ .../yanabada/domain/member/entity/Member.java | 4 ++ .../domain/member/service/MemberService.java | 30 +++++++++++++ src/main/resources/application.yml | 5 ++- 14 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 docker-compose.yml create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/config/RedisConfig.java rename src/main/java/kr/co/fastcampus/yanabada/common/jwt/{ => constant}/JwtConstant.java (88%) rename src/main/java/kr/co/fastcampus/yanabada/common/jwt/{ => dto}/TokenInfoDTO.java (62%) rename src/main/java/kr/co/fastcampus/yanabada/common/jwt/{ => filter}/JwtAuthFilter.java (84%) rename src/main/java/kr/co/fastcampus/yanabada/common/jwt/{ => filter}/JwtExceptionFilter.java (96%) rename src/main/java/kr/co/fastcampus/yanabada/common/jwt/{ => util}/JwtProvider.java (91%) create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/redis/RedisUtils.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/domain/member/service/MemberService.java diff --git a/build.gradle b/build.gradle index b406c3f2..9978b1c0 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,9 @@ dependencies { //oauth // implementation("org.springframework.boot:spring-boot-starter-oauth2-client") + //redis + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + } tasks.named('test') { diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..180c6b03 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +version : "3" +services : + redis: + hostname: yanabada + container_name: yanabada-redis + image: redis:latest + ports: + - "6379:6379" diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/config/RedisConfig.java b/src/main/java/kr/co/fastcampus/yanabada/common/config/RedisConfig.java new file mode 100644 index 00000000..31aeb969 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/config/RedisConfig.java @@ -0,0 +1,43 @@ +package kr.co.fastcampus.yanabada.common.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Slf4j +@Configuration +@EnableRedisRepositories +public class RedisConfig { + + @Value("${spring.redis.host}") + private String host; + @Value("${spring.redis.port}") + private int port; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisStandaloneConfiguration + = new RedisStandaloneConfiguration(); + redisStandaloneConfiguration.setHostName(host); + redisStandaloneConfiguration.setPort(port); + return new LettuceConnectionFactory(redisStandaloneConfiguration); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + + return redisTemplate; + } +} \ No newline at end of file diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java index 050ce6e0..a9e0be0a 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java @@ -1,12 +1,15 @@ package kr.co.fastcampus.yanabada.common.config; -import kr.co.fastcampus.yanabada.common.jwt.JwtAuthFilter; -import kr.co.fastcampus.yanabada.common.jwt.JwtExceptionFilter; +import kr.co.fastcampus.yanabada.common.jwt.filter.JwtAuthFilter; +import kr.co.fastcampus.yanabada.common.jwt.filter.JwtExceptionFilter; import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.security.servlet.PathRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; @@ -15,7 +18,6 @@ import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import java.util.Arrays; import java.util.List; @Configuration @@ -75,4 +77,11 @@ CorsConfigurationSource corsConfigurationSource() { return source; } + @Bean + @ConditionalOnProperty(name = "spring.h2.console.enabled",havingValue = "true") + public WebSecurityCustomizer configureH2ConsoleEnable() { // h2-console 화면설정 + return web -> web.ignoring() + .requestMatchers(PathRequest.toH2Console()); + } + } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtConstant.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/constant/JwtConstant.java similarity index 88% rename from src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtConstant.java rename to src/main/java/kr/co/fastcampus/yanabada/common/jwt/constant/JwtConstant.java index 5f193022..0096354d 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtConstant.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/constant/JwtConstant.java @@ -1,4 +1,4 @@ -package kr.co.fastcampus.yanabada.common.jwt; +package kr.co.fastcampus.yanabada.common.jwt.constant; public class JwtConstant { public static final String BEARER_TYPE = "Bearer"; diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/TokenInfoDTO.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/dto/TokenInfoDTO.java similarity index 62% rename from src/main/java/kr/co/fastcampus/yanabada/common/jwt/TokenInfoDTO.java rename to src/main/java/kr/co/fastcampus/yanabada/common/jwt/dto/TokenInfoDTO.java index 757ccdf2..2723be82 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/TokenInfoDTO.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/dto/TokenInfoDTO.java @@ -1,4 +1,4 @@ -package kr.co.fastcampus.yanabada.common.jwt; +package kr.co.fastcampus.yanabada.common.jwt.dto; public record TokenInfoDTO ( String accessToken, diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtAuthFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java similarity index 84% rename from src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtAuthFilter.java rename to src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java index 7847747d..9e0394e6 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtAuthFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java @@ -1,9 +1,10 @@ -package kr.co.fastcampus.yanabada.common.jwt; +package kr.co.fastcampus.yanabada.common.jwt.filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import kr.co.fastcampus.yanabada.common.jwt.util.JwtProvider; import kr.co.fastcampus.yanabada.common.security.PrincipalDetails; import kr.co.fastcampus.yanabada.domain.member.entity.Member; import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; @@ -19,8 +20,8 @@ import java.io.IOException; -import static kr.co.fastcampus.yanabada.common.jwt.JwtConstant.AUTHORIZATION_HEADER; -import static kr.co.fastcampus.yanabada.common.jwt.JwtConstant.BEARER_PREFIX; +import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.AUTHORIZATION_HEADER; +import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.BEARER_PREFIX; @Slf4j @Component @@ -30,6 +31,12 @@ public class JwtAuthFilter extends OncePerRequestFilter { private final JwtProvider jwtProvider; private final MemberRepository memberRepository; + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + /* 토큰 재발급, 로그아웃일 경우 해당 필터 실행 안됨 */ + return request.getRequestURI().contains("token/"); + } + @Override protected void doFilterInternal( HttpServletRequest request, diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtExceptionFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java similarity index 96% rename from src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtExceptionFilter.java rename to src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java index 29a49e18..c8b7fde1 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtExceptionFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java @@ -1,4 +1,4 @@ -package kr.co.fastcampus.yanabada.common.jwt; +package kr.co.fastcampus.yanabada.common.jwt.filter; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.FilterChain; diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtProvider.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java similarity index 91% rename from src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtProvider.java rename to src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java index 311d247e..0f040d66 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/JwtProvider.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java @@ -1,4 +1,4 @@ -package kr.co.fastcampus.yanabada.common.jwt; +package kr.co.fastcampus.yanabada.common.jwt.util; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; @@ -6,16 +6,20 @@ import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import jakarta.annotation.PostConstruct; +import kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant; +import kr.co.fastcampus.yanabada.common.jwt.dto.TokenInfoDTO; import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; import kr.co.fastcampus.yanabada.domain.member.entity.RoleType; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; -import javax.management.relation.Role; import java.security.Key; import java.util.Date; +import java.util.concurrent.TimeUnit; @Slf4j @Component diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/redis/RedisUtils.java b/src/main/java/kr/co/fastcampus/yanabada/common/redis/RedisUtils.java new file mode 100644 index 00000000..780ecfab --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/redis/RedisUtils.java @@ -0,0 +1,28 @@ +package kr.co.fastcampus.yanabada.common.redis; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +@Service +public class RedisUtils { + + private final RedisTemplate redisTemplate; + + public RedisUtils(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + public void setData(String key, String value,Long expiredTime){ + redisTemplate.opsForValue().set(key, value, expiredTime, TimeUnit.MILLISECONDS); + } + + public String getData(String key){ + return (String) redisTemplate.opsForValue().get(key); + } + + public void deleteData(String key){ + redisTemplate.delete(key); + } +} \ No newline at end of file diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java index 5647ad04..34186e8f 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java @@ -1,13 +1,55 @@ package kr.co.fastcampus.yanabada.domain.member.controller; +import kr.co.fastcampus.yanabada.common.redis.RedisUtils; +import kr.co.fastcampus.yanabada.domain.member.service.MemberService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; +@Slf4j @RestController +@RequiredArgsConstructor public class MemberController { + private final MemberService memberService; + private final RedisUtils redisUtils; + private final StringRedisTemplate stringStringRedisTemplate; + @GetMapping("/test") public String test() { return "test"; } + + @GetMapping("/test/member") + public String testMember() { + memberService.saveMember(); + return "test"; + } + + @GetMapping("/test/redis/utils") + public String testRedisUtils() { + redisUtils.setData("hi", "hihihi", 60000L); + ValueOperations stringValueOperations = stringStringRedisTemplate.opsForValue(); + stringValueOperations.set("hihi", "hihivalue"); + return "test-redis-utils"; + } + + @GetMapping("/test/redis/get/{key}") + public String getTest(@PathVariable("key") String key) { + log.info("key={}", key); + ValueOperations stringValueOperations = stringStringRedisTemplate.opsForValue(); + String value = stringValueOperations.get(key); + if(value==null) { + value = redisUtils.getData(key); + } + if(value==null) return "null"; + return value; + } + + + } diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java index bec619c4..e924a2bc 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java @@ -2,11 +2,15 @@ import jakarta.persistence.*; import kr.co.fastcampus.yanabada.common.baseentity.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @Getter @NoArgsConstructor +@AllArgsConstructor +@Builder @Entity public class Member extends BaseEntity { diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/service/MemberService.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/service/MemberService.java new file mode 100644 index 00000000..9c7e83cf --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/service/MemberService.java @@ -0,0 +1,30 @@ +package kr.co.fastcampus.yanabada.domain.member.service; + +import kr.co.fastcampus.yanabada.domain.member.entity.Member; +import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; +import kr.co.fastcampus.yanabada.domain.member.entity.RoleType; +import kr.co.fastcampus.yanabada.domain.member.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class MemberService { + + private final MemberRepository memberRepository; + + @Transactional + public void saveMember() { + Member member = Member.builder() + .email("test@test.com") + .memberName("test") + .nickName("test") + .password("1234") + .roleType(RoleType.ROLE_USER) + .providerType(ProviderType.EMAIL) + .build(); + memberRepository.save(member); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 64f3e212..9c4ed147 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,6 +16,9 @@ spring: hibernate: show_sql: true format_sql: true + redis: + host: localhost + port: 6379 jwt: - secretKey: yanabadaSecretKeyyanabadaSecretKeyyanabadaSecretKey \ No newline at end of file + secretKey: yanabadaSecretKeyyanabadaSecretKeyyanabadaSecretKey From dbb53e00eeb44f0a90259fdec9a53bbbf412268f Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Mon, 8 Jan 2024 00:23:12 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20Auth=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8,=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yanabada/common/config/AppConfig.java | 7 ++ .../common/config/SecurityConfig.java | 4 +- .../common/jwt/filter/JwtAuthFilter.java | 12 +++- .../common/jwt/filter/JwtExceptionFilter.java | 6 +- .../common/jwt/service/TokenService.java | 36 ++++++++++ .../yanabada/common/jwt/util/JwtProvider.java | 48 +++++++------ .../auth/controller/AuthController.java | 72 +++++++++++++++++++ .../domain/auth/dto/LoginRequest.java | 12 ++++ .../domain/auth/dto/SignUpRequest.java | 12 ++++ .../domain/auth/service/AuthService.java | 64 +++++++++++++++++ .../yanabada/domain/member/entity/Member.java | 3 +- .../member/repository/MemberRepository.java | 3 +- .../domain/member/service/MemberService.java | 5 ++ 13 files changed, 253 insertions(+), 31 deletions(-) create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/jwt/service/TokenService.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/LoginRequest.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/SignUpRequest.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/config/AppConfig.java b/src/main/java/kr/co/fastcampus/yanabada/common/config/AppConfig.java index d6aef72d..0f1e8680 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/config/AppConfig.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/config/AppConfig.java @@ -6,6 +6,8 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; @Configuration public class AppConfig { @@ -17,4 +19,9 @@ public ObjectMapper objectMapper() { objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return objectMapper; } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java index a9e0be0a..e924b40a 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java @@ -29,8 +29,8 @@ public class SecurityConfig { private final JwtExceptionFilter jwtExceptionFilter; private static final String[] PERMIT_PATHS = { - "/", - "/**" + "/api", + "/api/**" }; @Bean diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java index 9e0394e6..212da1ff 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java @@ -1,9 +1,13 @@ package kr.co.fastcampus.yanabada.common.jwt.filter; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import kr.co.fastcampus.yanabada.common.exception.MemberNotFoundException; import kr.co.fastcampus.yanabada.common.jwt.util.JwtProvider; import kr.co.fastcampus.yanabada.common.security.PrincipalDetails; import kr.co.fastcampus.yanabada.domain.member.entity.Member; @@ -57,10 +61,10 @@ protected void doFilterInternal( throw new RuntimeException("Access Token 만료!"); //todo: Custom Ex } - // AccessToken의 값이 있고, 유효한 경우에 진행한다. - if (jwtProvider.verifyToken(token)) { + try { String email = jwtProvider.getEmail(token); - ProviderType provider = jwtProvider.getProvider(token); + ProviderType provider = ProviderType.valueOf(jwtProvider.getProvider(token)); + Member findMember = memberRepository.getMember(email, provider); PrincipalDetails principalDetails = PrincipalDetails.of(findMember); @@ -68,6 +72,8 @@ protected void doFilterInternal( // SecurityContext에 인증 객체를 등록해준다. Authentication auth = getAuthentication(principalDetails); SecurityContextHolder.getContext().setAuthentication(auth); + } catch(MemberNotFoundException e) { + throw new RuntimeException("Illegal Token"); //todo } filterChain.doFilter(request, response); diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java index c8b7fde1..1c800d82 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java @@ -6,6 +6,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; @@ -13,6 +14,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +@Slf4j @Component @RequiredArgsConstructor public class JwtExceptionFilter extends OncePerRequestFilter { @@ -24,7 +26,9 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse /* ControllerAdvice와 같은 ExHandler 역할 수행 */ try { filterChain.doFilter(request, response); - } catch (RuntimeException e) { + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); response.setStatus(401); response.setContentType(APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/service/TokenService.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/service/TokenService.java new file mode 100644 index 00000000..840a40ab --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/service/TokenService.java @@ -0,0 +1,36 @@ +package kr.co.fastcampus.yanabada.common.jwt.service; + +import kr.co.fastcampus.yanabada.common.redis.RedisUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.REFRESH_TOKEN_EXPIRE_TIME; + +@Service +@RequiredArgsConstructor +public class TokenService { + + private final RedisUtils redisUtils; + + @Transactional + public void saveRefreshToken( + String email, + String provider, + String refreshToken + ) { + String value = email + " " + provider; + redisUtils.setData(refreshToken, value, REFRESH_TOKEN_EXPIRE_TIME); + } + + @Transactional + public String getValue(String refreshToken) { + return redisUtils.getData(refreshToken); + } + + @Transactional + public void deleteRefreshToken(String refreshToken) { + redisUtils.deleteData(refreshToken); + } + +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java index 0f040d66..d2b46ef4 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java @@ -1,32 +1,30 @@ package kr.co.fastcampus.yanabada.common.jwt.util; import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import jakarta.annotation.PostConstruct; import kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant; import kr.co.fastcampus.yanabada.common.jwt.dto.TokenInfoDTO; +import kr.co.fastcampus.yanabada.common.jwt.service.TokenService; import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; import kr.co.fastcampus.yanabada.domain.member.entity.RoleType; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; -import org.springframework.stereotype.Service; import java.security.Key; import java.util.Date; -import java.util.concurrent.TimeUnit; @Slf4j @Component @RequiredArgsConstructor public class JwtProvider { -// private final RefreshTokenService tokenService; + private final TokenService tokenService; + @Value("${jwt.secretKey}") private String secretKeyPlain; private Key secretKey; @@ -38,25 +36,25 @@ protected void init() { this.secretKey = Keys.hmacShaKeyFor(keyBytes); } - public TokenInfoDTO generateTokenInfo(String email, RoleType role, ProviderType provider) { + public TokenInfoDTO generateTokenInfo(String email, String role, String provider) { String accessToken = generateAccessToken(email, role, provider); String refreshToken = generateRefreshToken(email, role, provider); -// tokenService.saveTokenInfo(email, refreshToken, accessToken); todo: 토큰을 redis에 저장 + tokenService.saveRefreshToken(email, provider, refreshToken); return new TokenInfoDTO(accessToken, refreshToken); } - private String generateAccessToken(String email, RoleType role, ProviderType provider) { + public String generateAccessToken(String email, String role, String provider) { return generateToken(email, role, provider, JwtConstant.ACCESS_TOKEN_EXPIRE_TIME); } - private String generateRefreshToken(String email, RoleType role, ProviderType provider) { + public String generateRefreshToken(String email, String role, String provider) { return generateToken(email, role, provider, JwtConstant.REFRESH_TOKEN_EXPIRE_TIME); } private String generateToken( - String email, RoleType role, - ProviderType provider, long tokenExpireTime + String email, String role, + String provider, long tokenExpireTime ) { Claims claims = Jwts.claims().setSubject(email); claims.put("role", role); @@ -74,11 +72,8 @@ private String generateToken( public boolean verifyToken(String token) { try { - Jws claims = - Jwts.parserBuilder().setSigningKey(secretKey) - .build().parseClaimsJws(token); // 토큰의 만료 시간과 현재 시간비교 - return claims.getBody() + return parseClaims(token) .getExpiration() .after(new Date()); } catch (Exception e) { @@ -87,17 +82,24 @@ public boolean verifyToken(String token) { } public String getEmail(String token) { - return Jwts.parserBuilder().setSigningKey(secretKey) - .build().parseClaimsJws(token).getBody().getSubject(); + return parseClaims(token).getSubject(); + } + + public String getRole(String token) { + return parseClaims(token).get("role", String.class); } - public RoleType getRole(String token) { - return Jwts.parserBuilder().setSigningKey(secretKey) - .build().parseClaimsJws(token).getBody().get("role", RoleType.class); + public String getProvider(String token) { + return parseClaims(token).get("provider", String.class); } - public ProviderType getProvider(String token) { - return Jwts.parserBuilder().setSigningKey(secretKey) - .build().parseClaimsJws(token).getBody().get("role", ProviderType.class); + private Claims parseClaims(String accessToken) { + try { + return Jwts.parserBuilder().setSigningKey(secretKey) + .build().parseClaimsJws(accessToken).getBody(); + } catch (Exception e) { + e.printStackTrace(); //todo: CannotParseToken + throw e; + } } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java new file mode 100644 index 00000000..1c17fb23 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java @@ -0,0 +1,72 @@ +package kr.co.fastcampus.yanabada.domain.auth.controller; + +import kr.co.fastcampus.yanabada.common.jwt.service.TokenService; +import kr.co.fastcampus.yanabada.common.jwt.util.JwtProvider; +import kr.co.fastcampus.yanabada.domain.auth.dto.LoginRequest; +import kr.co.fastcampus.yanabada.domain.auth.dto.SignUpRequest; +import kr.co.fastcampus.yanabada.domain.auth.service.AuthService; +import kr.co.fastcampus.yanabada.domain.member.entity.Member; +import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; +import kr.co.fastcampus.yanabada.domain.member.service.MemberService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.AUTHORIZATION_HEADER; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/api") +public class AuthController { + + private final AuthService authService; + private final TokenService tokenService; + private final JwtProvider jwtProvider; + private final MemberService memberService; + + @PostMapping("/sign-up") + public String signUp(@RequestBody SignUpRequest signUpRequest) { + authService.signUp(signUpRequest); + return "sign-up-success"; + } + + @PostMapping("/login") + public String login(@RequestBody LoginRequest loginRequest) { + return authService.login(loginRequest); + } + + @PostMapping("token/logout") + public String logout(@RequestHeader(AUTHORIZATION_HEADER) final String refreshToken) { + + tokenService.deleteRefreshToken(refreshToken); + return "success"; //todo: return값 변경 예정 + } + + @PostMapping("/token/refresh") + public String refresh(@RequestHeader(AUTHORIZATION_HEADER) final String refreshToken) { + + log.info("token = {}", refreshToken); + + if(StringUtils.hasText(refreshToken) && jwtProvider.verifyToken(refreshToken)) { + String value = tokenService.getValue(refreshToken); + if(value==null) { + throw new RuntimeException("RefreshToken이 만료되었습니다."); //todo + } + String[] splits = value.split(" "); + String email = splits[0]; + ProviderType provider = ProviderType.valueOf(splits[1]); + Member findMember = memberService.findMember(email, provider); + + return jwtProvider.generateAccessToken( + findMember.getEmail(), + findMember.getRoleType().name(), + findMember.getProviderType().name() + ); + } + + return "Not Valid refreshToken"; //todo: 반환 값 변경 예정 + } + +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/LoginRequest.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/LoginRequest.java new file mode 100644 index 00000000..a8d34567 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/LoginRequest.java @@ -0,0 +1,12 @@ +package kr.co.fastcampus.yanabada.domain.auth.dto; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; + +public record LoginRequest ( + String email, + String password +) { + public UsernamePasswordAuthenticationToken toAuthentication() { + return new UsernamePasswordAuthenticationToken(email, password); + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/SignUpRequest.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/SignUpRequest.java new file mode 100644 index 00000000..e2705c2f --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/SignUpRequest.java @@ -0,0 +1,12 @@ +package kr.co.fastcampus.yanabada.domain.auth.dto; + +public record SignUpRequest ( + String email, + String password, + String memberName, + String nickName, + String phoneNumber, + String deviceKey + +) { +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java new file mode 100644 index 00000000..701d0823 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java @@ -0,0 +1,64 @@ +package kr.co.fastcampus.yanabada.domain.auth.service; + +import kr.co.fastcampus.yanabada.common.jwt.dto.TokenInfoDTO; +import kr.co.fastcampus.yanabada.common.jwt.util.JwtProvider; +import kr.co.fastcampus.yanabada.domain.auth.dto.LoginRequest; +import kr.co.fastcampus.yanabada.domain.auth.dto.SignUpRequest; +import kr.co.fastcampus.yanabada.domain.member.entity.Member; +import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; +import kr.co.fastcampus.yanabada.domain.member.entity.RoleType; +import kr.co.fastcampus.yanabada.domain.member.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.core.Authentication; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static kr.co.fastcampus.yanabada.domain.member.entity.ProviderType.EMAIL; +import static kr.co.fastcampus.yanabada.domain.member.entity.RoleType.ROLE_USER; + +@Slf4j +@Service +@RequiredArgsConstructor +public class AuthService { + + private final MemberRepository memberRepository; + private final PasswordEncoder passwordEncoder; + private final JwtProvider jwtProvider; + private final AuthenticationManagerBuilder authenticationManagerBuilder; + + @Transactional + public void signUp(SignUpRequest signUpRequest) { + if(memberRepository.existsByEmailAndProviderType(signUpRequest.email(), EMAIL)) { + throw new RuntimeException("이미 존재하는 이메일"); //todo custom + 닉네임도 중복 체크 + } + + String encodedPassword = passwordEncoder.encode(signUpRequest.password()); + Member member = Member.builder() + .email(signUpRequest.email()) + .memberName(signUpRequest.memberName()) + .nickName(signUpRequest.nickName()) + .password(encodedPassword) + .phoneNumber(signUpRequest.phoneNumber()) + .roleType(ROLE_USER) + .providerType(EMAIL) + .build(); + + memberRepository.save(member); + + } + + @Transactional + public String login(LoginRequest loginRequest) { + UsernamePasswordAuthenticationToken authenticationToken = loginRequest.toAuthentication(); + authenticationManagerBuilder.getObject().authenticate(authenticationToken); + TokenInfoDTO tokenInfoDTO + = jwtProvider.generateTokenInfo(loginRequest.email(), ROLE_USER.name(), EMAIL.name()); + + return tokenInfoDTO.accessToken(); + + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java index e924a2bc..3812f061 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java @@ -23,7 +23,8 @@ public class Member extends BaseEntity { private String password; private String phoneNumber; private String imageUrl; - private Integer point; + @Builder.Default + private Integer point = 0; @Enumerated(EnumType.STRING) private RoleType roleType; @Enumerated(EnumType.STRING) diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java index 7b04a735..cee7c443 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java @@ -18,7 +18,8 @@ default Member getMember(String email, ProviderType providerType) { .orElseThrow(MemberNotFoundException::new); } - Optional findMemberByEmailAndProviderType(String email, ProviderType providerType); + boolean existsByEmailAndProviderType(String email, ProviderType provider); + Optional findMemberByEmailAndProviderType(String email, ProviderType providerType); } diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/service/MemberService.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/service/MemberService.java index 9c7e83cf..2bc6a722 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/service/MemberService.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/service/MemberService.java @@ -27,4 +27,9 @@ public void saveMember() { memberRepository.save(member); } + @Transactional + public Member findMember(String email, ProviderType provider) { + return memberRepository.getMember(email, provider); + } + } From e5c6956138ff587b9da756e18e9892c4da3a9d62 Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Mon, 8 Jan 2024 02:25:08 +0900 Subject: [PATCH 06/11] =?UTF-8?q?feat:=20JWT=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=ED=95=B8=EB=93=A4=EB=9F=AC(JwtExceptionFilter)=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/SecurityConfig.java | 2 +- .../exception/ClaimParseFailedException.java | 9 +++++++ .../exception/TokenExpiredException.java | 9 +++++++ .../exception/TokenNotValidatedException.java | 9 +++++++ .../common/jwt/filter/JwtAuthFilter.java | 15 +++++++----- .../common/jwt/filter/JwtExceptionFilter.java | 18 ++++++++++---- .../yanabada/common/jwt/util/JwtProvider.java | 24 +++++++------------ .../yanabada/common/response/ErrorCode.java | 6 +++++ 8 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java create mode 100644 src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java index e924b40a..0a5a9d01 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java @@ -69,7 +69,7 @@ CorsConfigurationSource corsConfigurationSource() { configuration.setAllowedMethods(List.of("*")); configuration.setAllowedHeaders(List.of("*")); configuration.addExposedHeader("Authorization"); - configuration.setAllowCredentials(true); //todo: 쿠키를 포함한 크로스 도메인 요청을 허용? 확인필요 + configuration.setAllowCredentials(true); //todo : 쿠키를 포함한 크로스 도메인 요청을 허용? 확인필요 configuration.setMaxAge(3600L); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java new file mode 100644 index 00000000..e54fef46 --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java @@ -0,0 +1,9 @@ +package kr.co.fastcampus.yanabada.common.exception; + +import kr.co.fastcampus.yanabada.common.response.ErrorCode; + +public class ClaimParseFailedException extends BaseException { + public ClaimParseFailedException() { + super(ErrorCode.CLAIM_PARSE_FAILED.getMessage()); + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java new file mode 100644 index 00000000..2ad08fba --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java @@ -0,0 +1,9 @@ +package kr.co.fastcampus.yanabada.common.exception; + +import kr.co.fastcampus.yanabada.common.response.ErrorCode; + +public class TokenExpiredException extends BaseException { + public TokenExpiredException() { + super(ErrorCode.TOKEN_EXPIRED.getMessage()); + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java new file mode 100644 index 00000000..e74dc72b --- /dev/null +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java @@ -0,0 +1,9 @@ +package kr.co.fastcampus.yanabada.common.exception; + +import kr.co.fastcampus.yanabada.common.response.ErrorCode; + +public class TokenNotValidatedException extends BaseException { + public TokenNotValidatedException() { + super(ErrorCode.TOKEN_NOT_VALIDATED.getMessage()); + } +} diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java index 212da1ff..7bf50c7c 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java @@ -8,6 +8,8 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import kr.co.fastcampus.yanabada.common.exception.MemberNotFoundException; +import kr.co.fastcampus.yanabada.common.exception.TokenExpiredException; +import kr.co.fastcampus.yanabada.common.exception.TokenNotValidatedException; import kr.co.fastcampus.yanabada.common.jwt.util.JwtProvider; import kr.co.fastcampus.yanabada.common.security.PrincipalDetails; import kr.co.fastcampus.yanabada.domain.member.entity.Member; @@ -37,8 +39,10 @@ public class JwtAuthFilter extends OncePerRequestFilter { @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - /* 토큰 재발급, 로그아웃일 경우 해당 필터 실행 안됨 */ - return request.getRequestURI().contains("token/"); + /* 토큰 로그인, 회원가입, 리프레시 토큰 재발급, 로그아웃일 경우 해당 필터 실행 안됨 */ + return request.getRequestURI().contains("token/") + || request.getRequestURI().contains("/sign-up") + || request.getRequestURI().contains("/login"); } @Override @@ -56,9 +60,8 @@ protected void doFilterInternal( return; } - // AccessToken을 검증하고, 만료되었을경우 예외를 발생시킨다. if (!jwtProvider.verifyToken(token)) { - throw new RuntimeException("Access Token 만료!"); //todo: Custom Ex + throw new TokenExpiredException(); } try { @@ -69,11 +72,11 @@ protected void doFilterInternal( PrincipalDetails principalDetails = PrincipalDetails.of(findMember); - // SecurityContext에 인증 객체를 등록해준다. + // SecurityContext에 인증 객체를 등록 Authentication auth = getAuthentication(principalDetails); SecurityContextHolder.getContext().setAuthentication(auth); } catch(MemberNotFoundException e) { - throw new RuntimeException("Illegal Token"); //todo + throw new TokenNotValidatedException(); } filterChain.doFilter(request, response); diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java index 1c800d82..94871dcc 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java @@ -5,6 +5,8 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import kr.co.fastcampus.yanabada.common.exception.TokenExpiredException; +import kr.co.fastcampus.yanabada.common.response.ResponseBody; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -24,16 +26,22 @@ public class JwtExceptionFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { /* ControllerAdvice와 같은 ExHandler 역할 수행 */ + + String responseBody = ""; try { filterChain.doFilter(request, response); - } catch (Exception e) { - e.printStackTrace(); - log.error(e.getMessage()); + } catch(TokenExpiredException e) { + responseBody = objectMapper.writeValueAsString(ResponseBody.fail(e.getMessage())); response.setStatus(401); response.setContentType(APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); -// objectMapper.writeValue(response.getWriter(), StatusResponseDto.addStatus(401)); - //todo: response body 채울 예정 + response.getWriter().write(responseBody); + } catch (Exception e) { + responseBody = objectMapper.writeValueAsString(ResponseBody.fail(e.getMessage())); + response.setStatus(400); + response.setContentType(APPLICATION_JSON_VALUE); + response.setCharacterEncoding("UTF-8"); + response.getWriter().write(responseBody); } } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java index d2b46ef4..3aad6c9d 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java @@ -5,6 +5,7 @@ import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import jakarta.annotation.PostConstruct; +import kr.co.fastcampus.yanabada.common.exception.ClaimParseFailedException; import kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant; import kr.co.fastcampus.yanabada.common.jwt.dto.TokenInfoDTO; import kr.co.fastcampus.yanabada.common.jwt.service.TokenService; @@ -31,7 +32,6 @@ public class JwtProvider { @PostConstruct protected void init() { - log.info("JwtProvider.init() : secretKeyPlain={}", secretKeyPlain); byte[] keyBytes = Decoders.BASE64URL.decode(secretKeyPlain); this.secretKey = Keys.hmacShaKeyFor(keyBytes); } @@ -63,22 +63,17 @@ private String generateToken( Date now = new Date(); return Jwts.builder() - .setClaims(claims) // Payload를 구성하는 속성들을 정의한다. - .setIssuedAt(now) // 발행일자를 넣는다. - .setExpiration(new Date(now.getTime() + tokenExpireTime)) // 토큰의 만료일시를 설정한다. - .signWith(secretKey) // 지정된 서명 알고리즘과 비밀 키를 사용하여 토큰을 서명한다. + .setClaims(claims) // Payload 설정 + .setIssuedAt(now) // 발행일자 설정 + .setExpiration(new Date(now.getTime() + tokenExpireTime)) // 토큰 만료일짜 설정 + .signWith(secretKey) // 비밀 키로 토큰 서명 .compact(); } public boolean verifyToken(String token) { - try { - // 토큰의 만료 시간과 현재 시간비교 - return parseClaims(token) - .getExpiration() - .after(new Date()); - } catch (Exception e) { - return false; - } + return parseClaims(token) + .getExpiration() + .after(new Date()); } public String getEmail(String token) { @@ -98,8 +93,7 @@ private Claims parseClaims(String accessToken) { return Jwts.parserBuilder().setSigningKey(secretKey) .build().parseClaimsJws(accessToken).getBody(); } catch (Exception e) { - e.printStackTrace(); //todo: CannotParseToken - throw e; + throw new ClaimParseFailedException(); } } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/response/ErrorCode.java b/src/main/java/kr/co/fastcampus/yanabada/common/response/ErrorCode.java index 8e6d2a46..816c9024 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/response/ErrorCode.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/response/ErrorCode.java @@ -10,6 +10,12 @@ public enum ErrorCode { ACCOMMODATION_NOT_FOUND("존재하지 않는 숙소입니다."), ROOM_NOT_FOUND("존재하지 않는 객실입니다."), MEMBER_NOT_FOUND("존재하지 않는 회원입니다."), + + //Auth + CLAIM_PARSE_FAILED("토큰의 클레임을 읽을 수 없습니다."), + TOKEN_EXPIRED("토큰이 만료되었습니다."), + TOKEN_NOT_VALIDATED("잘못된 토큰입니다."), + ; private final String message; From b4c036dc080d19939097ff985a984f7c145602e0 Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Mon, 8 Jan 2024 02:41:47 +0900 Subject: [PATCH 07/11] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EB=B0=98=ED=99=98=20dto?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/controller/AuthController.java | 14 ++++++++------ .../yanabada/domain/auth/service/AuthService.java | 13 +++++-------- .../domain/member/controller/MemberController.java | 11 +++-------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java index 1c17fb23..c88a20ac 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java @@ -1,7 +1,10 @@ package kr.co.fastcampus.yanabada.domain.auth.controller; +import kr.co.fastcampus.yanabada.common.exception.TokenExpiredException; +import kr.co.fastcampus.yanabada.common.jwt.dto.TokenInfoDTO; import kr.co.fastcampus.yanabada.common.jwt.service.TokenService; import kr.co.fastcampus.yanabada.common.jwt.util.JwtProvider; +import kr.co.fastcampus.yanabada.common.response.ResponseBody; import kr.co.fastcampus.yanabada.domain.auth.dto.LoginRequest; import kr.co.fastcampus.yanabada.domain.auth.dto.SignUpRequest; import kr.co.fastcampus.yanabada.domain.auth.service.AuthService; @@ -27,14 +30,13 @@ public class AuthController { private final MemberService memberService; @PostMapping("/sign-up") - public String signUp(@RequestBody SignUpRequest signUpRequest) { - authService.signUp(signUpRequest); - return "sign-up-success"; + public ResponseBody signUp(@RequestBody SignUpRequest signUpRequest) { + return ResponseBody.ok(authService.signUp(signUpRequest)); } @PostMapping("/login") - public String login(@RequestBody LoginRequest loginRequest) { - return authService.login(loginRequest); + public ResponseBody login(@RequestBody LoginRequest loginRequest) { + return ResponseBody.ok(authService.login(loginRequest)); } @PostMapping("token/logout") @@ -52,7 +54,7 @@ public String refresh(@RequestHeader(AUTHORIZATION_HEADER) final String refreshT if(StringUtils.hasText(refreshToken) && jwtProvider.verifyToken(refreshToken)) { String value = tokenService.getValue(refreshToken); if(value==null) { - throw new RuntimeException("RefreshToken이 만료되었습니다."); //todo + throw new TokenExpiredException(); //todo: ControllerAdvice에서 핸들러 처리 } String[] splits = value.split(" "); String email = splits[0]; diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java index 701d0823..a7bb1b43 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java @@ -31,7 +31,7 @@ public class AuthService { private final AuthenticationManagerBuilder authenticationManagerBuilder; @Transactional - public void signUp(SignUpRequest signUpRequest) { + public Long signUp(SignUpRequest signUpRequest) { if(memberRepository.existsByEmailAndProviderType(signUpRequest.email(), EMAIL)) { throw new RuntimeException("이미 존재하는 이메일"); //todo custom + 닉네임도 중복 체크 } @@ -47,18 +47,15 @@ public void signUp(SignUpRequest signUpRequest) { .providerType(EMAIL) .build(); - memberRepository.save(member); - + Member savedMember = memberRepository.save(member); + return savedMember.getId(); } @Transactional - public String login(LoginRequest loginRequest) { + public TokenInfoDTO login(LoginRequest loginRequest) { UsernamePasswordAuthenticationToken authenticationToken = loginRequest.toAuthentication(); authenticationManagerBuilder.getObject().authenticate(authenticationToken); - TokenInfoDTO tokenInfoDTO - = jwtProvider.generateTokenInfo(loginRequest.email(), ROLE_USER.name(), EMAIL.name()); - - return tokenInfoDTO.accessToken(); + return jwtProvider.generateTokenInfo(loginRequest.email(), ROLE_USER.name(), EMAIL.name()); } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java index 34186e8f..3332728d 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java @@ -8,6 +8,7 @@ import org.springframework.data.redis.core.ValueOperations; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Slf4j @@ -24,13 +25,7 @@ public String test() { return "test"; } - @GetMapping("/test/member") - public String testMember() { - memberService.saveMember(); - return "test"; - } - - @GetMapping("/test/redis/utils") + @GetMapping("/redis/utils") public String testRedisUtils() { redisUtils.setData("hi", "hihihi", 60000L); ValueOperations stringValueOperations = stringStringRedisTemplate.opsForValue(); @@ -38,7 +33,7 @@ public String testRedisUtils() { return "test-redis-utils"; } - @GetMapping("/test/redis/get/{key}") + @GetMapping("/redis/get/{key}") public String getTest(@PathVariable("key") String key) { log.info("key={}", key); ValueOperations stringValueOperations = stringStringRedisTemplate.opsForValue(); From 7cc248fa98b40c4be1567ae484b01dda1929a947 Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Mon, 8 Jan 2024 11:29:14 +0900 Subject: [PATCH 08/11] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=84=98=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=20import=20->=20static=20import=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD,=20TokenInfoDTO=20=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/SecurityConfig.java | 4 +-- .../exception/ClaimParseFailedException.java | 4 ++- .../CommonEntityNotFoundException.java | 4 ++- .../exception/MemberNotFoundException.java | 4 ++- .../exception/RoomNotFoundException.java | 4 ++- .../exception/TokenExpiredException.java | 4 ++- .../exception/TokenNotValidatedException.java | 4 ++- ...enInfoDTO.java => TokenIssueResponse.java} | 2 +- .../yanabada/common/jwt/util/JwtProvider.java | 8 ++--- .../auth/controller/AuthController.java | 4 +-- .../domain/auth/service/AuthService.java | 7 ++--- .../member/controller/MemberController.java | 29 ------------------- 12 files changed, 28 insertions(+), 50 deletions(-) rename src/main/java/kr/co/fastcampus/yanabada/common/jwt/dto/{TokenInfoDTO.java => TokenIssueResponse.java} (75%) diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java index 0a5a9d01..4734035b 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java @@ -29,8 +29,8 @@ public class SecurityConfig { private final JwtExceptionFilter jwtExceptionFilter; private static final String[] PERMIT_PATHS = { - "/api", - "/api/**" + "/", + "/**" }; @Bean diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java index e54fef46..cd361077 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java @@ -2,8 +2,10 @@ import kr.co.fastcampus.yanabada.common.response.ErrorCode; +import static kr.co.fastcampus.yanabada.common.response.ErrorCode.CLAIM_PARSE_FAILED; + public class ClaimParseFailedException extends BaseException { public ClaimParseFailedException() { - super(ErrorCode.CLAIM_PARSE_FAILED.getMessage()); + super(CLAIM_PARSE_FAILED.getMessage()); } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/CommonEntityNotFoundException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/CommonEntityNotFoundException.java index eb5690a7..5e5f7f66 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/CommonEntityNotFoundException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/CommonEntityNotFoundException.java @@ -2,8 +2,10 @@ import kr.co.fastcampus.yanabada.common.response.ErrorCode; +import static kr.co.fastcampus.yanabada.common.response.ErrorCode.COMMON_ENTITY_NOT_FOUND; + public class CommonEntityNotFoundException extends BaseException { public CommonEntityNotFoundException() { - super(ErrorCode.COMMON_ENTITY_NOT_FOUND.getMessage()); + super(COMMON_ENTITY_NOT_FOUND.getMessage()); } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/MemberNotFoundException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/MemberNotFoundException.java index 6c9c8139..fb1a647b 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/MemberNotFoundException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/MemberNotFoundException.java @@ -2,9 +2,11 @@ import kr.co.fastcampus.yanabada.common.response.ErrorCode; +import static kr.co.fastcampus.yanabada.common.response.ErrorCode.MEMBER_NOT_FOUND; + public class MemberNotFoundException extends BaseException { public MemberNotFoundException() { - super(ErrorCode.MEMBER_NOT_FOUND.getMessage()); + super(MEMBER_NOT_FOUND.getMessage()); } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/RoomNotFoundException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/RoomNotFoundException.java index fb2d1300..fe4d700a 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/RoomNotFoundException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/RoomNotFoundException.java @@ -2,8 +2,10 @@ import kr.co.fastcampus.yanabada.common.response.ErrorCode; +import static kr.co.fastcampus.yanabada.common.response.ErrorCode.ROOM_NOT_FOUND; + public class RoomNotFoundException extends BaseException { public RoomNotFoundException() { - super(ErrorCode.ROOM_NOT_FOUND.getMessage()); + super(ROOM_NOT_FOUND.getMessage()); } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java index 2ad08fba..ead804a8 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java @@ -2,8 +2,10 @@ import kr.co.fastcampus.yanabada.common.response.ErrorCode; +import static kr.co.fastcampus.yanabada.common.response.ErrorCode.TOKEN_EXPIRED; + public class TokenExpiredException extends BaseException { public TokenExpiredException() { - super(ErrorCode.TOKEN_EXPIRED.getMessage()); + super(TOKEN_EXPIRED.getMessage()); } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java index e74dc72b..2bb5b913 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java @@ -2,8 +2,10 @@ import kr.co.fastcampus.yanabada.common.response.ErrorCode; +import static kr.co.fastcampus.yanabada.common.response.ErrorCode.TOKEN_NOT_VALIDATED; + public class TokenNotValidatedException extends BaseException { public TokenNotValidatedException() { - super(ErrorCode.TOKEN_NOT_VALIDATED.getMessage()); + super(TOKEN_NOT_VALIDATED.getMessage()); } } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/dto/TokenInfoDTO.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/dto/TokenIssueResponse.java similarity index 75% rename from src/main/java/kr/co/fastcampus/yanabada/common/jwt/dto/TokenInfoDTO.java rename to src/main/java/kr/co/fastcampus/yanabada/common/jwt/dto/TokenIssueResponse.java index 2723be82..40c62f27 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/dto/TokenInfoDTO.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/dto/TokenIssueResponse.java @@ -1,6 +1,6 @@ package kr.co.fastcampus.yanabada.common.jwt.dto; -public record TokenInfoDTO ( +public record TokenIssueResponse( String accessToken, String refreshToken ) { diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java index 3aad6c9d..51efc20b 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java @@ -7,10 +7,8 @@ import jakarta.annotation.PostConstruct; import kr.co.fastcampus.yanabada.common.exception.ClaimParseFailedException; import kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant; -import kr.co.fastcampus.yanabada.common.jwt.dto.TokenInfoDTO; +import kr.co.fastcampus.yanabada.common.jwt.dto.TokenIssueResponse; import kr.co.fastcampus.yanabada.common.jwt.service.TokenService; -import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; -import kr.co.fastcampus.yanabada.domain.member.entity.RoleType; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -36,12 +34,12 @@ protected void init() { this.secretKey = Keys.hmacShaKeyFor(keyBytes); } - public TokenInfoDTO generateTokenInfo(String email, String role, String provider) { + public TokenIssueResponse generateTokenInfo(String email, String role, String provider) { String accessToken = generateAccessToken(email, role, provider); String refreshToken = generateRefreshToken(email, role, provider); tokenService.saveRefreshToken(email, provider, refreshToken); - return new TokenInfoDTO(accessToken, refreshToken); + return new TokenIssueResponse(accessToken, refreshToken); } public String generateAccessToken(String email, String role, String provider) { diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java index c88a20ac..31feb889 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java @@ -1,7 +1,7 @@ package kr.co.fastcampus.yanabada.domain.auth.controller; import kr.co.fastcampus.yanabada.common.exception.TokenExpiredException; -import kr.co.fastcampus.yanabada.common.jwt.dto.TokenInfoDTO; +import kr.co.fastcampus.yanabada.common.jwt.dto.TokenIssueResponse; import kr.co.fastcampus.yanabada.common.jwt.service.TokenService; import kr.co.fastcampus.yanabada.common.jwt.util.JwtProvider; import kr.co.fastcampus.yanabada.common.response.ResponseBody; @@ -35,7 +35,7 @@ public ResponseBody signUp(@RequestBody SignUpRequest signUpRequest) { } @PostMapping("/login") - public ResponseBody login(@RequestBody LoginRequest loginRequest) { + public ResponseBody login(@RequestBody LoginRequest loginRequest) { return ResponseBody.ok(authService.login(loginRequest)); } diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java index a7bb1b43..e3665bbe 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java @@ -1,18 +1,15 @@ package kr.co.fastcampus.yanabada.domain.auth.service; -import kr.co.fastcampus.yanabada.common.jwt.dto.TokenInfoDTO; +import kr.co.fastcampus.yanabada.common.jwt.dto.TokenIssueResponse; import kr.co.fastcampus.yanabada.common.jwt.util.JwtProvider; import kr.co.fastcampus.yanabada.domain.auth.dto.LoginRequest; import kr.co.fastcampus.yanabada.domain.auth.dto.SignUpRequest; import kr.co.fastcampus.yanabada.domain.member.entity.Member; -import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; -import kr.co.fastcampus.yanabada.domain.member.entity.RoleType; import kr.co.fastcampus.yanabada.domain.member.repository.MemberRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.core.Authentication; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -52,7 +49,7 @@ public Long signUp(SignUpRequest signUpRequest) { } @Transactional - public TokenInfoDTO login(LoginRequest loginRequest) { + public TokenIssueResponse login(LoginRequest loginRequest) { UsernamePasswordAuthenticationToken authenticationToken = loginRequest.toAuthentication(); authenticationManagerBuilder.getObject().authenticate(authenticationToken); diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java index 3332728d..baef7be9 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/controller/MemberController.java @@ -16,35 +16,6 @@ @RequiredArgsConstructor public class MemberController { - private final MemberService memberService; - private final RedisUtils redisUtils; - private final StringRedisTemplate stringStringRedisTemplate; - - @GetMapping("/test") - public String test() { - return "test"; - } - - @GetMapping("/redis/utils") - public String testRedisUtils() { - redisUtils.setData("hi", "hihihi", 60000L); - ValueOperations stringValueOperations = stringStringRedisTemplate.opsForValue(); - stringValueOperations.set("hihi", "hihivalue"); - return "test-redis-utils"; - } - - @GetMapping("/redis/get/{key}") - public String getTest(@PathVariable("key") String key) { - log.info("key={}", key); - ValueOperations stringValueOperations = stringStringRedisTemplate.opsForValue(); - String value = stringValueOperations.get(key); - if(value==null) { - value = redisUtils.getData(key); - } - if(value==null) return "null"; - return value; - } - } From 06d856f040ff4b14e0de6b5ce21f4b400228b55b Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Mon, 8 Jan 2024 17:53:48 +0900 Subject: [PATCH 09/11] =?UTF-8?q?style:=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yanabada/common/config/SecurityConfig.java | 15 ++++----------- .../exception/ClaimParseFailedException.java | 2 -- .../exception/CommonEntityNotFoundException.java | 2 -- .../common/exception/MemberNotFoundException.java | 2 -- .../common/exception/RoomNotFoundException.java | 2 -- .../common/exception/TokenExpiredException.java | 2 -- .../exception/TokenNotValidatedException.java | 2 -- .../yanabada/common/jwt/constant/JwtConstant.java | 4 ++-- .../yanabada/common/jwt/filter/JwtAuthFilter.java | 14 +++++--------- .../common/jwt/filter/JwtExceptionFilter.java | 15 +++++++++------ .../yanabada/common/jwt/service/TokenService.java | 4 ++-- .../yanabada/common/jwt/util/JwtProvider.java | 5 ++--- .../yanabada/common/redis/RedisUtils.java | 9 ++++----- .../common/security/PrincipalDetails.java | 8 +++----- .../common/security/PrincipalDetailsService.java | 5 ++--- .../domain/auth/controller/AuthController.java | 14 +++++++++----- .../yanabada/domain/auth/dto/LoginRequest.java | 2 +- .../yanabada/domain/auth/dto/SignUpRequest.java | 2 +- .../yanabada/domain/auth/service/AuthService.java | 8 ++++---- .../yanabada/domain/member/entity/Member.java | 7 ++++++- .../member/repository/MemberRepository.java | 3 +-- 21 files changed, 55 insertions(+), 72 deletions(-) diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java index 4734035b..59224db5 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/config/SecurityConfig.java @@ -1,5 +1,6 @@ package kr.co.fastcampus.yanabada.common.config; +import java.util.List; import kr.co.fastcampus.yanabada.common.jwt.filter.JwtAuthFilter; import kr.co.fastcampus.yanabada.common.jwt.filter.JwtExceptionFilter; import lombok.RequiredArgsConstructor; @@ -18,8 +19,6 @@ import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import java.util.List; - @Configuration @EnableWebSecurity @RequiredArgsConstructor @@ -29,8 +28,8 @@ public class SecurityConfig { private final JwtExceptionFilter jwtExceptionFilter; private static final String[] PERMIT_PATHS = { - "/", - "/**" + "/", + "/**" }; @Bean @@ -49,12 +48,6 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { ); //todo: oauth 설정 예정 -// .oauth2Login() // OAuth2 로그인 설정시작 -// .userInfoEndpoint().userService(customOAuth2UserService) // OAuth2 로그인시 사용자 정보를 가져오는 엔드포인트와 사용자 서비스를 설정 -// .and() -// .failureHandler(oAuth2LoginFailureHandler) // OAuth2 로그인 실패시 처리할 핸들러를 지정해준다. -// .successHandler(oAuth2LoginSuccessHandler); // OAuth2 로그인 성공시 처리할 핸들러를 지정해준다. - http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) .addFilterBefore(jwtExceptionFilter, JwtAuthFilter.class); @@ -78,7 +71,7 @@ CorsConfigurationSource corsConfigurationSource() { } @Bean - @ConditionalOnProperty(name = "spring.h2.console.enabled",havingValue = "true") + @ConditionalOnProperty(name = "spring.h2.console.enabled", havingValue = "true") public WebSecurityCustomizer configureH2ConsoleEnable() { // h2-console 화면설정 return web -> web.ignoring() .requestMatchers(PathRequest.toH2Console()); diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java index cd361077..357a3cb4 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/ClaimParseFailedException.java @@ -1,7 +1,5 @@ package kr.co.fastcampus.yanabada.common.exception; -import kr.co.fastcampus.yanabada.common.response.ErrorCode; - import static kr.co.fastcampus.yanabada.common.response.ErrorCode.CLAIM_PARSE_FAILED; public class ClaimParseFailedException extends BaseException { diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/CommonEntityNotFoundException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/CommonEntityNotFoundException.java index 74f55a83..58a7ee2e 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/CommonEntityNotFoundException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/CommonEntityNotFoundException.java @@ -2,8 +2,6 @@ import static kr.co.fastcampus.yanabada.common.response.ErrorCode.COMMON_ENTITY_NOT_FOUND; -import static kr.co.fastcampus.yanabada.common.response.ErrorCode.COMMON_ENTITY_NOT_FOUND; - public class CommonEntityNotFoundException extends BaseException { public CommonEntityNotFoundException() { super(COMMON_ENTITY_NOT_FOUND.getMessage()); diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/MemberNotFoundException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/MemberNotFoundException.java index e4b32f71..e90e5c84 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/MemberNotFoundException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/MemberNotFoundException.java @@ -2,8 +2,6 @@ import static kr.co.fastcampus.yanabada.common.response.ErrorCode.MEMBER_NOT_FOUND; -import static kr.co.fastcampus.yanabada.common.response.ErrorCode.MEMBER_NOT_FOUND; - public class MemberNotFoundException extends BaseException { public MemberNotFoundException() { diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/RoomNotFoundException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/RoomNotFoundException.java index b6abe426..513025f8 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/RoomNotFoundException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/RoomNotFoundException.java @@ -2,8 +2,6 @@ import static kr.co.fastcampus.yanabada.common.response.ErrorCode.ROOM_NOT_FOUND; -import static kr.co.fastcampus.yanabada.common.response.ErrorCode.ROOM_NOT_FOUND; - public class RoomNotFoundException extends BaseException { public RoomNotFoundException() { super(ROOM_NOT_FOUND.getMessage()); diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java index ead804a8..a9650bd4 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenExpiredException.java @@ -1,7 +1,5 @@ package kr.co.fastcampus.yanabada.common.exception; -import kr.co.fastcampus.yanabada.common.response.ErrorCode; - import static kr.co.fastcampus.yanabada.common.response.ErrorCode.TOKEN_EXPIRED; public class TokenExpiredException extends BaseException { diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java index 2bb5b913..cd4e53e8 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/exception/TokenNotValidatedException.java @@ -1,7 +1,5 @@ package kr.co.fastcampus.yanabada.common.exception; -import kr.co.fastcampus.yanabada.common.response.ErrorCode; - import static kr.co.fastcampus.yanabada.common.response.ErrorCode.TOKEN_NOT_VALIDATED; public class TokenNotValidatedException extends BaseException { diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/constant/JwtConstant.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/constant/JwtConstant.java index 0096354d..e4b3fc42 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/constant/JwtConstant.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/constant/JwtConstant.java @@ -3,7 +3,7 @@ public class JwtConstant { public static final String BEARER_TYPE = "Bearer"; public static final String BEARER_PREFIX = "Bearer "; - public static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30; // 30분 1000 * 60 * 30 - public static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 7; // 7일 1000 * 60 * 60 * 24 * 7 + public static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30; //30min + public static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 7; //7days public static final String AUTHORIZATION_HEADER = "Authorization"; } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java index 7bf50c7c..ca0671ee 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java @@ -1,12 +1,13 @@ package kr.co.fastcampus.yanabada.common.jwt.filter; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.Jwts; +import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.AUTHORIZATION_HEADER; +import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.BEARER_PREFIX; + import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; import kr.co.fastcampus.yanabada.common.exception.MemberNotFoundException; import kr.co.fastcampus.yanabada.common.exception.TokenExpiredException; import kr.co.fastcampus.yanabada.common.exception.TokenNotValidatedException; @@ -24,11 +25,6 @@ import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; -import java.io.IOException; - -import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.AUTHORIZATION_HEADER; -import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.BEARER_PREFIX; - @Slf4j @Component @RequiredArgsConstructor @@ -75,7 +71,7 @@ protected void doFilterInternal( // SecurityContext에 인증 객체를 등록 Authentication auth = getAuthentication(principalDetails); SecurityContextHolder.getContext().setAuthentication(auth); - } catch(MemberNotFoundException e) { + } catch (MemberNotFoundException e) { throw new TokenNotValidatedException(); } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java index 94871dcc..8ec968d5 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java @@ -1,10 +1,13 @@ package kr.co.fastcampus.yanabada.common.jwt.filter; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; import kr.co.fastcampus.yanabada.common.exception.TokenExpiredException; import kr.co.fastcampus.yanabada.common.response.ResponseBody; import lombok.RequiredArgsConstructor; @@ -12,10 +15,6 @@ import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; -import java.io.IOException; - -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; - @Slf4j @Component @RequiredArgsConstructor @@ -24,13 +23,17 @@ public class JwtExceptionFilter extends OncePerRequestFilter { private final ObjectMapper objectMapper; @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + protected void doFilterInternal( + HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain + ) throws ServletException, IOException { /* ControllerAdvice와 같은 ExHandler 역할 수행 */ String responseBody = ""; try { filterChain.doFilter(request, response); - } catch(TokenExpiredException e) { + } catch (TokenExpiredException e) { responseBody = objectMapper.writeValueAsString(ResponseBody.fail(e.getMessage())); response.setStatus(401); response.setContentType(APPLICATION_JSON_VALUE); diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/service/TokenService.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/service/TokenService.java index 840a40ab..79eb5a53 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/service/TokenService.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/service/TokenService.java @@ -1,12 +1,12 @@ package kr.co.fastcampus.yanabada.common.jwt.service; +import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.REFRESH_TOKEN_EXPIRE_TIME; + import kr.co.fastcampus.yanabada.common.redis.RedisUtils; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.REFRESH_TOKEN_EXPIRE_TIME; - @Service @RequiredArgsConstructor public class TokenService { diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java index 51efc20b..339e2d0b 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/util/JwtProvider.java @@ -5,6 +5,8 @@ import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import jakarta.annotation.PostConstruct; +import java.security.Key; +import java.util.Date; import kr.co.fastcampus.yanabada.common.exception.ClaimParseFailedException; import kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant; import kr.co.fastcampus.yanabada.common.jwt.dto.TokenIssueResponse; @@ -14,9 +16,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import java.security.Key; -import java.util.Date; - @Slf4j @Component @RequiredArgsConstructor diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/redis/RedisUtils.java b/src/main/java/kr/co/fastcampus/yanabada/common/redis/RedisUtils.java index 780ecfab..2c9075bd 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/redis/RedisUtils.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/redis/RedisUtils.java @@ -1,10 +1,9 @@ package kr.co.fastcampus.yanabada.common.redis; +import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; -import java.util.concurrent.TimeUnit; - @Service public class RedisUtils { @@ -14,15 +13,15 @@ public RedisUtils(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } - public void setData(String key, String value,Long expiredTime){ + public void setData(String key, String value, Long expiredTime) { redisTemplate.opsForValue().set(key, value, expiredTime, TimeUnit.MILLISECONDS); } - public String getData(String key){ + public String getData(String key) { return (String) redisTemplate.opsForValue().get(key); } - public void deleteData(String key){ + public void deleteData(String key) { redisTemplate.delete(key); } } \ No newline at end of file diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetails.java b/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetails.java index 05721d65..979f161e 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetails.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetails.java @@ -1,16 +1,14 @@ package kr.co.fastcampus.yanabada.common.security; +import static kr.co.fastcampus.yanabada.domain.member.entity.RoleType.ROLE_USER; +import java.util.Collection; +import java.util.List; import kr.co.fastcampus.yanabada.domain.member.entity.Member; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; -import java.util.Collection; -import java.util.List; - -import static kr.co.fastcampus.yanabada.domain.member.entity.RoleType.ROLE_USER; - public record PrincipalDetails( String email, String password, diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetailsService.java b/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetailsService.java index 01699028..c2eeec10 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetailsService.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/security/PrincipalDetailsService.java @@ -1,7 +1,8 @@ package kr.co.fastcampus.yanabada.common.security; +import static kr.co.fastcampus.yanabada.domain.member.entity.ProviderType.EMAIL; + import kr.co.fastcampus.yanabada.domain.member.entity.Member; -import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; import kr.co.fastcampus.yanabada.domain.member.repository.MemberRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -10,8 +11,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import static kr.co.fastcampus.yanabada.domain.member.entity.ProviderType.EMAIL; - @Slf4j @Service @RequiredArgsConstructor diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java index 31feb889..bb9d257c 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/controller/AuthController.java @@ -1,5 +1,7 @@ package kr.co.fastcampus.yanabada.domain.auth.controller; +import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.AUTHORIZATION_HEADER; + import kr.co.fastcampus.yanabada.common.exception.TokenExpiredException; import kr.co.fastcampus.yanabada.common.jwt.dto.TokenIssueResponse; import kr.co.fastcampus.yanabada.common.jwt.service.TokenService; @@ -14,9 +16,11 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.*; - -import static kr.co.fastcampus.yanabada.common.jwt.constant.JwtConstant.AUTHORIZATION_HEADER; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @@ -51,9 +55,9 @@ public String refresh(@RequestHeader(AUTHORIZATION_HEADER) final String refreshT log.info("token = {}", refreshToken); - if(StringUtils.hasText(refreshToken) && jwtProvider.verifyToken(refreshToken)) { + if (StringUtils.hasText(refreshToken) && jwtProvider.verifyToken(refreshToken)) { String value = tokenService.getValue(refreshToken); - if(value==null) { + if (value == null) { throw new TokenExpiredException(); //todo: ControllerAdvice에서 핸들러 처리 } String[] splits = value.split(" "); diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/LoginRequest.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/LoginRequest.java index a8d34567..3d185ec2 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/LoginRequest.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/LoginRequest.java @@ -2,7 +2,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -public record LoginRequest ( +public record LoginRequest( String email, String password ) { diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/SignUpRequest.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/SignUpRequest.java index e2705c2f..6d88404d 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/SignUpRequest.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/dto/SignUpRequest.java @@ -1,6 +1,6 @@ package kr.co.fastcampus.yanabada.domain.auth.dto; -public record SignUpRequest ( +public record SignUpRequest( String email, String password, String memberName, diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java index e3665bbe..1e58cded 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/auth/service/AuthService.java @@ -1,5 +1,8 @@ package kr.co.fastcampus.yanabada.domain.auth.service; +import static kr.co.fastcampus.yanabada.domain.member.entity.ProviderType.EMAIL; +import static kr.co.fastcampus.yanabada.domain.member.entity.RoleType.ROLE_USER; + import kr.co.fastcampus.yanabada.common.jwt.dto.TokenIssueResponse; import kr.co.fastcampus.yanabada.common.jwt.util.JwtProvider; import kr.co.fastcampus.yanabada.domain.auth.dto.LoginRequest; @@ -14,9 +17,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static kr.co.fastcampus.yanabada.domain.member.entity.ProviderType.EMAIL; -import static kr.co.fastcampus.yanabada.domain.member.entity.RoleType.ROLE_USER; - @Slf4j @Service @RequiredArgsConstructor @@ -29,7 +29,7 @@ public class AuthService { @Transactional public Long signUp(SignUpRequest signUpRequest) { - if(memberRepository.existsByEmailAndProviderType(signUpRequest.email(), EMAIL)) { + if (memberRepository.existsByEmailAndProviderType(signUpRequest.email(), EMAIL)) { throw new RuntimeException("이미 존재하는 이메일"); //todo custom + 닉네임도 중복 체크 } diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java index 3812f061..31b0dd1f 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/entity/Member.java @@ -1,6 +1,11 @@ package kr.co.fastcampus.yanabada.domain.member.entity; -import jakarta.persistence.*; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; import kr.co.fastcampus.yanabada.common.baseentity.BaseEntity; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java b/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java index 674212af..79dbcbb9 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java +++ b/src/main/java/kr/co/fastcampus/yanabada/domain/member/repository/MemberRepository.java @@ -1,12 +1,11 @@ package kr.co.fastcampus.yanabada.domain.member.repository; +import java.util.Optional; import kr.co.fastcampus.yanabada.common.exception.MemberNotFoundException; import kr.co.fastcampus.yanabada.domain.member.entity.Member; import kr.co.fastcampus.yanabada.domain.member.entity.ProviderType; import org.springframework.data.jpa.repository.JpaRepository; -import java.util.Optional; - public interface MemberRepository extends JpaRepository { default Member getMember(Long id) { From ac775d7dd3db43b151f8f837c590e31e4db23e62 Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Tue, 9 Jan 2024 22:52:51 +0900 Subject: [PATCH 10/11] =?UTF-8?q?refactor:=20=EB=A9=94=EC=86=8C=EB=93=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EA=B0=92=20=EB=8C=80=EC=8B=A0=20enum=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/jwt/filter/JwtAuthFilter.java | 2 +- .../common/jwt/filter/JwtExceptionFilter.java | 29 ++++++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java index ca0671ee..b4bcd2ea 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtAuthFilter.java @@ -87,7 +87,7 @@ public Authentication getAuthentication(PrincipalDetails principal) { private String extractTokenFromRequest(HttpServletRequest request) { String token = request.getHeader(AUTHORIZATION_HEADER); if (StringUtils.hasText(token) && token.startsWith(BEARER_PREFIX)) { - return token.substring(7); + return token.substring(BEARER_PREFIX.length()); } return null; } diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java index 8ec968d5..0c223e9e 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java @@ -1,5 +1,7 @@ package kr.co.fastcampus.yanabada.common.jwt.filter; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.UNAUTHORIZED; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import com.fasterxml.jackson.databind.ObjectMapper; @@ -12,6 +14,7 @@ import kr.co.fastcampus.yanabada.common.response.ResponseBody; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; @@ -30,21 +33,25 @@ protected void doFilterInternal( ) throws ServletException, IOException { /* ControllerAdvice와 같은 ExHandler 역할 수행 */ - String responseBody = ""; try { filterChain.doFilter(request, response); } catch (TokenExpiredException e) { - responseBody = objectMapper.writeValueAsString(ResponseBody.fail(e.getMessage())); - response.setStatus(401); - response.setContentType(APPLICATION_JSON_VALUE); - response.setCharacterEncoding("UTF-8"); - response.getWriter().write(responseBody); + completeResponse(response, e, UNAUTHORIZED.value()); } catch (Exception e) { - responseBody = objectMapper.writeValueAsString(ResponseBody.fail(e.getMessage())); - response.setStatus(400); - response.setContentType(APPLICATION_JSON_VALUE); - response.setCharacterEncoding("UTF-8"); - response.getWriter().write(responseBody); + completeResponse(response, e, BAD_REQUEST.value()); } } + + private void completeResponse( + HttpServletResponse response, + Exception e, + int status + ) throws IOException { + String responseBody = objectMapper + .writeValueAsString(ResponseBody.fail(e.getMessage())); + response.setStatus(status); + response.setContentType(APPLICATION_JSON_VALUE); + response.setCharacterEncoding("UTF-8"); + response.getWriter().write(responseBody); + } } From 85d452813c830ea868364dc91ee8a0a35160647c Mon Sep 17 00:00:00 2001 From: tjdtn0219 Date: Tue, 9 Jan 2024 22:56:21 +0900 Subject: [PATCH 11/11] =?UTF-8?q?style:=20=EB=B3=80=EC=88=98=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8=20=EB=B0=8F=20=EC=82=AC=EC=9A=A9=20=EA=B0=84=EA=B2=A9?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yanabada/common/jwt/filter/JwtExceptionFilter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java index 0c223e9e..eaf0ecb6 100644 --- a/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java +++ b/src/main/java/kr/co/fastcampus/yanabada/common/jwt/filter/JwtExceptionFilter.java @@ -47,11 +47,11 @@ private void completeResponse( Exception e, int status ) throws IOException { - String responseBody = objectMapper - .writeValueAsString(ResponseBody.fail(e.getMessage())); response.setStatus(status); response.setContentType(APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); + String responseBody = objectMapper + .writeValueAsString(ResponseBody.fail(e.getMessage())); response.getWriter().write(responseBody); } }