diff --git a/user-service/build.gradle b/user-service/build.gradle index aaa9f2e..fea05ef 100644 --- a/user-service/build.gradle +++ b/user-service/build.gradle @@ -38,6 +38,7 @@ dependencies { compileOnly 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.cloud:spring-cloud-starter-config' /*implementation 'org.springframework.kafka:spring-kafka'*/ + implementation 'org.springframework.boot:spring-boot-starter-data-redis' // https://mvnrepository.com/artifact/com.github.gavlyukovskiy/p6spy-spring-boot-starter implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.0' diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/exception/TokenRefreshException.java b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/exception/AccessTokenNotValidException.java similarity index 64% rename from user-service/src/main/java/com/justpickup/userservice/domain/jwt/exception/TokenRefreshException.java rename to user-service/src/main/java/com/justpickup/userservice/domain/jwt/exception/AccessTokenNotValidException.java index 691c99f..a0ffe60 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/exception/TokenRefreshException.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/exception/AccessTokenNotValidException.java @@ -3,9 +3,9 @@ package com.justpickup.userservice.domain.jwt.exception; import com.justpickup.userservice.global.exception.CustomException; import org.springframework.http.HttpStatus; -public class TokenRefreshException extends CustomException { +public class AccessTokenNotValidException extends CustomException { - public TokenRefreshException(String message) { + public AccessTokenNotValidException(String message) { super(HttpStatus.FORBIDDEN, message); } } diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/exception/RefreshTokenNotValidException.java b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/exception/RefreshTokenNotValidException.java new file mode 100644 index 0000000..392c0ab --- /dev/null +++ b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/exception/RefreshTokenNotValidException.java @@ -0,0 +1,14 @@ +package com.justpickup.userservice.domain.jwt.exception; + +import com.justpickup.userservice.global.dto.Result; +import lombok.Getter; + +@Getter +public class RefreshTokenNotValidException extends RuntimeException { + + private Result result; + + public RefreshTokenNotValidException(String message) { + this.result = Result.createErrorResult(message); + } +} diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/redis/RefreshToken.java b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/redis/RefreshToken.java new file mode 100644 index 0000000..ad91c54 --- /dev/null +++ b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/redis/RefreshToken.java @@ -0,0 +1,25 @@ +package com.justpickup.userservice.domain.jwt.redis; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + + +@Getter +@RedisHash("refresh_token") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class RefreshToken { + + @Id + private String userId; + private String refreshTokenId; + + public static RefreshToken of(String userId, String refreshTokenId) { + RefreshToken refreshToken = new RefreshToken(); + refreshToken.userId = userId; + refreshToken.refreshTokenId = refreshTokenId; + return refreshToken; + } +} diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/repository/RefreshTokenRedisRepository.java b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/repository/RefreshTokenRedisRepository.java new file mode 100644 index 0000000..dd46557 --- /dev/null +++ b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/repository/RefreshTokenRedisRepository.java @@ -0,0 +1,7 @@ +package com.justpickup.userservice.domain.jwt.repository; + +import com.justpickup.userservice.domain.jwt.redis.RefreshToken; +import org.springframework.data.repository.CrudRepository; + +public interface RefreshTokenRedisRepository extends CrudRepository { +} diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenService.java b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenService.java index 3475a20..84b91b1 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenService.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenService.java @@ -5,4 +5,5 @@ import com.justpickup.userservice.domain.user.dto.JwtTokenDto; public interface RefreshTokenService { void updateRefreshToken(Long id, String refreshToken); JwtTokenDto refreshJwtToken(String accessToken, String refreshToken); + void logoutToken(String accessToken); } diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenServiceImpl.java b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenServiceImpl.java index e5706cb..fcd9981 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenServiceImpl.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/service/RefreshTokenServiceImpl.java @@ -1,15 +1,21 @@ package com.justpickup.userservice.domain.jwt.service; -import com.justpickup.userservice.domain.jwt.exception.TokenRefreshException; +import com.justpickup.userservice.domain.jwt.exception.AccessTokenNotValidException; +import com.justpickup.userservice.domain.jwt.exception.RefreshTokenNotValidException; +import com.justpickup.userservice.domain.jwt.redis.RefreshToken; +import com.justpickup.userservice.domain.jwt.repository.RefreshTokenRedisRepository; +import com.justpickup.userservice.global.utils.JwtTokenProvider; import com.justpickup.userservice.domain.user.dto.JwtTokenDto; import com.justpickup.userservice.domain.user.entity.User; import com.justpickup.userservice.domain.user.exception.NotExistUserException; import com.justpickup.userservice.domain.user.repository.UserRepository; -import com.justpickup.userservice.domain.jwt.utils.JwtTokenProvider; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -21,16 +27,18 @@ import java.util.stream.Collectors; @Transactional(readOnly = true) @Slf4j public class RefreshTokenServiceImpl implements RefreshTokenService { - private final UserRepository userRepository; + private final UserDetailsService userDetailsService; private final JwtTokenProvider jwtTokenProvider; + private final UserRepository userRepository; + private final RefreshTokenRedisRepository refreshTokenRedisRepository; @Transactional @Override - public void updateRefreshToken(Long id, String refreshToken) { + public void updateRefreshToken(Long id, String uuid) { User user = userRepository.findById(id) .orElseThrow(() -> new NotExistUserException("사용자 고유번호 : " + id + "는 없는 사용자입니다.")); - user.changeRefreshToken(refreshToken); + refreshTokenRedisRepository.save(RefreshToken.of(user.getId().toString(), uuid)); } @Transactional @@ -38,36 +46,51 @@ public class RefreshTokenServiceImpl implements RefreshTokenService { public JwtTokenDto refreshJwtToken(String accessToken, String refreshToken) { String userId = jwtTokenProvider.getUserId(accessToken); - User user = userRepository.findById(Long.valueOf(userId)) - .orElseThrow(() -> new NotExistUserException("사용자 고유번호 : " + userId + "는 없는 사용자입니다.")); + RefreshToken findRefreshToken = refreshTokenRedisRepository.findById(userId) + .orElseThrow(() + -> new RefreshTokenNotValidException("사용자 고유번호 : " + userId + "는 등록된 리프레쉬 토큰이 없습니다.") + ); // refresh token 검증 - if (!jwtTokenProvider.validateJwtToken(refreshToken)) { - // 익셉션 발생 - 로그 아웃 후 로그인 페이지로 이동 처리 - user.deleteRefreshToken(); - throw new TokenRefreshException("Not validate jwt token = " + refreshToken); + String findRefreshTokenId = findRefreshToken.getRefreshTokenId(); + if (!jwtTokenProvider.validateJwtToken(refreshToken) || + !jwtTokenProvider.equalRefreshTokenId(findRefreshTokenId, refreshToken)) { + + refreshTokenRedisRepository.delete(findRefreshToken); + throw new RefreshTokenNotValidException("Not validate jwt token = " + refreshToken); } - String userRefreshTokenId = user.getRefreshTokenId(); - if (!jwtTokenProvider.equalRefreshTokenId(userRefreshTokenId, refreshToken)) { - // 익셉션 발생 - 로그인 아웃 후 로그인 페이지로 이동 처리 - user.deleteRefreshToken(); - throw new TokenRefreshException("Not equal jwt token! user = " + userRefreshTokenId + - ", refreshToken = " + refreshToken); - } + User findUser = userRepository.findById(Long.valueOf(userId)) + .orElseThrow(() -> new NotExistUserException("유저 고유 번호 : " + userId + "는 없는 유저입니다.")); - Authentication authentication = jwtTokenProvider.getAuthentication(user.getEmail()); + // access token 생성 + Authentication authentication = getAuthentication(findUser.getEmail()); List roles = authentication.getAuthorities() .stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); String newAccessToken = jwtTokenProvider.createJwtAccessToken(userId, "/refreshToken", roles); - String newRefreshToken = jwtTokenProvider.createJwtRefreshToken(); - - user.changeRefreshToken(newRefreshToken); return JwtTokenDto.builder() .accessToken(newAccessToken) - .refreshToken(newRefreshToken) + .refreshToken(refreshToken) .build(); } + + @Override + public void logoutToken(String accessToken) { + if (!jwtTokenProvider.validateJwtToken(accessToken)) { + // 예외 발생 + throw new AccessTokenNotValidException("access token is not valid"); + } + + RefreshToken refreshToken = refreshTokenRedisRepository.findById(jwtTokenProvider.getUserId(accessToken)) + .orElseThrow(() -> new RefreshTokenNotValidException("refresh Token is not exist")); + + refreshTokenRedisRepository.delete(refreshToken); + } + + public Authentication getAuthentication(String email) { + UserDetails userDetails = userDetailsService.loadUserByUsername(email); + return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); + } } diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/web/AuthController.java b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/web/AuthController.java index 9841b69..c814285 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/web/AuthController.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/jwt/web/AuthController.java @@ -3,23 +3,29 @@ package com.justpickup.userservice.domain.jwt.web; import com.justpickup.userservice.domain.jwt.service.RefreshTokenServiceImpl; import com.justpickup.userservice.domain.user.dto.JwtTokenDto; import com.justpickup.userservice.global.dto.Result; +import com.justpickup.userservice.global.utils.CookieProvider; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; +import javax.ws.rs.core.HttpHeaders; + @RestController @RequiredArgsConstructor @Slf4j public class AuthController { private final RefreshTokenServiceImpl refreshTokenServiceImpl; + private final CookieProvider cookieProvider; @GetMapping("/refreshToken") public ResponseEntity refreshToken(@RequestHeader("X-AUTH-TOKEN") String accessToken, @@ -27,7 +33,11 @@ public class AuthController { JwtTokenDto jwtTokenDto = refreshTokenServiceImpl.refreshJwtToken(accessToken, refreshToken); - return ResponseEntity.ok(Result.createSuccessResult(new RefreshTokenResponse(jwtTokenDto))); + ResponseCookie responseCookie = cookieProvider.createRefreshTokenCookie(refreshToken); + + return ResponseEntity.status(HttpStatus.OK) + .header(HttpHeaders.SET_COOKIE, responseCookie.toString()) + .body(Result.createSuccessResult(new RefreshTokenResponse(jwtTokenDto))); } @Data @@ -35,19 +45,22 @@ public class AuthController { @AllArgsConstructor static class RefreshTokenResponse { private String accessToken; - private String refreshToken; public RefreshTokenResponse(JwtTokenDto jwtTokenDto) { this.accessToken = jwtTokenDto.getAccessToken(); - this.refreshToken = jwtTokenDto.getRefreshToken(); } } @PostMapping("/logout") public ResponseEntity logout(@RequestHeader("X-AUTH-TOKEN") String accessToken, @RequestHeader("REFRESH-TOKEN") String refreshToken) { - log.info("########### logout!"); - // TODO: 2022/02/16 logout 구현 필요 - return ResponseEntity.ok(Result.createSuccessResult("success")); + + refreshTokenServiceImpl.logoutToken(accessToken); + + ResponseCookie refreshCookie = cookieProvider.removeRefreshTokenCookie(); + + return ResponseEntity.status(HttpStatus.OK) + .header(HttpHeaders.SET_COOKIE, refreshCookie.toString()) + .body(Result.createErrorResult("")); } } diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/StoreOwner.java b/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/StoreOwner.java index 25a58bb..5b1bf75 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/StoreOwner.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/StoreOwner.java @@ -1,6 +1,5 @@ package com.justpickup.userservice.domain.user.entity; -import com.justpickup.userservice.domain.user.dto.StoreOwnerDto; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -15,8 +14,8 @@ public class StoreOwner extends User { private String businessNumber; public StoreOwner(String email, String password, String name, String phoneNumber, - String businessNumber, String refreshTokenId) { - super(email, password, name, phoneNumber, refreshTokenId); + String businessNumber) { + super(email, password, name, phoneNumber); this.businessNumber = businessNumber; } } diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/User.java b/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/User.java index bfce461..ee5077f 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/User.java +++ b/user-service/src/main/java/com/justpickup/userservice/domain/user/entity/User.java @@ -12,7 +12,7 @@ import javax.persistence.*; @Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorColumn(name = "DTYPE") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class User extends BaseEntity { +public abstract class User extends BaseEntity { @Id @GeneratedValue @Column(name = "user_id") @@ -26,8 +26,6 @@ public class User extends BaseEntity { private String phoneNumber; - private String refreshTokenId; - @Enumerated(EnumType.STRING) private Role role; @@ -42,19 +40,11 @@ public class User extends BaseEntity { this.role = role; } - public User(String email, String password, String name, String phoneNumber, String refreshTokenId) { + public User(String email, String password, String name, String phoneNumber) { this.email = email; this.password = password; this.name = name; this.phoneNumber = phoneNumber; - this.refreshTokenId = refreshTokenId; } - public void changeRefreshToken(String refreshToken) { - this.refreshTokenId = refreshToken; - } - - public void deleteRefreshToken() { - this.refreshTokenId = null; - } } diff --git a/user-service/src/main/java/com/justpickup/userservice/global/config/RedisConfig.java b/user-service/src/main/java/com/justpickup/userservice/global/config/RedisConfig.java new file mode 100644 index 0000000..2bebb13 --- /dev/null +++ b/user-service/src/main/java/com/justpickup/userservice/global/config/RedisConfig.java @@ -0,0 +1,31 @@ +package com.justpickup.userservice.global.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +@RequiredArgsConstructor +public class RedisConfig { + + private final RedisProperties redisProperties; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort()); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + return redisTemplate; + } +} diff --git a/user-service/src/main/java/com/justpickup/userservice/global/exception/GlobalExceptionHandler.java b/user-service/src/main/java/com/justpickup/userservice/global/exception/GlobalExceptionHandler.java index 97ccca2..55d9c4a 100644 --- a/user-service/src/main/java/com/justpickup/userservice/global/exception/GlobalExceptionHandler.java +++ b/user-service/src/main/java/com/justpickup/userservice/global/exception/GlobalExceptionHandler.java @@ -1,8 +1,13 @@ package com.justpickup.userservice.global.exception; +import com.justpickup.userservice.domain.jwt.exception.RefreshTokenNotValidException; import com.justpickup.userservice.global.dto.Result; +import com.justpickup.userservice.global.utils.CookieProvider; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; @@ -11,9 +16,12 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice +@RequiredArgsConstructor @Slf4j public class GlobalExceptionHandler { + private final CookieProvider cookieProvider; + @ExceptionHandler(CustomException.class) public ResponseEntity customExceptionHandler(CustomException ce) { HttpStatus status = ce.getStatus(); @@ -23,6 +31,16 @@ public class GlobalExceptionHandler { .body(errorResult); } + @ExceptionHandler(RefreshTokenNotValidException.class) + public ResponseEntity customJwtExceptionHandler(RefreshTokenNotValidException e) { + // 쿠키 삭제 + ResponseCookie responseCookie = cookieProvider.removeRefreshTokenCookie(); + + return ResponseEntity.status(HttpStatus.FORBIDDEN) + .header(HttpHeaders.SET_COOKIE, responseCookie.toString()) + .body(e.getResult()); + } + @ExceptionHandler(BindException.class) public ResponseEntity bindExceptionHandler(BindException exception) { return getValidationErrorBody(exception); diff --git a/user-service/src/main/java/com/justpickup/userservice/global/security/LoginAuthenticationFilter.java b/user-service/src/main/java/com/justpickup/userservice/global/security/LoginAuthenticationFilter.java index 311a6a1..1b6ad13 100644 --- a/user-service/src/main/java/com/justpickup/userservice/global/security/LoginAuthenticationFilter.java +++ b/user-service/src/main/java/com/justpickup/userservice/global/security/LoginAuthenticationFilter.java @@ -2,10 +2,13 @@ package com.justpickup.userservice.global.security; import com.fasterxml.jackson.databind.ObjectMapper; import com.justpickup.userservice.domain.jwt.service.RefreshTokenServiceImpl; -import com.justpickup.userservice.domain.jwt.utils.JwtTokenProvider; +import com.justpickup.userservice.global.utils.JwtTokenProvider; import com.justpickup.userservice.global.dto.LoginRequest; +import com.justpickup.userservice.global.dto.Result; +import com.justpickup.userservice.global.utils.CookieProvider; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseCookie; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -16,6 +19,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic import javax.servlet.FilterChain; import javax.servlet.ServletException; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -32,6 +36,7 @@ public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFil private final AuthenticationManager authenticationManager; private final JwtTokenProvider jwtTokenProvider; private final RefreshTokenServiceImpl refreshTokenServiceImpl; + private final CookieProvider cookieProvider; // login 리퀘스트 패스로 오는 요청을 판단 @Override @@ -69,14 +74,20 @@ public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFil refreshTokenServiceImpl.updateRefreshToken(Long.valueOf(userId), jwtTokenProvider.getRefreshTokenId(refreshToken)); - Map tokens = Map.of( - "access_token", accessToken, - "refresh_token", refreshToken - ); + // 쿠키 설정 + ResponseCookie refreshTokenCookie = cookieProvider.createRefreshTokenCookie(refreshToken); + + Cookie cookie = cookieProvider.of(refreshTokenCookie); response.setContentType(APPLICATION_JSON_VALUE); + response.addCookie(cookie); - new ObjectMapper().writeValue(response.getOutputStream(), tokens); + // body 설정 + Map tokens = Map.of( + "access_token", accessToken + ); + + new ObjectMapper().writeValue(response.getOutputStream(), Result.createSuccessResult(tokens)); } @Override diff --git a/user-service/src/main/java/com/justpickup/userservice/global/security/SecurityConfig.java b/user-service/src/main/java/com/justpickup/userservice/global/security/SecurityConfig.java index 7c73707..f876491 100644 --- a/user-service/src/main/java/com/justpickup/userservice/global/security/SecurityConfig.java +++ b/user-service/src/main/java/com/justpickup/userservice/global/security/SecurityConfig.java @@ -1,8 +1,9 @@ package com.justpickup.userservice.global.security; import com.justpickup.userservice.domain.jwt.service.RefreshTokenServiceImpl; -import com.justpickup.userservice.domain.jwt.utils.JwtTokenProvider; +import com.justpickup.userservice.global.utils.JwtTokenProvider; import com.justpickup.userservice.domain.user.service.UserService; +import com.justpickup.userservice.global.utils.CookieProvider; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; @@ -23,6 +24,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { private final BCryptPasswordEncoder bCryptPasswordEncoder; private final JwtTokenProvider jwtTokenProvider; private final RefreshTokenServiceImpl refreshTokenServiceImpl; + private final CookieProvider cookieProvider; private final UserService userService; @@ -34,7 +36,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { LoginAuthenticationFilter loginAuthenticationFilter = - new LoginAuthenticationFilter(authenticationManagerBean(), jwtTokenProvider, refreshTokenServiceImpl); + new LoginAuthenticationFilter(authenticationManagerBean(), jwtTokenProvider, refreshTokenServiceImpl, cookieProvider); loginAuthenticationFilter.setFilterProcessesUrl("/login"); http.csrf().disable(); @@ -45,7 +47,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { http.logout() .logoutUrl("/logout") - .deleteCookies(""); + .deleteCookies("refresh-token"); http.oauth2Login() .defaultSuccessUrl("http://just-pickup.com:8000/customer-frontend-service/") diff --git a/user-service/src/main/java/com/justpickup/userservice/global/utils/CookieProvider.java b/user-service/src/main/java/com/justpickup/userservice/global/utils/CookieProvider.java new file mode 100644 index 0000000..3641bf0 --- /dev/null +++ b/user-service/src/main/java/com/justpickup/userservice/global/utils/CookieProvider.java @@ -0,0 +1,36 @@ +package com.justpickup.userservice.global.utils; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseCookie; +import org.springframework.stereotype.Component; + +import javax.servlet.http.Cookie; + +@Component +public class CookieProvider { + + @Value("${token.refresh-expired-time}") + private String refreshTokenExpiredTime; + + public ResponseCookie createRefreshTokenCookie(String refreshToken) { + return ResponseCookie.from("refresh-token", refreshToken) + .httpOnly(true) + .secure(true) + .path("/") + .maxAge(Long.parseLong(refreshTokenExpiredTime)).build(); + } + + public ResponseCookie removeRefreshTokenCookie() { + return ResponseCookie.from("refresh-token", null) + .build(); + } + + public Cookie of(ResponseCookie responseCookie) { + Cookie cookie = new Cookie(responseCookie.getName(), responseCookie.getValue()); + cookie.setPath(responseCookie.getPath()); + cookie.setSecure(responseCookie.isSecure()); + cookie.setHttpOnly(responseCookie.isHttpOnly()); + cookie.setMaxAge((int) responseCookie.getMaxAge().getSeconds()); + return cookie; + } +} diff --git a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/utils/JwtTokenProvider.java b/user-service/src/main/java/com/justpickup/userservice/global/utils/JwtTokenProvider.java similarity index 75% rename from user-service/src/main/java/com/justpickup/userservice/domain/jwt/utils/JwtTokenProvider.java rename to user-service/src/main/java/com/justpickup/userservice/global/utils/JwtTokenProvider.java index ab6e357..2c8950a 100644 --- a/user-service/src/main/java/com/justpickup/userservice/domain/jwt/utils/JwtTokenProvider.java +++ b/user-service/src/main/java/com/justpickup/userservice/global/utils/JwtTokenProvider.java @@ -1,4 +1,4 @@ -package com.justpickup.userservice.domain.jwt.utils; +package com.justpickup.userservice.global.utils; import io.jsonwebtoken.*; @@ -16,12 +16,9 @@ import java.util.List; import java.util.UUID; @Component -@RequiredArgsConstructor @Slf4j public class JwtTokenProvider { - private final UserDetailsService userDetailsService; - @Value("${token.access-expired-time}") private long ACCESS_EXPIRED_TIME; @@ -60,29 +57,24 @@ public class JwtTokenProvider { .compact(); } - public Authentication getAuthentication(String email) { - UserDetails userDetails = userDetailsService.loadUserByUsername(email); - return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); - } - public String getUserId(String token) { - try { - return getClaimsFromJwtToken(token).getBody().getSubject(); - } catch (ExpiredJwtException expiredJwtException) { - return expiredJwtException.getClaims().getSubject(); - } + return getClaimsFromJwtToken(token).getSubject(); } - private Jws getClaimsFromJwtToken(String token) { - return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token); + private Claims getClaimsFromJwtToken(String token) { + try { + return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); + } catch (ExpiredJwtException e) { + return e.getClaims(); + } } public String getRefreshTokenId(String token) { - try { - return getClaimsFromJwtToken(token).getBody().get("value").toString(); - } catch (ExpiredJwtException expiredJwtException) { - return expiredJwtException.getClaims().get("value").toString(); - } + return getClaimsFromJwtToken(token).get("value").toString(); + } + + public List getRoles(String token) { + return (List) getClaimsFromJwtToken(token).get("roles"); } public boolean validateJwtToken(String token) { diff --git a/user-service/src/main/resources/application.yml b/user-service/src/main/resources/application.yml index 581c92a..4db3d79 100644 --- a/user-service/src/main/resources/application.yml +++ b/user-service/src/main/resources/application.yml @@ -26,6 +26,10 @@ spring: hibernate: default_batch_fetch_size: 1000 + redis: + host: 127.0.0.1 + port: 6379 + eureka: client: service-url: @@ -39,8 +43,6 @@ logging: level: com.justpickup: DEBUG - - # jpa query, parameter 로그 (p6spy) decorator.datasource.p6spy: enable-logging: true @@ -48,4 +50,6 @@ decorator.datasource.p6spy: token: access-expired-time: 3600000 refresh-expired-time: 604800000 - secret: my-secret \ No newline at end of file + secret: my-secret + refresh-token-name: refresh-token + access-token-name: access-token \ No newline at end of file