diff --git a/build.gradle b/build.gradle index 003f137..fb87a3c 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,8 @@ dependencies { implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' + //OAuth 2.0 + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' // S3 AWS implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-aws', version: '2.2.6.RELEASE' diff --git a/src/main/java/org/sophy/sophy/InitDb.java b/src/main/java/org/sophy/sophy/InitDb.java index 3b9c075..d30e235 100644 --- a/src/main/java/org/sophy/sophy/InitDb.java +++ b/src/main/java/org/sophy/sophy/InitDb.java @@ -39,7 +39,7 @@ public void dbInit() { .password(passwordEncoder.encode("Iammember10!")) .phoneNum("01012345678") .marketingAgree(true) - .authority(Authority.ROLE_USER) + .authority(Authority.USER) .build(); em.persist(citizen); @@ -56,7 +56,7 @@ public void dbInit() { .password(passwordEncoder.encode("sophy123")) .phoneNum("01012345678") .marketingAgree(false) - .authority(Authority.ROLE_AUTHOR) + .authority(Authority.AUTHOR) .build(); author1.setAuthorProperty(memauthor1); @@ -66,7 +66,7 @@ public void dbInit() { .password(passwordEncoder.encode("sophy234")) .phoneNum("01023456789") .marketingAgree(false) - .authority(Authority.ROLE_AUTHOR) + .authority(Authority.AUTHOR) .build(); author2.setAuthorProperty(memauthor2); @@ -76,7 +76,7 @@ public void dbInit() { .password(passwordEncoder.encode("sophy345")) .phoneNum("01098765432") .marketingAgree(false) - .authority(Authority.ROLE_AUTHOR) + .authority(Authority.AUTHOR) .build(); author3.setAuthorProperty(memauthor3); @@ -194,7 +194,7 @@ public void dbInit() { .password(passwordEncoder.encode("Iamoperator10!")) .phoneNum("01056784321") .marketingAgree(true) - .authority(Authority.ROLE_OPERATOR) + .authority(Authority.OPERATOR) .build(); oper.setOperatorProperty(memOper); em.persist(oper); diff --git a/src/main/java/org/sophy/sophy/common/component/UserAuditorAware.java b/src/main/java/org/sophy/sophy/common/component/UserAuditorAware.java new file mode 100644 index 0000000..45aa886 --- /dev/null +++ b/src/main/java/org/sophy/sophy/common/component/UserAuditorAware.java @@ -0,0 +1,22 @@ +package org.sophy.sophy.common.component; + +import java.util.Optional; +import org.springframework.data.domain.AuditorAware; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +@Component +public class UserAuditorAware implements AuditorAware { + + @Override + public Optional getCurrentAuditor() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication == null || !authentication.isAuthenticated()) { + return Optional.empty(); + } + + return Optional.of(authentication.getName()); + } +} diff --git a/src/main/java/org/sophy/sophy/config/auth/CustomOAuth2User.java b/src/main/java/org/sophy/sophy/config/auth/CustomOAuth2User.java new file mode 100644 index 0000000..0607d88 --- /dev/null +++ b/src/main/java/org/sophy/sophy/config/auth/CustomOAuth2User.java @@ -0,0 +1,32 @@ +package org.sophy.sophy.config.auth; + +import java.util.Collection; +import java.util.Map; +import lombok.Getter; +import org.sophy.sophy.domain.enumerate.Authority; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; + +@Getter +public class CustomOAuth2User extends DefaultOAuth2User { + + private final String email; + private final Authority authority; + + /** + * Constructs a {@code DefaultOAuth2User} using the provided parameters. + * + * @param authorities the authorities granted to the user + * @param attributes the attributes about the user + * @param nameAttributeKey the key used to access the user's "name" from + * {@link #getAttributes()} + */ + public CustomOAuth2User( + Collection authorities, + Map attributes, String nameAttributeKey, + String email, Authority authority) { + super(authorities, attributes, nameAttributeKey); + this.email = email; + this.authority = authority; + } +} diff --git a/src/main/java/org/sophy/sophy/config/JwtSecurityConfig.java b/src/main/java/org/sophy/sophy/config/auth/JwtSecurityConfig.java similarity index 97% rename from src/main/java/org/sophy/sophy/config/JwtSecurityConfig.java rename to src/main/java/org/sophy/sophy/config/auth/JwtSecurityConfig.java index 9ed7926..b75a7fa 100644 --- a/src/main/java/org/sophy/sophy/config/JwtSecurityConfig.java +++ b/src/main/java/org/sophy/sophy/config/auth/JwtSecurityConfig.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.config; +package org.sophy.sophy.config.auth; import lombok.RequiredArgsConstructor; import org.sophy.sophy.jwt.JwtExceptionFilter; diff --git a/src/main/java/org/sophy/sophy/config/SecurityConfig.java b/src/main/java/org/sophy/sophy/config/auth/SecurityConfig.java similarity index 71% rename from src/main/java/org/sophy/sophy/config/SecurityConfig.java rename to src/main/java/org/sophy/sophy/config/auth/SecurityConfig.java index 44a2611..b464494 100644 --- a/src/main/java/org/sophy/sophy/config/SecurityConfig.java +++ b/src/main/java/org/sophy/sophy/config/auth/SecurityConfig.java @@ -1,8 +1,11 @@ -package org.sophy.sophy.config; +package org.sophy.sophy.config.auth; import java.util.Arrays; import java.util.List; import lombok.RequiredArgsConstructor; +import org.sophy.sophy.config.auth.common.CustomOAuth2UserService; +import org.sophy.sophy.config.auth.common.OAuth2LoginFailureHandler; +import org.sophy.sophy.config.auth.common.OAuth2LoginSuccessHandler; import org.sophy.sophy.jwt.JwtAccessDeniedHandler; import org.sophy.sophy.jwt.JwtAuthenticationEntryPoint; import org.sophy.sophy.jwt.JwtExceptionFilter; @@ -12,6 +15,7 @@ import org.springframework.data.redis.core.RedisTemplate; 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.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -31,6 +35,9 @@ public class SecurityConfig { private final JwtExceptionFilter jwtExceptionFilter; private final RedisTemplate redisTemplate; + private final CustomOAuth2UserService customOAuth2UserService; + private final OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler; + private final OAuth2LoginFailureHandler oAuth2LoginFailureHandler; @Bean public PasswordEncoder passwordEncoder() { @@ -42,7 +49,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { //CSRF 설정 Disable http.csrf().disable() - .cors(cors -> cors.disable()) + .cors(AbstractHttpConfigurer::disable) //exception handling 할 때 우리가 만든 클래스를 추가 .exceptionHandling() @@ -64,18 +71,19 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .and() .authorizeRequests() .antMatchers("/author/**").hasRole("AUTHOR") - .antMatchers("/auth/**").permitAll() - .antMatchers("/profile/**").permitAll() - .antMatchers("/actuator/**").permitAll() - .antMatchers("/health/**").permitAll() - .antMatchers("/home/**").permitAll() - .antMatchers("/booktalk/search/**").permitAll() + .antMatchers("/auth/**", "/profile/**", "/actuator/**", "/health/**").permitAll() + .antMatchers("/home/**", "/booktalk/search/**", "/test/**", "/").permitAll() .antMatchers("/swagger-ui/**", "/v3/api-docs/**", "/api-docs/**", "/swagger-ui.html").permitAll() - .anyRequest().authenticated() //나머지 API는 전부 인증 필요 + .anyRequest().authenticated();//나머지 API는 전부 인증 필요 - //JwtFilter 를 addFilterBefore 로 등록했던 JwtSecurityConfig 클래스를 적용 + http.oauth2Login() + .successHandler(oAuth2LoginSuccessHandler) // 동의하고 계속하기를 눌렀을 때 Handler 설정 + .failureHandler(oAuth2LoginFailureHandler) // 소셜 로그인 실패 시 핸들러 설정 + .userInfoEndpoint().userService(customOAuth2UserService) // customUserService 설정 .and() - .apply(new JwtSecurityConfig(tokenProvider, redisTemplate, jwtExceptionFilter)); + .permitAll(); + //JwtFilter 를 addFilterBefore 로 등록했던 JwtSecurityConfig 클래스를 적용 + http.apply(new JwtSecurityConfig(tokenProvider, redisTemplate, jwtExceptionFilter)); return http.build(); } diff --git a/src/main/java/org/sophy/sophy/config/auth/common/CustomOAuth2UserService.java b/src/main/java/org/sophy/sophy/config/auth/common/CustomOAuth2UserService.java new file mode 100644 index 0000000..12b6970 --- /dev/null +++ b/src/main/java/org/sophy/sophy/config/auth/common/CustomOAuth2UserService.java @@ -0,0 +1,77 @@ +package org.sophy.sophy.config.auth.common; + +import java.util.Collections; +import lombok.RequiredArgsConstructor; +import org.sophy.sophy.config.auth.CustomOAuth2User; +import org.sophy.sophy.config.auth.dto.OAuthAttributes; +import org.sophy.sophy.domain.Member; +import org.sophy.sophy.infrastructure.MemberRepository; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CustomOAuth2UserService implements OAuth2UserService { + + private final MemberRepository memberRepository; + + /** + * DefaultOAuth2UserService 객체를 생성하여, loadUser(userRequest)를 통해 DefaultOAuth2User 객체를 생성 후 반환 + * DefaultOAuth2UserService의 loadUser()는 소셜 로그인 API의 사용자 정보 제공 URI로 요청을 보내서 + * 사용자 정보를 얻은 후, 이를 통해 DefaultOAuth2User 객체를 생성 후 반환한다. + * 결과적으로, OAuth2User는 OAuth 서비스에서 가져온 유저 정보를 담고 있는 유저 + */ + @Override + public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { + OAuth2UserService delegate = new DefaultOAuth2UserService(); + OAuth2User oAuth2User = delegate.loadUser(userRequest); // OAuth2 정보를 가져옵니다. + + //현재 로그인 진행 중인 서비스를 구분하는 코드 (구글 or 네이버 or 카카오 ...) + String registrationId = userRequest.getClientRegistration().getRegistrationId(); + //OAuth2 로그인 진행 시 키가되는 필드 값, Primary Key와 같은 의미 + String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails() + .getUserInfoEndpoint().getUserNameAttributeName(); + + //OAuth2UserService를 통해 가져온 OAuth2User의 attribute를 담을 클래스 + // 소셜 로그인에서 API가 제공하는 userInfo의 Json 값(유저 정보들) + OAuthAttributes attributes = OAuthAttributes.of(registrationId, userNameAttributeName, + oAuth2User.getAttributes()); + + Member member = getMember(attributes); + + return new CustomOAuth2User( + Collections.singleton(new SimpleGrantedAuthority(member.getAuthority().getKey())), + oAuth2User.getAttributes(), + attributes.getNameAttributeKey(), + member.getEmail(), //user 로드할 때 현재 member의 email 저장 + member.getAuthority() + ); + } + + private Member getMember(OAuthAttributes attributes) { + Member findMember = memberRepository.findBySocialId( + attributes.getOAuth2UserInfo().getId()).orElse(null); + + if (findMember == null) { + return saveMember(attributes); + } + return findMember; + } + + /** + * 이미 존재하는 회원이라면 이름과 프로필이미지를 업데이트해줍니다. + * 처음 가입하는 회원이라면 Member 테이블을 생성합니다. (소셜 회원가입) + **/ + private Member saveMember(OAuthAttributes attributes) { + //기존 유저도 이메일 인증을 했기에, 둘이 이메일이 같으면 같은 유저임 + // update는 기존 유저의 소셜 ID 컬럼에 값을 추가하는 것 정도만 있으면 될듯 + Member member = attributes.toEntity(attributes.getOAuth2UserInfo()); + return memberRepository.save(member); + } + +} diff --git a/src/main/java/org/sophy/sophy/service/CustomUserDetailsService.java b/src/main/java/org/sophy/sophy/config/auth/common/CustomUserDetailsService.java similarity index 97% rename from src/main/java/org/sophy/sophy/service/CustomUserDetailsService.java rename to src/main/java/org/sophy/sophy/config/auth/common/CustomUserDetailsService.java index 3d0a83a..24dd79a 100644 --- a/src/main/java/org/sophy/sophy/service/CustomUserDetailsService.java +++ b/src/main/java/org/sophy/sophy/config/auth/common/CustomUserDetailsService.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.service; +package org.sophy.sophy.config.auth.common; import lombok.RequiredArgsConstructor; import org.sophy.sophy.domain.Member; diff --git a/src/main/java/org/sophy/sophy/config/auth/common/OAuth2LoginFailureHandler.java b/src/main/java/org/sophy/sophy/config/auth/common/OAuth2LoginFailureHandler.java new file mode 100644 index 0000000..5b9ff25 --- /dev/null +++ b/src/main/java/org/sophy/sophy/config/auth/common/OAuth2LoginFailureHandler.java @@ -0,0 +1,23 @@ +package org.sophy.sophy.config.auth.common; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class OAuth2LoginFailureHandler implements AuthenticationFailureHandler { + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, + AuthenticationException exception) throws IOException, ServletException { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + response.getWriter().write("소셜 로그인이 실패하였습니다."); + log.error("소셜 로그인에 실패했습니다. 에러메세지 : {}", exception.getMessage()); + } +} diff --git a/src/main/java/org/sophy/sophy/config/auth/common/OAuth2LoginSuccessHandler.java b/src/main/java/org/sophy/sophy/config/auth/common/OAuth2LoginSuccessHandler.java new file mode 100644 index 0000000..88553dc --- /dev/null +++ b/src/main/java/org/sophy/sophy/config/auth/common/OAuth2LoginSuccessHandler.java @@ -0,0 +1,67 @@ +package org.sophy.sophy.config.auth.common; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.sophy.sophy.common.dto.ApiResponseDto; +import org.sophy.sophy.config.auth.CustomOAuth2User; +import org.sophy.sophy.controller.dto.response.TokenDto; +import org.sophy.sophy.domain.enumerate.Authority; +import org.sophy.sophy.exception.SuccessStatus; +import org.sophy.sophy.jwt.TokenProvider; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Component +@RequiredArgsConstructor +public class OAuth2LoginSuccessHandler implements AuthenticationSuccessHandler { + + private final TokenProvider tokenProvider; + private final RedisTemplate redisTemplate; + private final ObjectMapper mapper = new ObjectMapper(); + + /** + * 현재 로직 + * 1. 로그인 성공 but 처음 온 회원 + * 2. access token주고 회원 가입 페이지로 리다이렉트 + * 3. post를 보내면 token의 이메일로 Member 찾기 + * 4. Request Dto의 값들로 Member 값 변경 (더티체킹으로 트랜잭션이 끝날 때 자동으로 업뎃 쿼리 나감) + * 5. 소셜 로그인 회원가입 끝~~ + */ + @Override + @Transactional + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + CustomOAuth2User customOAuth2User = (CustomOAuth2User) authentication.getPrincipal(); + + // User의 Role이 GUEST일 경우 처음 요청한 회원이므로 회원가입 페이지로 리다이렉트 + if(customOAuth2User.getAuthority() == Authority.GUEST) { + String accessToken = tokenProvider.generateAccessToken(authentication); + response.addHeader("Authorization", "Bearer " + accessToken); + response.sendRedirect("/test"); // 프론트의 회원가입 추가 정보 입력 폼으로 리다이렉트 (추가 컨트롤러를 만들고 거기서 post 해야지 User로 바뀜) + } else { + TokenDto tokenDto = tokenProvider.generateTokenDto(authentication); + redisTemplate.opsForValue().set("RT:" + authentication.getName(), + tokenDto.getRefreshToken(), + tokenProvider.getRefreshTokenExpireTime(), + TimeUnit.MILLISECONDS); + response.setStatus(HttpServletResponse.SC_OK); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().write(mapper.writeValueAsString( + ApiResponseDto.success(SuccessStatus.LOGIN_SUCCESS, + tokenDto) + )); + // 로그인에 성공한 경우 access, refresh 토큰 생성 + } + } +} diff --git a/src/main/java/org/sophy/sophy/config/auth/dto/GoogleOAuth2UserInfo.java b/src/main/java/org/sophy/sophy/config/auth/dto/GoogleOAuth2UserInfo.java new file mode 100644 index 0000000..bacdb18 --- /dev/null +++ b/src/main/java/org/sophy/sophy/config/auth/dto/GoogleOAuth2UserInfo.java @@ -0,0 +1,21 @@ +package org.sophy.sophy.config.auth.dto; + +import java.util.Map; + +public class GoogleOAuth2UserInfo extends OAuth2UserInfo{ + + public GoogleOAuth2UserInfo(Map attributes) { + super(attributes); + } + + @Override + public String getId() { + return (String) attributes.get("sub"); + } + + @Override + public String getNickname() { + return (String) attributes.get("name"); + + } +} diff --git a/src/main/java/org/sophy/sophy/config/auth/dto/KakaoOAuth2UserInfo.java b/src/main/java/org/sophy/sophy/config/auth/dto/KakaoOAuth2UserInfo.java new file mode 100644 index 0000000..aa63b67 --- /dev/null +++ b/src/main/java/org/sophy/sophy/config/auth/dto/KakaoOAuth2UserInfo.java @@ -0,0 +1,27 @@ +package org.sophy.sophy.config.auth.dto; + +import java.util.Map; + +public class KakaoOAuth2UserInfo extends OAuth2UserInfo{ + + public KakaoOAuth2UserInfo(Map attributes) { + super(attributes); + } + + @Override + public String getId() { + return String.valueOf(attributes.get("id")); + } + + @Override + public String getNickname() { + Map account = (Map) attributes.get("kakao_account"); + Map profile = (Map) account.get("profile"); + + if (profile == null) { + return null; + } + + return (String) profile.get("nickname"); + } +} diff --git a/src/main/java/org/sophy/sophy/config/auth/dto/NaverOAuth2UserInfo.java b/src/main/java/org/sophy/sophy/config/auth/dto/NaverOAuth2UserInfo.java new file mode 100644 index 0000000..125978a --- /dev/null +++ b/src/main/java/org/sophy/sophy/config/auth/dto/NaverOAuth2UserInfo.java @@ -0,0 +1,31 @@ +package org.sophy.sophy.config.auth.dto; + +import java.util.Map; + +public class NaverOAuth2UserInfo extends OAuth2UserInfo{ + + public NaverOAuth2UserInfo(Map attributes) { + super(attributes); + } + + @Override + public String getId() { + Map response = (Map) attributes.get("response"); + + if (response == null) { + return null; + } + return (String) response.get("id"); + } + + @Override + public String getNickname() { + Map response = (Map) attributes.get("response"); + + if (response == null) { + return null; + } + + return (String) response.get("nickname"); + } +} diff --git a/src/main/java/org/sophy/sophy/config/auth/dto/OAuth2UserInfo.java b/src/main/java/org/sophy/sophy/config/auth/dto/OAuth2UserInfo.java new file mode 100644 index 0000000..682bda6 --- /dev/null +++ b/src/main/java/org/sophy/sophy/config/auth/dto/OAuth2UserInfo.java @@ -0,0 +1,17 @@ +package org.sophy.sophy.config.auth.dto; + +import java.util.Map; + +public abstract class OAuth2UserInfo { + + protected Map attributes; + + public OAuth2UserInfo(Map attributes) { + this.attributes = attributes; + } + + public abstract String getId(); //소셜 식별 값 : 구글 - "sub", 카카오 - "id", 네이버 - "id" + + public abstract String getNickname(); + +} diff --git a/src/main/java/org/sophy/sophy/config/auth/dto/OAuthAttributes.java b/src/main/java/org/sophy/sophy/config/auth/dto/OAuthAttributes.java new file mode 100644 index 0000000..3c41b8e --- /dev/null +++ b/src/main/java/org/sophy/sophy/config/auth/dto/OAuthAttributes.java @@ -0,0 +1,58 @@ +package org.sophy.sophy.config.auth.dto; + +import java.util.Map; +import java.util.UUID; +import lombok.Builder; +import lombok.Getter; +import org.sophy.sophy.domain.Member; +import org.sophy.sophy.domain.enumerate.Authority; + +@Getter +@Builder +public class OAuthAttributes { + private String nameAttributeKey; + private OAuth2UserInfo oAuth2UserInfo; + + public static OAuthAttributes of(String registrationId, String userNameAttributeName, + Map attributes) { + if ("naver".equals(registrationId)) { + return ofNaver(userNameAttributeName, attributes); + } else if ("kakao".equals(registrationId)) { + return ofKakao(userNameAttributeName, attributes); + } + return ofGoogle(userNameAttributeName, attributes); + } + + private static OAuthAttributes ofGoogle(String userNameAttributeName, + Map attributes) { + return OAuthAttributes.builder() + .nameAttributeKey(userNameAttributeName) + .oAuth2UserInfo(new GoogleOAuth2UserInfo(attributes)) + .build(); + } + + private static OAuthAttributes ofNaver(String userNameAttributeName, + Map attributes) { + return OAuthAttributes.builder() + .nameAttributeKey(userNameAttributeName) + .oAuth2UserInfo(new NaverOAuth2UserInfo(attributes)) + .build(); + } + + private static OAuthAttributes ofKakao(String userNameAttributeName, + Map attributes) { + return OAuthAttributes.builder() + .nameAttributeKey(userNameAttributeName) + .oAuth2UserInfo(new KakaoOAuth2UserInfo(attributes)) + .build(); + } + + public Member toEntity(OAuth2UserInfo oAuth2UserInfo) { + return Member.builder() + .name(oAuth2UserInfo.getNickname()) + .socialId(oAuth2UserInfo.getId()) + .email(UUID.randomUUID() + "@socialUser.com") + .authority(Authority.GUEST) + .build(); + } +} diff --git a/src/main/java/org/sophy/sophy/controller/AuthController.java b/src/main/java/org/sophy/sophy/controller/AuthController.java index 49f9749..a09560d 100644 --- a/src/main/java/org/sophy/sophy/controller/AuthController.java +++ b/src/main/java/org/sophy/sophy/controller/AuthController.java @@ -16,8 +16,8 @@ import org.sophy.sophy.controller.dto.response.TokenDto; import org.sophy.sophy.exception.SuccessStatus; import org.sophy.sophy.jwt.TokenProvider; -import org.sophy.sophy.service.AuthService; -import org.sophy.sophy.service.EmailService; +import org.sophy.sophy.service.common.AuthService; +import org.sophy.sophy.service.common.EmailService; import org.springframework.http.HttpStatus; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; @@ -42,11 +42,23 @@ public class AuthController { @ResponseStatus(HttpStatus.CREATED) @Operation(summary = "회원가입") public ApiResponseDto signup( - @RequestBody @Valid MemberRequestDto memberRequestDto) { + @RequestBody @Valid MemberRequestDto memberRequestDto){ return ApiResponseDto.success(SuccessStatus.SIGNUP_SUCCESS, authService.signup(memberRequestDto)); } + @PostMapping("/signup/oauth") + @ResponseStatus(HttpStatus.CREATED) + @SecurityRequirement(name = "JWT Auth") + @Operation(summary = "소셜 로그인 회원가입") + public ApiResponseDto signup( + @RequestBody @Valid MemberRequestDto memberRequestDto, + @Parameter(hidden = true) @AuthenticationPrincipal User user){ + //여기서는 user.getUsername() 의 값이 social ID 값 + return ApiResponseDto.success(SuccessStatus.SIGNUP_SUCCESS, + authService.signup(memberRequestDto, user.getUsername())); + } + @PostMapping("/login") @Operation(summary = "로그인") public ApiResponseDto login( @@ -59,14 +71,10 @@ public ApiResponseDto login( @Operation(summary = "로그아웃") @SecurityRequirement(name = "JWT Auth") public ApiResponseDto logout(@Parameter(hidden = true) HttpServletRequest request) { - /** - * HttpServletRequest나 HttpServletResponse 객체가 Service 계층으로 넘어가는 것은 좋지 않다. - * request, response는 컨트롤러 계층에서 사용되는 객체이며, Service 계층이 request와 response를 알 필요가 없다. - */ String accessToken = tokenProvider.resolveAccessToken(request); return ApiResponseDto.success(SuccessStatus.LOGOUT_SUCCESS, authService.logout(accessToken)); - } + } // HttpServletRequest 나 HttpServletResponse 객체가 Service 계층으로 넘어가는 것은 좋지 않다. @PostMapping("/reissue") @Operation(summary = "액세스 토큰 재발행") diff --git a/src/main/java/org/sophy/sophy/controller/AuthorController.java b/src/main/java/org/sophy/sophy/controller/AuthorController.java index 51703b2..fe6b074 100644 --- a/src/main/java/org/sophy/sophy/controller/AuthorController.java +++ b/src/main/java/org/sophy/sophy/controller/AuthorController.java @@ -13,8 +13,8 @@ import org.sophy.sophy.domain.dto.booktalk.response.BooktalkDeleteResponseDto; import org.sophy.sophy.domain.dto.mypage.MyPageBooktalkDto; import org.sophy.sophy.exception.SuccessStatus; -import org.sophy.sophy.service.api.BooktalkService; -import org.sophy.sophy.service.api.MemberService; +import org.sophy.sophy.service.BooktalkService; +import org.sophy.sophy.service.MemberService; import org.springframework.http.HttpStatus; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; diff --git a/src/main/java/org/sophy/sophy/controller/BooktalkController.java b/src/main/java/org/sophy/sophy/controller/BooktalkController.java index 6c75f0c..da12c60 100644 --- a/src/main/java/org/sophy/sophy/controller/BooktalkController.java +++ b/src/main/java/org/sophy/sophy/controller/BooktalkController.java @@ -11,7 +11,7 @@ import org.sophy.sophy.domain.dto.booktalk.response.BooktalkResponseDto; import org.sophy.sophy.domain.enumerate.City; import org.sophy.sophy.exception.SuccessStatus; -import org.sophy.sophy.service.api.BooktalkService; +import org.sophy.sophy.service.BooktalkService; import org.springframework.http.HttpStatus; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; diff --git a/src/main/java/org/sophy/sophy/controller/HomeController.java b/src/main/java/org/sophy/sophy/controller/HomeController.java index f83dc3b..88345fc 100644 --- a/src/main/java/org/sophy/sophy/controller/HomeController.java +++ b/src/main/java/org/sophy/sophy/controller/HomeController.java @@ -8,7 +8,7 @@ import org.sophy.sophy.common.dto.ApiResponseDto; import org.sophy.sophy.domain.dto.HomeResponseDto; import org.sophy.sophy.exception.SuccessStatus; -import org.sophy.sophy.service.api.HomeService; +import org.sophy.sophy.service.HomeService; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; import org.springframework.web.bind.annotation.GetMapping; diff --git a/src/main/java/org/sophy/sophy/controller/MemberController.java b/src/main/java/org/sophy/sophy/controller/MemberController.java index 0fb0706..45f7d97 100644 --- a/src/main/java/org/sophy/sophy/controller/MemberController.java +++ b/src/main/java/org/sophy/sophy/controller/MemberController.java @@ -10,7 +10,7 @@ import org.sophy.sophy.domain.dto.mypage.MyPageDto; import org.sophy.sophy.domain.dto.mypage.MyInfoDto; import org.sophy.sophy.exception.SuccessStatus; -import org.sophy.sophy.service.api.MemberService; +import org.sophy.sophy.service.MemberService; import org.springframework.http.HttpStatus; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; @@ -41,6 +41,7 @@ public ApiResponseDto getMyPage( @Operation(summary = "내 정보 조회") public ApiResponseDto getInfo( @Parameter(hidden = true) @AuthenticationPrincipal User user) { + System.out.println(user.toString()); return ApiResponseDto.success(SuccessStatus.GET_MYPAGE_SUCCESS, memberService.getMyInfo(user.getUsername())); } diff --git a/src/main/java/org/sophy/sophy/controller/OperatorController.java b/src/main/java/org/sophy/sophy/controller/OperatorController.java index 4f533f6..2008421 100644 --- a/src/main/java/org/sophy/sophy/controller/OperatorController.java +++ b/src/main/java/org/sophy/sophy/controller/OperatorController.java @@ -6,7 +6,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.sophy.sophy.domain.Booktalk; -import org.sophy.sophy.service.api.OperatorService; +import org.sophy.sophy.service.OperatorService; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/org/sophy/sophy/controller/PlaceController.java b/src/main/java/org/sophy/sophy/controller/PlaceController.java index cf1eb33..62c61dd 100644 --- a/src/main/java/org/sophy/sophy/controller/PlaceController.java +++ b/src/main/java/org/sophy/sophy/controller/PlaceController.java @@ -10,7 +10,7 @@ import org.sophy.sophy.domain.dto.place.request.PlaceRequestDto; import org.sophy.sophy.domain.enumerate.City; import org.sophy.sophy.exception.SuccessStatus; -import org.sophy.sophy.service.api.PlaceService; +import org.sophy.sophy.service.PlaceService; import org.springframework.http.HttpStatus; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; diff --git a/src/main/java/org/sophy/sophy/controller/SophyStoryController.java b/src/main/java/org/sophy/sophy/controller/SophyStoryController.java index fb8bbfb..9257a4e 100644 --- a/src/main/java/org/sophy/sophy/controller/SophyStoryController.java +++ b/src/main/java/org/sophy/sophy/controller/SophyStoryController.java @@ -3,12 +3,13 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import javax.validation.Valid; import lombok.RequiredArgsConstructor; import org.sophy.sophy.common.dto.ApiResponseDto; import org.sophy.sophy.domain.dto.SophyStoryDto; import org.sophy.sophy.domain.dto.SophyStoryRequestDto; import org.sophy.sophy.exception.SuccessStatus; -import org.sophy.sophy.service.api.SophyStoryService; +import org.sophy.sophy.service.SophyStoryService; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; import org.springframework.web.bind.annotation.*; @@ -27,7 +28,7 @@ public class SophyStoryController { @GetMapping //소피스토리 연, 월로 조회 public ApiResponseDto> geyMySophyStory( @Parameter(hidden = true) @AuthenticationPrincipal User user, - @RequestBody SophyStoryRequestDto sophyStoryRequestDto) { + @RequestBody @Valid SophyStoryRequestDto sophyStoryRequestDto) { return ApiResponseDto.success(SuccessStatus.GET_SOPHY_STORY_SUCCESS, sophyStoryService.getMySophyStory(user.getUsername(), sophyStoryRequestDto)); } diff --git a/src/main/java/org/sophy/sophy/controller/dto/request/MemberLoginRequestDto.java b/src/main/java/org/sophy/sophy/controller/dto/request/MemberLoginRequestDto.java index e999c75..0cb5641 100644 --- a/src/main/java/org/sophy/sophy/controller/dto/request/MemberLoginRequestDto.java +++ b/src/main/java/org/sophy/sophy/controller/dto/request/MemberLoginRequestDto.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -10,6 +11,7 @@ @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor public class MemberLoginRequestDto { @Email(message = "이메일 형식에 맞지 않습니다.") diff --git a/src/main/java/org/sophy/sophy/controller/dto/request/MemberRequestDto.java b/src/main/java/org/sophy/sophy/controller/dto/request/MemberRequestDto.java index d2f0f65..333733c 100644 --- a/src/main/java/org/sophy/sophy/controller/dto/request/MemberRequestDto.java +++ b/src/main/java/org/sophy/sophy/controller/dto/request/MemberRequestDto.java @@ -53,7 +53,7 @@ public Member toMember(PasswordEncoder passwordEncoder) { .password(passwordEncoder.encode(password)) .phoneNum(phoneNum) .marketingAgree(marketingAgree) - .authority(Authority.ROLE_USER) + .authority(Authority.USER) .build(); } diff --git a/src/main/java/org/sophy/sophy/controller/ServerProfileController.java b/src/main/java/org/sophy/sophy/controller/other/ServerProfileController.java similarity index 93% rename from src/main/java/org/sophy/sophy/controller/ServerProfileController.java rename to src/main/java/org/sophy/sophy/controller/other/ServerProfileController.java index fac1c9a..1a694de 100644 --- a/src/main/java/org/sophy/sophy/controller/ServerProfileController.java +++ b/src/main/java/org/sophy/sophy/controller/other/ServerProfileController.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.controller; +package org.sophy.sophy.controller.other; import io.swagger.v3.oas.annotations.Hidden; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/org/sophy/sophy/controller/TestController.java b/src/main/java/org/sophy/sophy/controller/other/TestController.java similarity index 92% rename from src/main/java/org/sophy/sophy/controller/TestController.java rename to src/main/java/org/sophy/sophy/controller/other/TestController.java index f75917b..fc2d8b8 100644 --- a/src/main/java/org/sophy/sophy/controller/TestController.java +++ b/src/main/java/org/sophy/sophy/controller/other/TestController.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.controller; +package org.sophy.sophy.controller.other; import io.swagger.v3.oas.annotations.Hidden; import org.sophy.sophy.common.dto.ApiResponseDto; diff --git a/src/main/java/org/sophy/sophy/domain/Booktalk.java b/src/main/java/org/sophy/sophy/domain/Booktalk.java index 9d77267..0749476 100644 --- a/src/main/java/org/sophy/sophy/domain/Booktalk.java +++ b/src/main/java/org/sophy/sophy/domain/Booktalk.java @@ -15,13 +15,15 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import org.sophy.sophy.domain.common.AuditingEntity; +import org.sophy.sophy.domain.common.ScheduledBooktalk; @Entity @Getter @NoArgsConstructor @SQLDelete(sql = "UPDATE booktalk SET deleted = true WHERE booktalk_id=?") @Where(clause = "deleted=false") -public class Booktalk extends AuditingTimeEntity { +public class Booktalk extends AuditingEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/org/sophy/sophy/domain/CompletedBooktalk.java b/src/main/java/org/sophy/sophy/domain/CompletedBooktalk.java index 428b865..27468b6 100644 --- a/src/main/java/org/sophy/sophy/domain/CompletedBooktalk.java +++ b/src/main/java/org/sophy/sophy/domain/CompletedBooktalk.java @@ -13,6 +13,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.sophy.sophy.domain.enumerate.BookCategory; +import org.sophy.sophy.domain.common.AuditingTimeEntity; @Entity @Getter diff --git a/src/main/java/org/sophy/sophy/domain/Member.java b/src/main/java/org/sophy/sophy/domain/Member.java index 41d3a93..4b161ee 100644 --- a/src/main/java/org/sophy/sophy/domain/Member.java +++ b/src/main/java/org/sophy/sophy/domain/Member.java @@ -7,22 +7,23 @@ import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; import org.sophy.sophy.controller.dto.request.MemberAdditionalInfoDto; +import org.sophy.sophy.controller.dto.request.MemberRequestDto; import org.sophy.sophy.domain.dto.mypage.MyInfoDto; import org.sophy.sophy.domain.enumerate.Authority; import org.sophy.sophy.domain.enumerate.City; import javax.persistence.*; -import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import java.util.ArrayList; import java.util.List; +import org.sophy.sophy.domain.common.AuditingTimeEntity; +import org.springframework.security.crypto.password.PasswordEncoder; @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @SQLDelete(sql = "UPDATE member SET deleted = true WHERE member_id=?") @Where(clause = "deleted=false") -//@DiscriminatorColumn public class Member extends AuditingTimeEntity { @Id @@ -30,16 +31,16 @@ public class Member extends AuditingTimeEntity { @Column(name = "member_id") private Long id; + private String socialId; + @Column(nullable = false) private String name; @Column(nullable = false) private String email; - @Column(nullable = false) private String password; - @NotNull @Pattern(regexp = "^01(?:0|1|[6-9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$", message = "10 ~ 11 자리의 숫자만 입력 가능합니다.") private String phoneNum; @@ -75,13 +76,14 @@ public class Member extends AuditingTimeEntity { @Builder public Member(String name, String email, String password, String phoneNum, - boolean marketingAgree, Authority authority) { + boolean marketingAgree, Authority authority, String socialId) { this.name = name; this.email = email; this.password = password; this.phoneNum = phoneNum; this.marketingAgree = marketingAgree; this.authority = authority; + this.socialId = socialId; this.userBookTalkList = new ArrayList<>(); this.userBookTalkSize = 0; this.completedBookTalkList = new ArrayList<>(); @@ -121,9 +123,13 @@ public void patchMyInfo(MyInfoDto myInfoDto) { this.marketingAgree = myInfoDto.getMarketingAgree(); } -// public void addUserBooktalkandSortByStartDate(MemberBooktalk memberBooktalk) { //이게 merge? -// this.getUserBookTalkList().add(memberBooktalk); -// // 시작날짜순으로 정렬(예정된 북토크) -// this.getUserBookTalkList().sort(Comparator.comparing(o -> o.getBooktalk().getStartDate())); -// } + public Member socialSignUp(PasswordEncoder passwordEncoder, MemberRequestDto memberRequestDto) { + this.email = memberRequestDto.getEmail(); + this.name = memberRequestDto.getName(); + this.password = passwordEncoder.encode(memberRequestDto.getPassword()); + this.phoneNum = memberRequestDto.getPhoneNum(); + this.marketingAgree = memberRequestDto.getMarketingAgree(); + this.authority = Authority.USER; + return this; + } } diff --git a/src/main/java/org/sophy/sophy/domain/MemberBooktalk.java b/src/main/java/org/sophy/sophy/domain/MemberBooktalk.java index b08835e..24c467b 100644 --- a/src/main/java/org/sophy/sophy/domain/MemberBooktalk.java +++ b/src/main/java/org/sophy/sophy/domain/MemberBooktalk.java @@ -6,11 +6,12 @@ import lombok.NoArgsConstructor; import javax.persistence.*; +import org.sophy.sophy.domain.common.AuditingEntity; @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class MemberBooktalk extends AuditingTimeEntity { +public class MemberBooktalk extends AuditingEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/org/sophy/sophy/domain/Place.java b/src/main/java/org/sophy/sophy/domain/Place.java index e1eba1e..df7a9e9 100644 --- a/src/main/java/org/sophy/sophy/domain/Place.java +++ b/src/main/java/org/sophy/sophy/domain/Place.java @@ -9,11 +9,12 @@ import javax.persistence.*; import java.util.ArrayList; import java.util.List; +import org.sophy.sophy.domain.common.AuditingEntity; @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Place extends AuditingTimeEntity { +public class Place extends AuditingEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/org/sophy/sophy/domain/common/AuditingEntity.java b/src/main/java/org/sophy/sophy/domain/common/AuditingEntity.java new file mode 100644 index 0000000..847ca9f --- /dev/null +++ b/src/main/java/org/sophy/sophy/domain/common/AuditingEntity.java @@ -0,0 +1,22 @@ +package org.sophy.sophy.domain.common; + +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import lombok.Getter; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +@Getter +@EntityListeners(AuditingEntityListener.class) +@MappedSuperclass +public class AuditingEntity extends AuditingTimeEntity { + + @CreatedBy + @Column(updatable = false) + private String createdBy; + + @LastModifiedBy + private String lastModifiedBy; +} diff --git a/src/main/java/org/sophy/sophy/domain/AuditingTimeEntity.java b/src/main/java/org/sophy/sophy/domain/common/AuditingTimeEntity.java similarity index 84% rename from src/main/java/org/sophy/sophy/domain/AuditingTimeEntity.java rename to src/main/java/org/sophy/sophy/domain/common/AuditingTimeEntity.java index d7e6106..0e91037 100644 --- a/src/main/java/org/sophy/sophy/domain/AuditingTimeEntity.java +++ b/src/main/java/org/sophy/sophy/domain/common/AuditingTimeEntity.java @@ -1,5 +1,6 @@ -package org.sophy.sophy.domain; +package org.sophy.sophy.domain.common; +import javax.persistence.Column; import lombok.Getter; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; @@ -15,6 +16,7 @@ public class AuditingTimeEntity { @CreatedDate + @Column(updatable = false) private LocalDateTime createAt; @LastModifiedDate diff --git a/src/main/java/org/sophy/sophy/domain/ScheduledBooktalk.java b/src/main/java/org/sophy/sophy/domain/common/ScheduledBooktalk.java similarity index 88% rename from src/main/java/org/sophy/sophy/domain/ScheduledBooktalk.java rename to src/main/java/org/sophy/sophy/domain/common/ScheduledBooktalk.java index 186f10e..0ad07fd 100644 --- a/src/main/java/org/sophy/sophy/domain/ScheduledBooktalk.java +++ b/src/main/java/org/sophy/sophy/domain/common/ScheduledBooktalk.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.domain; +package org.sophy.sophy.domain.common; import lombok.Getter; import lombok.NoArgsConstructor; @@ -6,6 +6,7 @@ import javax.persistence.*; import java.util.ArrayList; import java.util.List; +import org.sophy.sophy.domain.Booktalk; @Entity @Getter diff --git a/src/main/java/org/sophy/sophy/domain/ScheduledBooktalkConverter.java b/src/main/java/org/sophy/sophy/domain/common/ScheduledBooktalkConverter.java similarity index 91% rename from src/main/java/org/sophy/sophy/domain/ScheduledBooktalkConverter.java rename to src/main/java/org/sophy/sophy/domain/common/ScheduledBooktalkConverter.java index a878020..c6e9d01 100644 --- a/src/main/java/org/sophy/sophy/domain/ScheduledBooktalkConverter.java +++ b/src/main/java/org/sophy/sophy/domain/common/ScheduledBooktalkConverter.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.domain; +package org.sophy.sophy.domain.common; import lombok.Getter; import lombok.NoArgsConstructor; @@ -6,6 +6,7 @@ import javax.persistence.*; import java.util.ArrayList; import java.util.List; +import org.sophy.sophy.domain.Booktalk; @Entity @Getter diff --git a/src/main/java/org/sophy/sophy/domain/dto/CityRequestDto.java b/src/main/java/org/sophy/sophy/domain/dto/CityRequestDto.java index 5de1b31..c3481b9 100644 --- a/src/main/java/org/sophy/sophy/domain/dto/CityRequestDto.java +++ b/src/main/java/org/sophy/sophy/domain/dto/CityRequestDto.java @@ -8,6 +8,6 @@ @Getter public class CityRequestDto { - @NotNull + @NotNull(message = "유효하지 않은 지역입니다.") private City city; } diff --git a/src/main/java/org/sophy/sophy/domain/dto/booktalk/BooktalkUpdateDto.java b/src/main/java/org/sophy/sophy/domain/dto/booktalk/BooktalkUpdateDto.java index c3c726b..c681e45 100644 --- a/src/main/java/org/sophy/sophy/domain/dto/booktalk/BooktalkUpdateDto.java +++ b/src/main/java/org/sophy/sophy/domain/dto/booktalk/BooktalkUpdateDto.java @@ -14,35 +14,35 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) public class BooktalkUpdateDto { - @NotNull + @NotNull(message = "유효하지 않은 공간 ID입니다.") @Schema(description = "장소 Id", example = "1") private Long placeId; private String booktalkImageUrl; - @NotBlank + @NotBlank(message = "유효하지 않은 북토크 제목입니다.") @Schema(description = "북토크 이름", example = "소나기") private String title; - @NotNull + @NotNull(message = "유효하지 않은 책 분야입니다.") @Schema(description = "책 분야", example = "LITERATURE") private BookCategory bookCategory; - @NotNull + @NotNull(message = "유효하지 않은 책 ID입니다.") @Schema(description = "책 Id", example = "1") private Long bookId; - @NotNull + @NotNull(message = "유효하지 않은 시작 날짜입니다.") @Schema(description = "북토크 시작시간", example = "2023-08-12 15:00:00") private LocalDateTime startDate; //TODO 시작 시간은 오늘날짜 이전은 안되도록? - @NotNull + @NotNull(message = "유효하지 않은 종료 날짜입니다.") @Schema(description = "북토크 종료시간", example = "2023-08-12 17:00:00") private LocalDateTime endDate; - @NotNull + @NotNull(message = "유효하지 않은 북토크 참가 인원입니다.") @Schema(description = "북토크 참가 인원", example = "8") private Integer participant; - @NotNull + @NotNull(message = "유효하지 않은 북토크 참가비입니다.") @Schema(description = "북토크 참가 비", example = "1000") private Integer participationFee; - @NotNull + @NotNull(message = "유효하지 않은 북토크 사전 준비 사항입니다.") @Schema(description = "북토크 사전 준비 사항", example = "PRE_READING") private PreliminaryInfo preliminaryInfo; - @NotBlank + @NotBlank(message = "유효하지 않은 북토크 상세 설명입니다.") @Schema(description = "북토크 상세 설명", example = "밖에 비온다 주륵주륵") private String description; } diff --git a/src/main/java/org/sophy/sophy/domain/dto/booktalk/request/BooktalkParticipationRequestDto.java b/src/main/java/org/sophy/sophy/domain/dto/booktalk/request/BooktalkParticipationRequestDto.java index 9f3d50b..821c56b 100644 --- a/src/main/java/org/sophy/sophy/domain/dto/booktalk/request/BooktalkParticipationRequestDto.java +++ b/src/main/java/org/sophy/sophy/domain/dto/booktalk/request/BooktalkParticipationRequestDto.java @@ -10,8 +10,8 @@ @Getter public class BooktalkParticipationRequestDto { - @NotNull - @Schema(description = "북토크 Id", example = "1") + @NotNull(message = "유효하지 않은 북토크 ID입니다.") + @Schema(description = "북토크 Id", example = "5") private Long booktalkId; public MemberBooktalk toMemberBooktalk(Booktalk booktalk, Member member) { diff --git a/src/main/java/org/sophy/sophy/domain/dto/booktalk/request/BooktalkRequestDto.java b/src/main/java/org/sophy/sophy/domain/dto/booktalk/request/BooktalkRequestDto.java index 1f0e7fa..81a3daf 100644 --- a/src/main/java/org/sophy/sophy/domain/dto/booktalk/request/BooktalkRequestDto.java +++ b/src/main/java/org/sophy/sophy/domain/dto/booktalk/request/BooktalkRequestDto.java @@ -20,36 +20,36 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) public class BooktalkRequestDto { - @NotNull + @NotNull(message = "유효하지 않은 공간 ID입니다.") @Schema(description = "장소 Id", example = "1") private Long placeId; private MultipartFile booktalkImage; - @NotBlank + @NotBlank(message = "유효하지 않은 북토크 제목입니다.") @Schema(description = "북토크 이름", example = "소나기") private String title; - @NotNull + @NotNull(message = "유효하지 않은 책 분야입니다.") @Schema(description = "책 분야", example = "LITERATURE") private BookCategory bookCategory; - @NotNull + @NotNull(message = "유효하지 않은 책 ID입니다.") @Schema(description = "책 Id", example = "1") private Long bookId; - @NotNull + @NotNull(message = "유효하지 않은 시작 날짜입니다.") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm") private LocalDateTime startDate; - @NotNull + @NotNull(message = "유효하지 않은 종료 날짜입니다.") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm") private LocalDateTime endDate; - @NotNull + @NotNull(message = "유효하지 않은 북토크 참가 인원입니다.") @Schema(description = "북토크 참가 인원", example = "8") private Integer participant; - @NotNull - @Schema(description = "북토크 참가 비", example = "1000") + @NotNull(message = "유효하지 않은 북토크 참가비입니다.") + @Schema(description = "북토크 참가비", example = "1000") private Integer participationFee; - @NotNull + @NotNull(message = "유효하지 않은 북토크 사전 준비 사항입니다.") @Schema(description = "북토크 사전 준비 사항", example = "PRE_READING") private PreliminaryInfo preliminaryInfo; - @NotBlank + @NotBlank(message = "유효하지 않은 북토크 상세 설명입니다.") @Schema(description = "북토크 상세 설명", example = "밖에 비온다 주륵주륵") private String description; diff --git a/src/main/java/org/sophy/sophy/domain/dto/place/request/PlaceRequestDto.java b/src/main/java/org/sophy/sophy/domain/dto/place/request/PlaceRequestDto.java index deabdf2..2a4a8b8 100644 --- a/src/main/java/org/sophy/sophy/domain/dto/place/request/PlaceRequestDto.java +++ b/src/main/java/org/sophy/sophy/domain/dto/place/request/PlaceRequestDto.java @@ -1,6 +1,7 @@ package org.sophy.sophy.domain.dto.place.request; import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.Valid; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import lombok.AccessLevel; @@ -15,21 +16,22 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) public class PlaceRequestDto { - @NotNull + @NotNull(message = "유효하지 않은 지역입니다.") @Schema(description = "지역 이름", example = "UIJEONGBU_DONG") private City city; - @NotBlank + @NotBlank(message = "유효하지 않은 공간 이름입니다.") @Schema(description = "공간 이름", example = "나의 공간") private String name; - @NotBlank + @NotBlank(message = "유효하지 않은 주소입니다.") @Schema(description = "공간 주소", example = "경기도 의정부시 의정부동 의정부로 14번길 7") private String address; - @NotNull + @NotNull(message = "유효하지 않은 최대 수용 인원입니다.") @Schema(description = "최대 수용 인원", example = "12") private Integer maximum; + @Valid private MultipartFile placeImage; public Place toPlace(Member member, String placeImageUrl) { diff --git a/src/main/java/org/sophy/sophy/domain/enumerate/Authority.java b/src/main/java/org/sophy/sophy/domain/enumerate/Authority.java index 74ec987..1a98100 100644 --- a/src/main/java/org/sophy/sophy/domain/enumerate/Authority.java +++ b/src/main/java/org/sophy/sophy/domain/enumerate/Authority.java @@ -1,6 +1,19 @@ package org.sophy.sophy.domain.enumerate; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor public enum Authority { - ROLE_ADMIN, ROLE_USER, ROLE_AUTHOR, ROLE_OPERATOR, ROLE_ALL + ADMIN("ROLE_ADMIN", "관리자"), + USER("ROLE_USER", "일반 사용자"), + GUEST("ROLE_GUEST", "비회원"), + AUTHOR("ROLE_AUTHOR", "작가"), + OPERATOR("ROLE_OPERATOR", "공간운영자"), + ALL("ROLE_ALL", "작가 + 공간운영자"); + + private final String key; + private final String title; } diff --git a/src/main/java/org/sophy/sophy/infrastructure/BooktalkRepository.java b/src/main/java/org/sophy/sophy/infrastructure/BooktalkRepository.java index b25870c..4bbdde5 100644 --- a/src/main/java/org/sophy/sophy/infrastructure/BooktalkRepository.java +++ b/src/main/java/org/sophy/sophy/infrastructure/BooktalkRepository.java @@ -30,4 +30,6 @@ default Booktalk getBooktalkById(Long booktalkId) { + " join m.operatorProperty o" + " where m.email = :email") List getWatingBooktalks(@Param("email") String email); + + List findAllByBooktalkStatusOrderByEndDate(BooktalkStatus booktalkStatus); } diff --git a/src/main/java/org/sophy/sophy/infrastructure/MemberRepository.java b/src/main/java/org/sophy/sophy/infrastructure/MemberRepository.java index 6fc9e93..73041ed 100644 --- a/src/main/java/org/sophy/sophy/infrastructure/MemberRepository.java +++ b/src/main/java/org/sophy/sophy/infrastructure/MemberRepository.java @@ -13,6 +13,7 @@ @Transactional public interface MemberRepository extends JpaRepository { Optional findByEmail(String email); + Optional findBySocialId(String socialId); boolean existsByEmail(String email); default Member getMemberByEmail(String email) { diff --git a/src/main/java/org/sophy/sophy/jwt/TokenProvider.java b/src/main/java/org/sophy/sophy/jwt/TokenProvider.java index 1ac60ae..ab1116a 100644 --- a/src/main/java/org/sophy/sophy/jwt/TokenProvider.java +++ b/src/main/java/org/sophy/sophy/jwt/TokenProvider.java @@ -18,6 +18,7 @@ import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; +import org.sophy.sophy.config.auth.CustomOAuth2User; import org.sophy.sophy.controller.dto.response.TokenDto; import org.sophy.sophy.exception.model.SophyJwtException; import org.springframework.beans.factory.annotation.Value; @@ -63,12 +64,23 @@ public TokenDto generateTokenDto(Authentication authentication) { //Access Token 생성 Date accessTokenExpiresIn = new Date(now + ACCESS_TOKEN_EXPIRE_TIME); - String accessToken = Jwts.builder() - .setSubject(authentication.getName()) // payload "sub" : "name" - .claim(AUTHORITIES_KEY, authorities) // payload "auth": "ROLE_USER" - .setExpiration(accessTokenExpiresIn) //payload "exp": 1516239022 (예시) - .signWith(key, SignatureAlgorithm.HS512) - .compact(); + String accessToken; + if(authentication.getClass() == UsernamePasswordAuthenticationToken.class){ + accessToken = Jwts.builder() + .setSubject(authentication.getName()) // payload "sub" : "name" + .claim(AUTHORITIES_KEY, authorities) // payload "auth": "ROLE_USER" + .setExpiration(accessTokenExpiresIn) //payload "exp": 1516239022 (예시) + .signWith(key, SignatureAlgorithm.HS512) + .compact(); + } else { //oauth2 로 로그인 했을 때 토큰 저장 + CustomOAuth2User customOAuth2User = (CustomOAuth2User) authentication.getPrincipal(); + accessToken = Jwts.builder() + .setSubject(customOAuth2User.getEmail()) // payload "sub" : "name" + .claim(AUTHORITIES_KEY, authorities) // payload "auth": "ROLE_USER" + .setExpiration(accessTokenExpiresIn) //payload "exp": 1516239022 (예시) + .signWith(key, SignatureAlgorithm.HS512) + .compact(); + } //Refresh Token 생성 String refreshToken = Jwts.builder() @@ -84,6 +96,24 @@ public TokenDto generateTokenDto(Authentication authentication) { .build(); } + public String generateAccessToken(Authentication authentication) { + //권한들 가져오기 + String authorities = authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.joining(",")); + long now = (new Date()).getTime(); + + //Access Token 생성 + Date accessTokenExpiresIn = new Date(now + ACCESS_TOKEN_EXPIRE_TIME); + CustomOAuth2User customOAuth2User = (CustomOAuth2User) authentication.getPrincipal(); + return Jwts.builder() + .setSubject(customOAuth2User.getEmail()) // payload "sub" : "name" + .claim(AUTHORITIES_KEY, authorities) // payload "auth": "ROLE_USER" + .setExpiration(accessTokenExpiresIn) //payload "exp": 1516239022 (예시) + .signWith(key, SignatureAlgorithm.HS512) + .compact(); + } + public Authentication getAuthentication(String accessToken) { //토큰 복호화 Claims claims = parseClaims(accessToken); diff --git a/src/main/java/org/sophy/sophy/service/api/BooktalkService.java b/src/main/java/org/sophy/sophy/service/BooktalkService.java similarity index 91% rename from src/main/java/org/sophy/sophy/service/api/BooktalkService.java rename to src/main/java/org/sophy/sophy/service/BooktalkService.java index 645d513..8df1442 100644 --- a/src/main/java/org/sophy/sophy/service/api/BooktalkService.java +++ b/src/main/java/org/sophy/sophy/service/BooktalkService.java @@ -1,7 +1,5 @@ -package org.sophy.sophy.service.api; +package org.sophy.sophy.service; -import java.util.ArrayList; -import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; @@ -53,7 +51,7 @@ public BooktalkCreateResponseDto createBooktalk(BooktalkRequestDto booktalkReque String email) { Place place = placeRepository.getPlaceById(booktalkRequestDto.getPlaceId()); Member member = memberRepository.getMemberByEmail(email); - if (!member.getAuthority().equals(Authority.ROLE_AUTHOR)) { + if (!member.getAuthority().equals(Authority.AUTHOR)) { throw new ForbiddenException(ErrorStatus.FORBIDDEN_USER_EXCEPTION, ErrorStatus.FORBIDDEN_USER_EXCEPTION.getMessage()); } @@ -122,22 +120,8 @@ public void postBooktalkParticipation( // 마감임박 북토크 조회 public List getBooktalkDeadlineUpcoming() { - List placeList = placeRepository.findAll(); - - List booktalkList = new ArrayList<>(); - placeList.forEach(place -> place.getBooktalkList().forEach(booktalk -> { - // 모집중인 북토크만 추가 - if (booktalk.getBooktalkStatus() == BooktalkStatus.RECRUITING) { - booktalkList.add(BooktalkDeadlineUpcomingDto.of(booktalk)); - } - } - )); - - // 마감 임박순으로 정렬 - booktalkList.sort(Comparator.comparing(BooktalkDeadlineUpcomingDto::getEndDate)); - - return booktalkList; - + return booktalkRepository.findAllByBooktalkStatusOrderByEndDate(BooktalkStatus.RECRUITING) + .stream().map(BooktalkDeadlineUpcomingDto::of).collect(Collectors.toList()); } //지역으로 북토크 조회 diff --git a/src/main/java/org/sophy/sophy/service/api/HomeService.java b/src/main/java/org/sophy/sophy/service/HomeService.java similarity index 94% rename from src/main/java/org/sophy/sophy/service/api/HomeService.java rename to src/main/java/org/sophy/sophy/service/HomeService.java index 0d95f04..e40d885 100644 --- a/src/main/java/org/sophy/sophy/service/api/HomeService.java +++ b/src/main/java/org/sophy/sophy/service/HomeService.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.service.api; +package org.sophy.sophy.service; import java.time.LocalDate; import java.time.LocalDateTime; @@ -33,7 +33,7 @@ public HomeResponseDto getHome(String email) { Integer booktalkCount = member.getUserBookTalkSize(); List booktalkDeadlineUpcoming = booktalkService.getBooktalkDeadlineUpcoming(); - if (member.getAuthority().equals(Authority.ROLE_AUTHOR)) { //작가냐 아니냐에 따라 홈 화면 분리 + if (member.getAuthority().equals(Authority.AUTHOR)) { //작가냐 아니냐에 따라 홈 화면 분리 List cityRank = getCityRank(); return HomeResponseDto.builder() .name(member.getName()) @@ -85,7 +85,6 @@ public List getCityRank() { List> cityLists = rank.entrySet().stream() .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) .collect(Collectors.toList()); - System.out.println(cityLists); List result = new ArrayList<>(); for (Map.Entry entry : cityLists.subList(0, 3)) { diff --git a/src/main/java/org/sophy/sophy/service/api/MemberService.java b/src/main/java/org/sophy/sophy/service/MemberService.java similarity index 97% rename from src/main/java/org/sophy/sophy/service/api/MemberService.java rename to src/main/java/org/sophy/sophy/service/MemberService.java index dae7bb9..9ff5360 100644 --- a/src/main/java/org/sophy/sophy/service/api/MemberService.java +++ b/src/main/java/org/sophy/sophy/service/MemberService.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.service.api; +package org.sophy.sophy.service; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; @@ -33,7 +33,7 @@ public class MemberService { public MyPageDto getMyPage(String email) { Member member = memberRepository.getMemberByEmail(email); //여기에 추가로 member에 있는 userBookTalk 리스트를 시간순으로 정렬해 가장 마감이 임박한 booktalk도 보여줌 - if (member.getAuthority().equals(Authority.ROLE_AUTHOR)) { + if (member.getAuthority().equals(Authority.AUTHOR)) { return MyPageDto.builder() .name(member.getName()) .expectedBookTalkCount(member.getAuthorProperty().getMyBookTalkSize()) diff --git a/src/main/java/org/sophy/sophy/service/api/OperatorService.java b/src/main/java/org/sophy/sophy/service/OperatorService.java similarity index 90% rename from src/main/java/org/sophy/sophy/service/api/OperatorService.java rename to src/main/java/org/sophy/sophy/service/OperatorService.java index 3622329..71f030d 100644 --- a/src/main/java/org/sophy/sophy/service/api/OperatorService.java +++ b/src/main/java/org/sophy/sophy/service/OperatorService.java @@ -1,8 +1,8 @@ -package org.sophy.sophy.service.api; +package org.sophy.sophy.service; import lombok.RequiredArgsConstructor; import org.sophy.sophy.domain.Booktalk; -import org.sophy.sophy.domain.ScheduledBooktalkConverter; +import org.sophy.sophy.domain.common.ScheduledBooktalkConverter; import org.sophy.sophy.domain.enumerate.BooktalkStatus; import org.sophy.sophy.infrastructure.BooktalkRepository; import org.springframework.stereotype.Service; diff --git a/src/main/java/org/sophy/sophy/service/api/PlaceService.java b/src/main/java/org/sophy/sophy/service/PlaceService.java similarity index 95% rename from src/main/java/org/sophy/sophy/service/api/PlaceService.java rename to src/main/java/org/sophy/sophy/service/PlaceService.java index 30b3337..c5c6a52 100644 --- a/src/main/java/org/sophy/sophy/service/api/PlaceService.java +++ b/src/main/java/org/sophy/sophy/service/PlaceService.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.service.api; +package org.sophy.sophy.service; import lombok.RequiredArgsConstructor; import org.sophy.sophy.controller.dto.response.PlaceResponseDto; @@ -30,7 +30,7 @@ public class PlaceService { @Transactional public PlaceResponseDto createPlace(PlaceRequestDto placeRequestDto, String email) { Member member = memberRepository.getMemberByEmail(email); - if (!member.getAuthority().equals(Authority.ROLE_OPERATOR)) { + if (!member.getAuthority().equals(Authority.OPERATOR)) { throw new ForbiddenException(ErrorStatus.FORBIDDEN_USER_EXCEPTION, ErrorStatus.FORBIDDEN_USER_EXCEPTION.getMessage()); } diff --git a/src/main/java/org/sophy/sophy/service/api/SophyStoryService.java b/src/main/java/org/sophy/sophy/service/SophyStoryService.java similarity index 66% rename from src/main/java/org/sophy/sophy/service/api/SophyStoryService.java rename to src/main/java/org/sophy/sophy/service/SophyStoryService.java index 18518c6..a8e4d62 100644 --- a/src/main/java/org/sophy/sophy/service/api/SophyStoryService.java +++ b/src/main/java/org/sophy/sophy/service/SophyStoryService.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.service.api; +package org.sophy.sophy.service; import lombok.RequiredArgsConstructor; import org.sophy.sophy.domain.CompletedBooktalk; @@ -38,15 +38,13 @@ public List getMySophyStory(String email, , LocalTime.of(23, 59, 59))); List sophyStoryDtos = new ArrayList<>(); - completedBooktalkList.forEach(completedBooktalk -> { - sophyStoryDtos.add(SophyStoryDto.builder() - .title(completedBooktalk.getTitle()) - .bookName(completedBooktalk.getBookName()) - .authorName(completedBooktalk.getAuthorName()) - .booktalkDate(completedBooktalk.getBooktalkDate()) - .placeName(completedBooktalk.getPlaceName()) - .build()); - }); + completedBooktalkList.forEach(completedBooktalk -> sophyStoryDtos.add(SophyStoryDto.builder() + .title(completedBooktalk.getTitle()) + .bookName(completedBooktalk.getBookName()) + .authorName(completedBooktalk.getAuthorName()) + .booktalkDate(completedBooktalk.getBooktalkDate()) + .placeName(completedBooktalk.getPlaceName()) + .build())); return sophyStoryDtos; } @@ -56,16 +54,14 @@ public List getMySophyStory(String email) { //모든 소피스토 List completedBooktalkList = member.getCompletedBookTalkList(); List sophyStoryDtos = new ArrayList<>(); - completedBooktalkList.forEach(completedBooktalk -> { - sophyStoryDtos.add(SophyStoryDto.builder() - .title(completedBooktalk.getTitle()) - .bookName(completedBooktalk.getBookName()) - .authorName(completedBooktalk.getAuthorName()) - .booktalkDate(completedBooktalk.getBooktalkDate()) - .placeName(completedBooktalk.getPlaceName()) - .bookCategory(completedBooktalk.getBookCategory()) - .build()); - }); + completedBooktalkList.forEach(completedBooktalk -> sophyStoryDtos.add(SophyStoryDto.builder() + .title(completedBooktalk.getTitle()) + .bookName(completedBooktalk.getBookName()) + .authorName(completedBooktalk.getAuthorName()) + .booktalkDate(completedBooktalk.getBooktalkDate()) + .placeName(completedBooktalk.getPlaceName()) + .bookCategory(completedBooktalk.getBookCategory()) + .build())); return sophyStoryDtos; } } diff --git a/src/main/java/org/sophy/sophy/service/AuthService.java b/src/main/java/org/sophy/sophy/service/common/AuthService.java similarity index 92% rename from src/main/java/org/sophy/sophy/service/AuthService.java rename to src/main/java/org/sophy/sophy/service/common/AuthService.java index 89234e1..35e770c 100644 --- a/src/main/java/org/sophy/sophy/service/AuthService.java +++ b/src/main/java/org/sophy/sophy/service/common/AuthService.java @@ -1,6 +1,7 @@ -package org.sophy.sophy.service; +package org.sophy.sophy.service.common; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.sophy.sophy.controller.dto.request.DuplCheckDto; import org.sophy.sophy.controller.dto.request.MemberLoginRequestDto; import org.sophy.sophy.controller.dto.request.MemberRequestDto; @@ -26,13 +27,14 @@ @Service @RequiredArgsConstructor +@Slf4j public class AuthService { private final AuthenticationManagerBuilder authenticationManagerBuilder; private final MemberRepository memberRepository; private final PasswordEncoder passwordEncoder; private final TokenProvider tokenProvider; - private final RedisTemplate redisTemplate; + private final RedisTemplate redisTemplate; //이메일 중복 체크만 가능한 서비스 하나 추가 @@ -47,6 +49,14 @@ public MemberResponseDto signup(MemberRequestDto memberRequestDto) { return MemberResponseDto.of(memberRepository.save(member)); } + @Transactional + public MemberResponseDto signup(MemberRequestDto memberRequestDto, String email) { + //GUEST를 USER로 바꾸며 회원가입 시키는 로직 + Member member = memberRepository.getMemberByEmail(email); + + return MemberResponseDto.of(member.socialSignUp(passwordEncoder, memberRequestDto)); + } + @Transactional public String duplCheck(DuplCheckDto email) { @@ -115,7 +125,7 @@ public TokenDto reissue(String accessToken, String refreshToken) { // 2. Access Token 에서 Member ID(user email) 가져오기 Authentication authentication = tokenProvider.getAuthentication(accessToken); // 3. 저장소에서 Member ID 를 기반으로 Refresh Token 값 가져옴 - String existRefreshToken = (String) redisTemplate.opsForValue() + String existRefreshToken = redisTemplate.opsForValue() .get("RT:" + authentication.getName()); //로그아웃 되어 Redis에 RefreshToken이 존재하지 않는 경우 처리 diff --git a/src/main/java/org/sophy/sophy/service/EmailService.java b/src/main/java/org/sophy/sophy/service/common/EmailService.java similarity index 92% rename from src/main/java/org/sophy/sophy/service/EmailService.java rename to src/main/java/org/sophy/sophy/service/common/EmailService.java index fd98580..4bf9d90 100644 --- a/src/main/java/org/sophy/sophy/service/EmailService.java +++ b/src/main/java/org/sophy/sophy/service/common/EmailService.java @@ -1,4 +1,4 @@ -package org.sophy.sophy.service; +package org.sophy.sophy.service.common; import java.util.Random; import javax.mail.internet.InternetAddress; @@ -47,7 +47,7 @@ private MimeMessage createMessage(String to)throws Exception{ } public void createKey() { - StringBuffer key = new StringBuffer(); + StringBuilder key = new StringBuilder(); Random rnd = new Random(); for (int i = 0; i < 8; i++) { // 인증코드 8자리 @@ -55,11 +55,11 @@ public void createKey() { switch (index) { case 0: - key.append((char) ((int) (rnd.nextInt(26)) + 97)); + key.append((char) (rnd.nextInt(26) + 97)); // a~z (ex. 1+97=98 => (char)98 = 'b') break; case 1: - key.append((char) ((int) (rnd.nextInt(26)) + 65)); + key.append((char) (rnd.nextInt(26) + 65)); // A~Z break; case 2: diff --git a/src/main/java/org/sophy/sophy/service/SchedulerService.java b/src/main/java/org/sophy/sophy/service/common/SchedulerService.java similarity index 93% rename from src/main/java/org/sophy/sophy/service/SchedulerService.java rename to src/main/java/org/sophy/sophy/service/common/SchedulerService.java index 93ae052..afbab72 100644 --- a/src/main/java/org/sophy/sophy/service/SchedulerService.java +++ b/src/main/java/org/sophy/sophy/service/common/SchedulerService.java @@ -1,14 +1,14 @@ -package org.sophy.sophy.service; +package org.sophy.sophy.service.common; import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; import lombok.RequiredArgsConstructor; import org.sophy.sophy.domain.Booktalk; -import org.sophy.sophy.domain.ScheduledBooktalkConverter; +import org.sophy.sophy.domain.common.ScheduledBooktalkConverter; import org.sophy.sophy.domain.enumerate.BooktalkStatus; import org.sophy.sophy.infrastructure.query.BooktalkQueryRepository; -import org.sophy.sophy.service.api.BooktalkService; +import org.sophy.sophy.service.BooktalkService; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;