diff --git a/backend/build.gradle b/backend/build.gradle index fbee1f0..e66b88a 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '2.6.5' + id 'org.springframework.boot' version '2.6.6' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'org.asciidoctor.convert' version '1.5.8' id 'java' @@ -54,9 +54,18 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.11.2' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.2' + implementation 'org.springframework.boot:spring-boot-starter-log4j2' + +} +configurations { + all*.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + all*.exclude group: 'org.springframework.boot', module: 'logback-classic' } +tasks.named('compileJava') { + inputs.files(tasks.named('processResources')) +} bootRun { mainClassName = 'com.hjjang.backend.BackendApplication' diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/controller/UserAuthController.java b/backend/src/main/java/com/hjjang/backend/domain/user/controller/UserAuthController.java index 677e6fe..1cc3f30 100644 --- a/backend/src/main/java/com/hjjang/backend/domain/user/controller/UserAuthController.java +++ b/backend/src/main/java/com/hjjang/backend/domain/user/controller/UserAuthController.java @@ -1,6 +1,5 @@ package com.hjjang.backend.domain.user.controller; -import com.hjjang.backend.domain.user.dto.LoginRequest; import com.hjjang.backend.domain.user.entity.RoleType; import com.hjjang.backend.domain.user.entity.UserRefreshToken; import com.hjjang.backend.domain.user.repository.UserRefreshTokenRepository; @@ -13,7 +12,9 @@ import com.hjjang.backend.global.util.HeaderUtil; import io.jsonwebtoken.Claims; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -30,16 +31,8 @@ public class UserAuthController { private final UserRefreshTokenRepository userRefreshTokenRepository; private final UserAuthService userAuthService; - private final static long THREE_DAYS_MSEC = 259200000; - private final static String REFRESH_TOKEN = "refresh_token"; - - @PostMapping("/login") - public ApiResponse login(HttpServletRequest request, HttpServletResponse response, - @RequestBody LoginRequest loginRequest - ) { - String token = userAuthService.login(request, response, loginRequest); - return ApiResponse.success("token", token); - } + private static final long THREE_DAYS_MSEC = 259200000; + private static final String REFRESH_TOKEN = "refresh_token"; @GetMapping("/refresh") public ApiResponse refreshToken(HttpServletRequest request, HttpServletResponse response) { @@ -55,31 +48,33 @@ public ApiResponse refreshToken(HttpServletRequest request, HttpServletResponse if (claims == null) { return ApiResponse.notExpiredTokenYet(); } - - String userId = claims.getSubject(); + String providerId = claims.getSubject(); RoleType roleType = RoleType.of(claims.get("role", String.class)); // refresh token String refreshToken = CookieUtil.getCookie(request, REFRESH_TOKEN) - .map(Cookie::getValue) - .orElse((null)); + .map(Cookie::getValue) + .orElse((null)); AuthToken authRefreshToken = tokenProvider.convertAuthToken(refreshToken); if (authRefreshToken.validate()) { return ApiResponse.invalidRefreshToken(); } - // userId, refresh token 으로 DB 확인 - UserRefreshToken userRefreshToken = userRefreshTokenRepository.findByUserIdAndRefreshToken(userId, refreshToken); + // providerId, refresh token 으로 DB 확인 + UserRefreshToken userRefreshToken = userRefreshTokenRepository.findByProviderIdAndRefreshToken(providerId, + refreshToken); if (userRefreshToken == null) { return ApiResponse.invalidRefreshToken(); } Date now = new Date(); AuthToken newAccessToken = tokenProvider.createAuthToken( - userId, roleType.getCode(), new Date(now.getTime() + authProperties.getTokenProperties().getTokenExpireDate()) + providerId, roleType.getCode(), + new Date(now.getTime() + authProperties.getTokenProperties().getTokenExpireDate()) ); - userAuthService.reissueRefreshTokenIfValidTimeleft3days(request, response, userRefreshToken, authRefreshToken, now); + userAuthService.reissueRefreshTokenIfValidTimeleft3days(request, response, userRefreshToken, authRefreshToken, + now); return ApiResponse.success("token", newAccessToken.getToken()); } diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/controller/UserController.java b/backend/src/main/java/com/hjjang/backend/domain/user/controller/UserController.java index 4db0396..3c3d1c0 100644 --- a/backend/src/main/java/com/hjjang/backend/domain/user/controller/UserController.java +++ b/backend/src/main/java/com/hjjang/backend/domain/user/controller/UserController.java @@ -1,27 +1,25 @@ package com.hjjang.backend.domain.user.controller; -import com.hjjang.backend.domain.user.entity.User; -import com.hjjang.backend.domain.user.service.UserService; -import com.hjjang.backend.global.dto.ApiResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.hjjang.backend.domain.user.dto.UserProfileDTO; +import com.hjjang.backend.domain.user.service.UserProfileService; +import com.hjjang.backend.global.dto.ApiResponse; + +import lombok.RequiredArgsConstructor; + @RestController @RequestMapping("/api/v1/users") @RequiredArgsConstructor public class UserController { - private final UserService userService; - - @GetMapping - public ApiResponse getUser() { - org.springframework.security.core.userdetails.User principal = (org.springframework.security.core.userdetails.User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - - User user = userService.getUser(principal.getUsername()); + private final UserProfileService userProfileService; - return ApiResponse.success("user", user); + @GetMapping("/profile") + public ApiResponse getProfile() { + UserProfileDTO userProfile = userProfileService.getUserProfile(); + return ApiResponse.success("userProfile", userProfile); } } diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/dto/UserProfileDTO.java b/backend/src/main/java/com/hjjang/backend/domain/user/dto/UserProfileDTO.java new file mode 100644 index 0000000..f47474c --- /dev/null +++ b/backend/src/main/java/com/hjjang/backend/domain/user/dto/UserProfileDTO.java @@ -0,0 +1,23 @@ +package com.hjjang.backend.domain.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserProfileDTO { + + private String userNickname; + + private String userImageUrl; + + private String userEmail; + + private Long userMannerTemperature; + + private String userUnivName; +} diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/entity/User.java b/backend/src/main/java/com/hjjang/backend/domain/user/entity/User.java index 04992b1..9b2af8d 100644 --- a/backend/src/main/java/com/hjjang/backend/domain/user/entity/User.java +++ b/backend/src/main/java/com/hjjang/backend/domain/user/entity/User.java @@ -11,6 +11,7 @@ import java.time.LocalDateTime; import static javax.persistence.EnumType.STRING; +import static javax.persistence.GenerationType.IDENTITY; import static lombok.AccessLevel.PROTECTED; @Getter @@ -22,8 +23,12 @@ public class User { @Id - @Column(name = "id", nullable = false, length = 128) - private String id; + @GeneratedValue(strategy = IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "provider_id", nullable = false, length = 128) + private String providerId; @Column(name = "nickname", nullable = false, length = 30) private String nickName; @@ -53,8 +58,8 @@ public class User { private RoleType role; @Builder - public User(String id, String nickName, String email, Long mannerTemperature, String imageUrl, Agreement isPushAgree, Long univId, RoleType role) { - this.id = id; + public User(String providerId, String nickName, String email, Long mannerTemperature, String imageUrl, Agreement isPushAgree, Long univId, RoleType role) { + this.providerId = providerId; this.nickName = nickName; this.email = email; this.mannerTemperature = mannerTemperature; diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/entity/UserRefreshToken.java b/backend/src/main/java/com/hjjang/backend/domain/user/entity/UserRefreshToken.java index c442458..77d98e4 100644 --- a/backend/src/main/java/com/hjjang/backend/domain/user/entity/UserRefreshToken.java +++ b/backend/src/main/java/com/hjjang/backend/domain/user/entity/UserRefreshToken.java @@ -1,12 +1,12 @@ package com.hjjang.backend.domain.user.entity; -import com.sun.istack.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.NoArgsConstructor; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.*; +import javax.validation.constraints.NotNull; import static javax.persistence.GenerationType.IDENTITY; import static lombok.AccessLevel.PROTECTED; @@ -22,20 +22,20 @@ public class UserRefreshToken { @Column(name = "id", nullable = false) private Long id; - @Column(name = "user_id", nullable = false, length = 64) - private String userId; + @Column(name = "provider_id", nullable = false, length = 64) + private String providerId; @Column(name = "refresh_token", nullable = false, length = 256) @NotNull private String refreshToken; @Builder - public UserRefreshToken(String userId, String refreshToken) { - this.userId = userId; + public UserRefreshToken(String providerId, String refreshToken) { + this.providerId = providerId; this.refreshToken = refreshToken; } - public void setRefreshToken(String refreshToken) { + public void reissueRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } } diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/exception/UserDoesNotExistException.java b/backend/src/main/java/com/hjjang/backend/domain/user/exception/UserDoesNotExistException.java new file mode 100644 index 0000000..559aac5 --- /dev/null +++ b/backend/src/main/java/com/hjjang/backend/domain/user/exception/UserDoesNotExistException.java @@ -0,0 +1,6 @@ +package com.hjjang.backend.domain.user.exception; + +public class UserDoesNotExistException { + public UserDoesNotExistException() { + } +} diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/repository/UserRefreshTokenRepository.java b/backend/src/main/java/com/hjjang/backend/domain/user/repository/UserRefreshTokenRepository.java index fbbb4e5..81bef94 100644 --- a/backend/src/main/java/com/hjjang/backend/domain/user/repository/UserRefreshTokenRepository.java +++ b/backend/src/main/java/com/hjjang/backend/domain/user/repository/UserRefreshTokenRepository.java @@ -3,8 +3,9 @@ import com.hjjang.backend.domain.user.entity.UserRefreshToken; import org.springframework.data.jpa.repository.JpaRepository; -public interface UserRefreshTokenRepository extends JpaRepository { - UserRefreshToken findByUserId(String userId); - UserRefreshToken findByUserIdAndRefreshToken(String userId, String refreshToken); +public interface UserRefreshTokenRepository extends JpaRepository { + UserRefreshToken findByProviderId(String providerId); + + UserRefreshToken findByProviderIdAndRefreshToken(String providerId, String refreshToken); } diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/repository/UserRepository.java b/backend/src/main/java/com/hjjang/backend/domain/user/repository/UserRepository.java index 89c4ebb..c11a5ad 100644 --- a/backend/src/main/java/com/hjjang/backend/domain/user/repository/UserRepository.java +++ b/backend/src/main/java/com/hjjang/backend/domain/user/repository/UserRepository.java @@ -3,6 +3,10 @@ import com.hjjang.backend.domain.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; -public interface UserRepository extends JpaRepository { - User findUserById(String Id); +import java.util.Optional; + +public interface UserRepository extends JpaRepository { + Optional findUserById(Long id); + + Optional findUserByProviderId(String providerId); } diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/service/UserAuthService.java b/backend/src/main/java/com/hjjang/backend/domain/user/service/UserAuthService.java index 39b5466..29ec82c 100644 --- a/backend/src/main/java/com/hjjang/backend/domain/user/service/UserAuthService.java +++ b/backend/src/main/java/com/hjjang/backend/domain/user/service/UserAuthService.java @@ -1,26 +1,23 @@ package com.hjjang.backend.domain.user.service; -import com.hjjang.backend.domain.user.dto.LoginRequest; -import com.hjjang.backend.domain.user.entity.RoleType; +import static com.hjjang.backend.global.config.security.repository.OAuth2AuthorizationRequestBasedOnCookieRepository.*; + +import java.util.Date; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.stereotype.Service; + import com.hjjang.backend.domain.user.entity.UserRefreshToken; import com.hjjang.backend.domain.user.repository.UserRefreshTokenRepository; import com.hjjang.backend.global.config.properties.AuthProperties; -import com.hjjang.backend.global.config.security.principal.UserPrincipal; import com.hjjang.backend.global.config.security.token.AuthToken; import com.hjjang.backend.global.config.security.token.AuthTokenProvider; import com.hjjang.backend.global.util.CookieUtil; -import lombok.RequiredArgsConstructor; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Service; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Date; -import static com.hjjang.backend.global.config.security.repository.OAuth2AuthorizationRequestBasedOnCookieRepository.REFRESH_TOKEN; +import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Service @@ -32,60 +29,8 @@ public class UserAuthService { private final UserRefreshTokenRepository userRefreshTokenRepository; private final static long THREE_DAYS_MSEC = 259200000; - public String login(HttpServletRequest request, HttpServletResponse response, - LoginRequest loginRequest) { - Authentication authentication = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken( - loginRequest.getId(), - loginRequest.getPassword() - ) - ); - - String userId = loginRequest.getId(); - SecurityContextHolder.getContext().setAuthentication(authentication); - - Date now = new Date(); - AuthToken accessToken = tokenProvider.createAuthToken( - userId, - ((UserPrincipal) authentication.getPrincipal()).getRoleType().getCode(), - new Date(now.getTime() + authProperties.getTokenProperties().getTokenExpireDate()) - ); - - long refreshTokenExpiry = authProperties.getTokenProperties().getRefreshTokenExpiry(); - AuthToken refreshToken = tokenProvider.createAuthToken( - authProperties.getTokenProperties().getTokenSecretKey(), - new Date(now.getTime() + refreshTokenExpiry) - ); - - // userId refresh token 으로 DB 확인 - UserRefreshToken userRefreshToken = userRefreshTokenRepository.findByUserId(userId); - if (userRefreshToken == null) { - // 없는 경우 새로 등록 - userRefreshToken = new UserRefreshToken(userId, refreshToken.getToken()); - userRefreshTokenRepository.saveAndFlush(userRefreshToken); - } else { - // DB에 refresh 토큰 업데이트 - userRefreshToken.setRefreshToken(refreshToken.getToken()); - } - - int cookieMaxAge = (int) refreshTokenExpiry / 60; - CookieUtil.deleteCookie(request, response, REFRESH_TOKEN); - CookieUtil.addCookie(response, REFRESH_TOKEN, refreshToken.getToken(), cookieMaxAge); - - return accessToken.getToken(); - } - - public AuthToken createAuthToken(String userId, RoleType roleType, Date expiredDate) { - Date now = new Date(); - return tokenProvider.createAuthToken( - userId, roleType.getCode(), - new Date(now.getTime() + authProperties.getTokenProperties().getTokenExpireDate()) - ); - } - - public void reissueRefreshTokenIfValidTimeleft3days(HttpServletRequest request, HttpServletResponse response, - UserRefreshToken userRefreshToken, AuthToken authRefreshToken, Date now) { + UserRefreshToken userRefreshToken, AuthToken authRefreshToken, Date now) { long validTime = authRefreshToken.getTokenClaims().getExpiration().getTime() - now.getTime(); // refresh 토큰 기간이 3일 이하로 남은 경우, refresh 토큰 갱신 @@ -94,14 +39,14 @@ public void reissueRefreshTokenIfValidTimeleft3days(HttpServletRequest request, long refreshTokenExpiry = authProperties.getTokenProperties().getRefreshTokenExpiry(); authRefreshToken = tokenProvider.createAuthToken( - authProperties.getTokenProperties().getTokenSecretKey(), - new Date(now.getTime() + refreshTokenExpiry) + authProperties.getTokenProperties().getTokenSecretKey(), + new Date(now.getTime() + refreshTokenExpiry) ); // DB에 refresh 토큰 업데이트 - userRefreshToken.setRefreshToken(authRefreshToken.getToken()); + userRefreshToken.reissueRefreshToken(authRefreshToken.getToken()); - int cookieMaxAge = (int) refreshTokenExpiry / 60; + int cookieMaxAge = (int)refreshTokenExpiry / 60; CookieUtil.deleteCookie(request, response, REFRESH_TOKEN); CookieUtil.addCookie(response, REFRESH_TOKEN, authRefreshToken.getToken(), cookieMaxAge); } diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/service/UserProfileService.java b/backend/src/main/java/com/hjjang/backend/domain/user/service/UserProfileService.java new file mode 100644 index 0000000..0a809fe --- /dev/null +++ b/backend/src/main/java/com/hjjang/backend/domain/user/service/UserProfileService.java @@ -0,0 +1,34 @@ +package com.hjjang.backend.domain.user.service; + +import javax.persistence.EntityNotFoundException; + +import org.springframework.stereotype.Service; + +import com.hjjang.backend.domain.user.dto.UserProfileDTO; +import com.hjjang.backend.domain.user.entity.User; +import com.hjjang.backend.domain.user.repository.UserRepository; +import com.hjjang.backend.global.util.UserUtil; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class UserProfileService { + private final UserRepository userRepository; + + public UserProfileDTO getUserProfile() { + String userId = UserUtil.getLoginUserIdByToken(); + + //todo add exception + User user = userRepository.findUserByProviderId(userId).orElseThrow(EntityNotFoundException::new); + + //todo found univName logic + return UserProfileDTO.builder() + .userNickname(user.getNickName()) + .userImageUrl(user.getImageUrl()) + .userMannerTemperature(user.getMannerTemperature()) + .userEmail(user.getEmail()) + .userUnivName("산기대") + .build(); + } +} diff --git a/backend/src/main/java/com/hjjang/backend/domain/user/service/UserService.java b/backend/src/main/java/com/hjjang/backend/domain/user/service/UserService.java deleted file mode 100644 index 3d6b1e5..0000000 --- a/backend/src/main/java/com/hjjang/backend/domain/user/service/UserService.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.hjjang.backend.domain.user.service; - -import com.hjjang.backend.domain.user.entity.User; -import com.hjjang.backend.domain.user.repository.UserRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class UserService { - private final UserRepository userRepository; - - public User getUser(String userId) { - return userRepository.findUserById(userId); - } -} diff --git a/backend/src/main/java/com/hjjang/backend/global/config/properties/AuthProperties.java b/backend/src/main/java/com/hjjang/backend/global/config/properties/AuthProperties.java index 4a6093b..8a7afa5 100644 --- a/backend/src/main/java/com/hjjang/backend/global/config/properties/AuthProperties.java +++ b/backend/src/main/java/com/hjjang/backend/global/config/properties/AuthProperties.java @@ -3,45 +3,40 @@ import java.util.ArrayList; import java.util.List; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; +import org.springframework.context.annotation.Configuration; -import lombok.Getter; -import lombok.Setter; +import lombok.Data; -@Getter -@Component -@ConfigurationProperties(prefix = "auth") +@Data +@Configuration +@ConfigurationProperties("auth-properties") public class AuthProperties { - - private final TokenProperties tokenProperties = new TokenProperties(); - private final OAuth2Properties oAuth2Properties = new OAuth2Properties(); - private final CorsProperties corsProperties = new CorsProperties(); - private final JwtProperties jwtProperties = new JwtProperties(); - - @Getter - @Setter - public final static class TokenProperties { + private TokenProperties tokenProperties = new TokenProperties(); + private JwtProperties jwtProperties = new JwtProperties(); + private CorsProperties corsProperties = new CorsProperties(); + private OAuth2Properties oAuth2Properties = new OAuth2Properties(); + @Data + public static class TokenProperties { + @Value("auth-properties.token-properties.token-secret-key") private String tokenSecretKey; private long tokenExpireDate; private long refreshTokenExpiry; } - - @Getter - @Setter - public final static class JwtProperties { + @Data + public static class JwtProperties { + @Value("auth-properties.jwt-properties.secret-key") private String secretKey; } - - @Getter - @Setter - public final static class OAuth2Properties { + @Data + public static class OAuth2Properties { + @Value("auth-properties.o-auth2-properties.redirect-uris[]") private List redirectUris = new ArrayList<>(); } - - @Getter - @Setter - public final static class CorsProperties { + @Data + public static class CorsProperties { + @Value("auth-properties.cors-properties.allowed-origins") private String allowedOrigins; private String allowedMethods; private String allowedHeaders; diff --git a/backend/src/main/java/com/hjjang/backend/global/config/security/SecurityConfig.java b/backend/src/main/java/com/hjjang/backend/global/config/security/SecurityConfig.java index b8912e2..63860b0 100644 --- a/backend/src/main/java/com/hjjang/backend/global/config/security/SecurityConfig.java +++ b/backend/src/main/java/com/hjjang/backend/global/config/security/SecurityConfig.java @@ -75,9 +75,10 @@ protected void configure(HttpSecurity http) throws Exception { .accessDeniedHandler(tokenAccessDeniedHandler) // 로그인 거부 예외 .and() .authorizeRequests() + .antMatchers("/login", "/accounts", "/swagger-resources/**", "/swagger-ui/**").permitAll() + .antMatchers("/oauth2/authorization/**", "**/oauth2/code/*").permitAll() .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() //cors를 검증 하는 option 함수의 경우 별도의 filter 없이 허용 - //.antMatchers("/api/**").hasAnyAuthority(RoleType.USER.getCode()) - // todo swagger 고치고 권한 인증 묻기 +// .antMatchers("/api/**").hasAnyAuthority(RoleType.USER.getCode()) .anyRequest().permitAll(); http diff --git a/backend/src/main/java/com/hjjang/backend/global/config/security/handler/OAuth2AuthenticationSuccessHandler.java b/backend/src/main/java/com/hjjang/backend/global/config/security/handler/OAuth2AuthenticationSuccessHandler.java index cc46b98..2ae9bdf 100644 --- a/backend/src/main/java/com/hjjang/backend/global/config/security/handler/OAuth2AuthenticationSuccessHandler.java +++ b/backend/src/main/java/com/hjjang/backend/global/config/security/handler/OAuth2AuthenticationSuccessHandler.java @@ -1,26 +1,5 @@ package com.hjjang.backend.global.config.security.handler; -import static com.hjjang.backend.global.config.security.repository.OAuth2AuthorizationRequestBasedOnCookieRepository.*; - -import java.io.IOException; -import java.net.URI; -import java.util.Collection; -import java.util.Date; -import java.util.Optional; - -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; -import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; -import org.springframework.stereotype.Component; -import org.springframework.web.util.UriComponentsBuilder; - import com.hjjang.backend.domain.user.entity.ProviderType; import com.hjjang.backend.domain.user.entity.RoleType; import com.hjjang.backend.domain.user.entity.UserRefreshToken; @@ -32,8 +11,27 @@ import com.hjjang.backend.global.config.security.token.AuthToken; import com.hjjang.backend.global.config.security.token.AuthTokenProvider; import com.hjjang.backend.global.util.CookieUtil; - import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.core.oidc.user.OidcUser; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URI; +import java.util.Collection; +import java.util.Date; +import java.util.Optional; + +import static com.hjjang.backend.global.config.security.repository.OAuth2AuthorizationRequestBasedOnCookieRepository.REDIRECT_URI_PARAM_COOKIE_NAME; +import static com.hjjang.backend.global.config.security.repository.OAuth2AuthorizationRequestBasedOnCookieRepository.REFRESH_TOKEN; @Component @RequiredArgsConstructor @@ -46,7 +44,7 @@ public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws IOException, ServletException { + Authentication authentication) throws IOException, ServletException { String targetUrl = determineTargetUrl(request, response, authentication); if (response.isCommitted()) { @@ -58,60 +56,61 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo getRedirectStrategy().sendRedirect(request, response, targetUrl); } + @Override protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) { + Authentication authentication) { Optional redirectUri = CookieUtil.getCookie(request, REDIRECT_URI_PARAM_COOKIE_NAME) - .map(Cookie::getValue); + .map(Cookie::getValue); if (redirectUri.isPresent() && !isAuthorizedRedirectUri(redirectUri.get())) { throw new IllegalArgumentException( - "Sorry! We've got an Unauthorized Redirect URI and can't proceed with the authentication"); + "Sorry! We've got an Unauthorized Redirect URI and can't proceed with the authentication"); } String targetUrl = redirectUri.orElse(getDefaultTargetUrl()); - OAuth2AuthenticationToken authToken = (OAuth2AuthenticationToken)authentication; + OAuth2AuthenticationToken authToken = (OAuth2AuthenticationToken) authentication; ProviderType providerType = ProviderType.valueOf(authToken.getAuthorizedClientRegistrationId().toUpperCase()); - OidcUser user = ((OidcUser)authentication.getPrincipal()); + OidcUser user = ((OidcUser) authentication.getPrincipal()); ParsingUserContext userInfo = ParsingUserContextFactory.getParsingParsingUserContext(providerType, - user.getAttributes()); - Collection authorities = ((OidcUser)authentication.getPrincipal()).getAuthorities(); + user.getAttributes()); + Collection authorities = ((OidcUser) authentication.getPrincipal()).getAuthorities(); RoleType userRole = hasAuthority(authorities, RoleType.ADMIN.getCode()) ? RoleType.ADMIN : RoleType.USER; Date now = new Date(); AuthToken accessToken = tokenProvider.createAuthToken( - userInfo.getId(), - userRole.getCode(), - new Date(now.getTime() + authProperties.getTokenProperties().getTokenExpireDate()) + userInfo.getId(), + userRole.getCode(), + new Date(now.getTime() + authProperties.getTokenProperties().getTokenExpireDate()) ); // refresh 토큰 설정 long refreshTokenExpiry = authProperties.getTokenProperties().getRefreshTokenExpiry(); AuthToken refreshToken = tokenProvider.createAuthToken( - authProperties.getTokenProperties().getTokenSecretKey(), - new Date(now.getTime() + refreshTokenExpiry) + authProperties.getTokenProperties().getTokenSecretKey(), + new Date(now.getTime() + refreshTokenExpiry) ); // DB 저장 - UserRefreshToken userRefreshToken = userRefreshTokenRepository.findByUserId(userInfo.getId()); + UserRefreshToken userRefreshToken = userRefreshTokenRepository.findByProviderId(userInfo.getId()); if (userRefreshToken != null) { - userRefreshToken.setRefreshToken(refreshToken.getToken()); + userRefreshToken.reissueRefreshToken(refreshToken.getToken()); } else { userRefreshToken = new UserRefreshToken(userInfo.getId(), refreshToken.getToken()); userRefreshTokenRepository.saveAndFlush(userRefreshToken); } - int cookieMaxAge = (int)refreshTokenExpiry / 60; + int cookieMaxAge = (int) refreshTokenExpiry / 60; CookieUtil.deleteCookie(request, response, REFRESH_TOKEN); CookieUtil.addCookie(response, REFRESH_TOKEN, refreshToken.getToken(), cookieMaxAge); return UriComponentsBuilder.fromUriString(targetUrl) - .queryParam("token", accessToken.getToken()) - .build().toUriString(); + .queryParam("token", accessToken.getToken()) + .build().toUriString(); } protected void clearAuthenticationAttributes(HttpServletRequest request, HttpServletResponse response) { @@ -136,15 +135,12 @@ private boolean isAuthorizedRedirectUri(String uri) { URI clientRedirectUri = URI.create(uri); return authProperties.getOAuth2Properties().getRedirectUris() - .stream() - .anyMatch(authorizedRedirectUri -> { - // Only validate host and port. Let the clients use different paths if they want to - URI authorizedURI = URI.create(authorizedRedirectUri); - if (authorizedURI.getHost().equalsIgnoreCase(clientRedirectUri.getHost()) - && authorizedURI.getPort() == clientRedirectUri.getPort()) { - return true; - } - return false; - }); + .stream() + .anyMatch(authorizedRedirectUri -> { + // Only validate host and port. Let the clients use different paths if they want to + URI authorizedURI = URI.create(authorizedRedirectUri); + return authorizedURI.getHost().equalsIgnoreCase(clientRedirectUri.getHost()) + && authorizedURI.getPort() == clientRedirectUri.getPort(); + }); } } diff --git a/backend/src/main/java/com/hjjang/backend/global/config/security/principal/UserPrincipal.java b/backend/src/main/java/com/hjjang/backend/global/config/security/principal/UserPrincipal.java index e90fe3d..cbae845 100644 --- a/backend/src/main/java/com/hjjang/backend/global/config/security/principal/UserPrincipal.java +++ b/backend/src/main/java/com/hjjang/backend/global/config/security/principal/UserPrincipal.java @@ -1,9 +1,11 @@ package com.hjjang.backend.global.config.security.principal; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; - +import com.hjjang.backend.domain.user.entity.RoleType; +import com.hjjang.backend.domain.user.entity.User; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -12,13 +14,9 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.oauth2.core.user.OAuth2User; -import com.hjjang.backend.domain.user.entity.RoleType; -import com.hjjang.backend.domain.user.entity.User; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; @Getter @AllArgsConstructor @@ -93,7 +91,7 @@ public OidcIdToken getIdToken() { public static UserPrincipal create(User user) { return new UserPrincipal( - user.getId(), + user.getProviderId(), RoleType.USER, Collections.singletonList(new SimpleGrantedAuthority(RoleType.USER.getCode()) )); diff --git a/backend/src/main/java/com/hjjang/backend/global/config/security/service/CustomOAuth2UserService.java b/backend/src/main/java/com/hjjang/backend/global/config/security/service/CustomOAuth2UserService.java index 9994de3..1224372 100644 --- a/backend/src/main/java/com/hjjang/backend/global/config/security/service/CustomOAuth2UserService.java +++ b/backend/src/main/java/com/hjjang/backend/global/config/security/service/CustomOAuth2UserService.java @@ -1,13 +1,5 @@ package com.hjjang.backend.global.config.security.service; -import org.springframework.security.authentication.InternalAuthenticationServiceException; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.stereotype.Service; - import com.hjjang.backend.domain.user.entity.Agreement; import com.hjjang.backend.domain.user.entity.RoleType; import com.hjjang.backend.domain.user.entity.User; @@ -15,8 +7,16 @@ import com.hjjang.backend.global.config.security.parser.KakaoParsingParsingUserContext; import com.hjjang.backend.global.config.security.parser.ParsingUserContext; import com.hjjang.backend.global.config.security.principal.UserPrincipal; - import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; + +import java.util.Optional; @Service @RequiredArgsConstructor @@ -40,7 +40,11 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic private OAuth2User process(OAuth2UserRequest userRequest, OAuth2User user) { ParsingUserContext userInfo = new KakaoParsingParsingUserContext(user.getAttributes()); - User savedUser = userRepository.findUserById(userInfo.getId()); + Optional optionalSavedUser = userRepository.findUserByProviderId(userInfo.getId()); + User savedUser = null; + if (optionalSavedUser.isPresent()) { + savedUser = optionalSavedUser.get(); + } if (savedUser == null) { savedUser = createUser(userInfo); @@ -51,7 +55,7 @@ private OAuth2User process(OAuth2UserRequest userRequest, OAuth2User user) { private User createUser(ParsingUserContext userInfo) { User user = User.builder() - .id(userInfo.getId()) + .providerId(userInfo.getId()) .nickName(userInfo.getName()) .email(userInfo.getEmail()) .isPushAgree(Agreement.DISAGREE) diff --git a/backend/src/main/java/com/hjjang/backend/global/defender/BinderControllerAdvice4springShell.java b/backend/src/main/java/com/hjjang/backend/global/defender/BinderControllerAdvice4springShell.java deleted file mode 100644 index ba22e50..0000000 --- a/backend/src/main/java/com/hjjang/backend/global/defender/BinderControllerAdvice4springShell.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.hjjang.backend.global.defender; - -import org.springframework.core.annotation.Order; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.InitBinder; - -@ControllerAdvice -@Order(10000) -public class BinderControllerAdvice4springShell { - - @InitBinder - public void setAllowedFields(WebDataBinder dataBinder) { - String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"}; - dataBinder.setDisallowedFields(denylist); - } -} diff --git a/backend/src/main/java/com/hjjang/backend/global/util/UserUtil.java b/backend/src/main/java/com/hjjang/backend/global/util/UserUtil.java new file mode 100644 index 0000000..e7af44f --- /dev/null +++ b/backend/src/main/java/com/hjjang/backend/global/util/UserUtil.java @@ -0,0 +1,32 @@ +package com.hjjang.backend.global.util; + +import com.hjjang.backend.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; +import org.springframework.stereotype.Component; + +import javax.persistence.EntityNotFoundException; + +@Component +@RequiredArgsConstructor +public class UserUtil { + + private final UserRepository userRepository; + + public static String getLoginUserIdByToken() { + User principal = (User) SecurityContextHolder.getContext() + .getAuthentication() + .getPrincipal(); + + return principal.getUsername(); + } + + public com.hjjang.backend.domain.user.entity.User getLoginUserByToken() { + + User principal = (User) SecurityContextHolder.getContext() + .getAuthentication() + .getPrincipal(); + return userRepository.findUserByProviderId(principal.getUsername()).orElseThrow(EntityNotFoundException::new); + } +} diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 6fe52ac..464d262 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -19,7 +19,7 @@ spring: database: "mysql" show-sql: "true" hibernate: - ddl-auto: "none" + ddl-auto: "update" properties: hibernate: format_sql: true @@ -82,6 +82,7 @@ auth: allowed-methods: GET,POST,PUT,DELETE,OPTIONS allowed-headers: '*' max-age: 3600 - oauth2Properties: + oAuth2Properties: redirectUris: - - 'http://localhost:3000/oauth/redirect' \ No newline at end of file + - 'http://localhost:3000/oauth/redirect' + - 'https://getpostman.com/oauth2/redirect' \ No newline at end of file diff --git a/backend/src/main/resources/log4j2.xml b/backend/src/main/resources/log4j2.xml new file mode 100644 index 0000000..85b2612 --- /dev/null +++ b/backend/src/main/resources/log4j2.xml @@ -0,0 +1,45 @@ + + + + + Default-Setting + %style{%d{yyyy/MM/dd HH:mm:ss,SSS}}{cyan} %highlight{[%-5p]}{FATAL=bg_red, ERROR=red, INFO=green, DEBUG=blue} [%C] %style{[%t]}{yellow}- %m%n - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/src/test/java/com/hjjang/backend/Integration/AuthPropertiesTest.java b/backend/src/test/java/com/hjjang/backend/Integration/AuthPropertiesTest.java deleted file mode 100644 index ba9a269..0000000 --- a/backend/src/test/java/com/hjjang/backend/Integration/AuthPropertiesTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.hjjang.backend.Integration; - -import com.hjjang.backend.global.config.properties.AuthProperties; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -@ExtendWith(SpringExtension.class) -@SpringBootTest -class AuthPropertiesTest { - - @Autowired - private AuthProperties authProperties; - - @Test - void getAuth() { - - AuthProperties.TokenProperties tokenProperties = authProperties.getTokenProperties(); - assertAll( - ()-> assertEquals("asodfhwoeihro1i34u1097r09u13rqfasc12", tokenProperties.getTokenSecretKey()), - ()-> assertEquals(1800000, tokenProperties.getTokenExpireDate()), - ()-> assertEquals(604800000, tokenProperties.getRefreshTokenExpiry()) - ); - } - - @Test - void getJwt() { - AuthProperties.JwtProperties jwtProperties = authProperties.getJwtProperties(); - assertAll( - ()-> assertEquals("HJJangHandsomeGnuKenny120hy12049h014",jwtProperties.getSecretKey()) - ); - } - - @Test - void getOauth() { - AuthProperties.OAuth2Properties oAuthProperties = authProperties.getOAuth2Properties(); - assertAll( - ()-> assertLinesMatch( List.of("http://localhost:3000/oauth/redirect"), oAuthProperties.getRedirectUris()) - ); - } - - @Test - void cors() { - AuthProperties.CorsProperties corsProperties = authProperties.getCorsProperties(); - assertAll( - ()-> assertEquals("http://localhost:3000", corsProperties.getAllowedOrigins()), - ()-> assertEquals("GET,POST,PUT,DELETE,OPTIONS", corsProperties.getAllowedMethods()), - ()-> assertEquals("*", corsProperties.getAllowedHeaders()), - ()-> assertEquals(3600, corsProperties.getMaxAge()) - ); - } - - -} \ No newline at end of file