feat(user-service): logout 기능, cookie 기능 추가, redis 설정 추가

- logout 기능 추가
- cookie 기능 추가
- redis 설정 추가
This commit is contained in:
bum12ark
2022-02-17 16:11:35 +09:00
parent 47faea59e4
commit 01538317f7
17 changed files with 246 additions and 79 deletions

View File

@@ -38,6 +38,7 @@ dependencies {
compileOnly 'org.springframework.boot:spring-boot-starter-oauth2-client' compileOnly 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.cloud:spring-cloud-starter-config' implementation 'org.springframework.cloud:spring-cloud-starter-config'
/*implementation 'org.springframework.kafka:spring-kafka'*/ /*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 // https://mvnrepository.com/artifact/com.github.gavlyukovskiy/p6spy-spring-boot-starter
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.0' implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.0'

View File

@@ -3,9 +3,9 @@ package com.justpickup.userservice.domain.jwt.exception;
import com.justpickup.userservice.global.exception.CustomException; import com.justpickup.userservice.global.exception.CustomException;
import org.springframework.http.HttpStatus; 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); super(HttpStatus.FORBIDDEN, message);
} }
} }

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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<RefreshToken, String> {
}

View File

@@ -5,4 +5,5 @@ import com.justpickup.userservice.domain.user.dto.JwtTokenDto;
public interface RefreshTokenService { public interface RefreshTokenService {
void updateRefreshToken(Long id, String refreshToken); void updateRefreshToken(Long id, String refreshToken);
JwtTokenDto refreshJwtToken(String accessToken, String refreshToken); JwtTokenDto refreshJwtToken(String accessToken, String refreshToken);
void logoutToken(String accessToken);
} }

View File

@@ -1,15 +1,21 @@
package com.justpickup.userservice.domain.jwt.service; 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.dto.JwtTokenDto;
import com.justpickup.userservice.domain.user.entity.User; import com.justpickup.userservice.domain.user.entity.User;
import com.justpickup.userservice.domain.user.exception.NotExistUserException; import com.justpickup.userservice.domain.user.exception.NotExistUserException;
import com.justpickup.userservice.domain.user.repository.UserRepository; import com.justpickup.userservice.domain.user.repository.UserRepository;
import com.justpickup.userservice.domain.jwt.utils.JwtTokenProvider;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; 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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -21,16 +27,18 @@ import java.util.stream.Collectors;
@Transactional(readOnly = true) @Transactional(readOnly = true)
@Slf4j @Slf4j
public class RefreshTokenServiceImpl implements RefreshTokenService { public class RefreshTokenServiceImpl implements RefreshTokenService {
private final UserRepository userRepository; private final UserDetailsService userDetailsService;
private final JwtTokenProvider jwtTokenProvider; private final JwtTokenProvider jwtTokenProvider;
private final UserRepository userRepository;
private final RefreshTokenRedisRepository refreshTokenRedisRepository;
@Transactional @Transactional
@Override @Override
public void updateRefreshToken(Long id, String refreshToken) { public void updateRefreshToken(Long id, String uuid) {
User user = userRepository.findById(id) User user = userRepository.findById(id)
.orElseThrow(() -> new NotExistUserException("사용자 고유번호 : " + id + "는 없는 사용자입니다.")); .orElseThrow(() -> new NotExistUserException("사용자 고유번호 : " + id + "는 없는 사용자입니다."));
user.changeRefreshToken(refreshToken); refreshTokenRedisRepository.save(RefreshToken.of(user.getId().toString(), uuid));
} }
@Transactional @Transactional
@@ -38,36 +46,51 @@ public class RefreshTokenServiceImpl implements RefreshTokenService {
public JwtTokenDto refreshJwtToken(String accessToken, String refreshToken) { public JwtTokenDto refreshJwtToken(String accessToken, String refreshToken) {
String userId = jwtTokenProvider.getUserId(accessToken); String userId = jwtTokenProvider.getUserId(accessToken);
User user = userRepository.findById(Long.valueOf(userId)) RefreshToken findRefreshToken = refreshTokenRedisRepository.findById(userId)
.orElseThrow(() -> new NotExistUserException("사용자 고유번호 : " + userId + "는 없는 사용자입니다.")); .orElseThrow(()
-> new RefreshTokenNotValidException("사용자 고유번호 : " + userId + "는 등록된 리프레쉬 토큰이 없습니다.")
);
// refresh token 검증 // refresh token 검증
if (!jwtTokenProvider.validateJwtToken(refreshToken)) { String findRefreshTokenId = findRefreshToken.getRefreshTokenId();
// 익셉션 발생 - 로그 아웃 후 로그인 페이지로 이동 처리 if (!jwtTokenProvider.validateJwtToken(refreshToken) ||
user.deleteRefreshToken(); !jwtTokenProvider.equalRefreshTokenId(findRefreshTokenId, refreshToken)) {
throw new TokenRefreshException("Not validate jwt token = " + refreshToken);
refreshTokenRedisRepository.delete(findRefreshToken);
throw new RefreshTokenNotValidException("Not validate jwt token = " + refreshToken);
} }
String userRefreshTokenId = user.getRefreshTokenId(); User findUser = userRepository.findById(Long.valueOf(userId))
if (!jwtTokenProvider.equalRefreshTokenId(userRefreshTokenId, refreshToken)) { .orElseThrow(() -> new NotExistUserException("유저 고유 번호 : " + userId + "는 없는 유저입니다."));
// 익셉션 발생 - 로그인 아웃 후 로그인 페이지로 이동 처리
user.deleteRefreshToken();
throw new TokenRefreshException("Not equal jwt token! user = " + userRefreshTokenId +
", refreshToken = " + refreshToken);
}
Authentication authentication = jwtTokenProvider.getAuthentication(user.getEmail()); // access token 생성
Authentication authentication = getAuthentication(findUser.getEmail());
List<String> roles = authentication.getAuthorities() List<String> roles = authentication.getAuthorities()
.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); .stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
String newAccessToken = jwtTokenProvider.createJwtAccessToken(userId, "/refreshToken", roles); String newAccessToken = jwtTokenProvider.createJwtAccessToken(userId, "/refreshToken", roles);
String newRefreshToken = jwtTokenProvider.createJwtRefreshToken();
user.changeRefreshToken(newRefreshToken);
return JwtTokenDto.builder() return JwtTokenDto.builder()
.accessToken(newAccessToken) .accessToken(newAccessToken)
.refreshToken(newRefreshToken) .refreshToken(refreshToken)
.build(); .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());
}
} }

View File

@@ -3,23 +3,29 @@ package com.justpickup.userservice.domain.jwt.web;
import com.justpickup.userservice.domain.jwt.service.RefreshTokenServiceImpl; import com.justpickup.userservice.domain.jwt.service.RefreshTokenServiceImpl;
import com.justpickup.userservice.domain.user.dto.JwtTokenDto; import com.justpickup.userservice.domain.user.dto.JwtTokenDto;
import com.justpickup.userservice.global.dto.Result; import com.justpickup.userservice.global.dto.Result;
import com.justpickup.userservice.global.utils.CookieProvider;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.ws.rs.core.HttpHeaders;
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class AuthController { public class AuthController {
private final RefreshTokenServiceImpl refreshTokenServiceImpl; private final RefreshTokenServiceImpl refreshTokenServiceImpl;
private final CookieProvider cookieProvider;
@GetMapping("/refreshToken") @GetMapping("/refreshToken")
public ResponseEntity<Result> refreshToken(@RequestHeader("X-AUTH-TOKEN") String accessToken, public ResponseEntity<Result> refreshToken(@RequestHeader("X-AUTH-TOKEN") String accessToken,
@@ -27,7 +33,11 @@ public class AuthController {
JwtTokenDto jwtTokenDto = refreshTokenServiceImpl.refreshJwtToken(accessToken, refreshToken); 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 @Data
@@ -35,19 +45,22 @@ public class AuthController {
@AllArgsConstructor @AllArgsConstructor
static class RefreshTokenResponse { static class RefreshTokenResponse {
private String accessToken; private String accessToken;
private String refreshToken;
public RefreshTokenResponse(JwtTokenDto jwtTokenDto) { public RefreshTokenResponse(JwtTokenDto jwtTokenDto) {
this.accessToken = jwtTokenDto.getAccessToken(); this.accessToken = jwtTokenDto.getAccessToken();
this.refreshToken = jwtTokenDto.getRefreshToken();
} }
} }
@PostMapping("/logout") @PostMapping("/logout")
public ResponseEntity<Result> logout(@RequestHeader("X-AUTH-TOKEN") String accessToken, public ResponseEntity<Result> logout(@RequestHeader("X-AUTH-TOKEN") String accessToken,
@RequestHeader("REFRESH-TOKEN") String refreshToken) { @RequestHeader("REFRESH-TOKEN") String refreshToken) {
log.info("########### logout!");
// TODO: 2022/02/16 logout 구현 필요 refreshTokenServiceImpl.logoutToken(accessToken);
return ResponseEntity.ok(Result.createSuccessResult("success"));
ResponseCookie refreshCookie = cookieProvider.removeRefreshTokenCookie();
return ResponseEntity.status(HttpStatus.OK)
.header(HttpHeaders.SET_COOKIE, refreshCookie.toString())
.body(Result.createErrorResult(""));
} }
} }

View File

@@ -1,6 +1,5 @@
package com.justpickup.userservice.domain.user.entity; package com.justpickup.userservice.domain.user.entity;
import com.justpickup.userservice.domain.user.dto.StoreOwnerDto;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -15,8 +14,8 @@ public class StoreOwner extends User {
private String businessNumber; private String businessNumber;
public StoreOwner(String email, String password, String name, String phoneNumber, public StoreOwner(String email, String password, String name, String phoneNumber,
String businessNumber, String refreshTokenId) { String businessNumber) {
super(email, password, name, phoneNumber, refreshTokenId); super(email, password, name, phoneNumber);
this.businessNumber = businessNumber; this.businessNumber = businessNumber;
} }
} }

View File

@@ -12,7 +12,7 @@ import javax.persistence.*;
@Inheritance(strategy = InheritanceType.JOINED) @Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE") @DiscriminatorColumn(name = "DTYPE")
@Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User extends BaseEntity { public abstract class User extends BaseEntity {
@Id @GeneratedValue @Id @GeneratedValue
@Column(name = "user_id") @Column(name = "user_id")
@@ -26,8 +26,6 @@ public class User extends BaseEntity {
private String phoneNumber; private String phoneNumber;
private String refreshTokenId;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private Role role; private Role role;
@@ -42,19 +40,11 @@ public class User extends BaseEntity {
this.role = role; 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.email = email;
this.password = password; this.password = password;
this.name = name; this.name = name;
this.phoneNumber = phoneNumber; this.phoneNumber = phoneNumber;
this.refreshTokenId = refreshTokenId;
} }
public void changeRefreshToken(String refreshToken) {
this.refreshTokenId = refreshToken;
}
public void deleteRefreshToken() {
this.refreshTokenId = null;
}
} }

View File

@@ -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<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}

View File

@@ -1,8 +1,13 @@
package com.justpickup.userservice.global.exception; 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.dto.Result;
import com.justpickup.userservice.global.utils.CookieProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
@@ -11,9 +16,12 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice @RestControllerAdvice
@RequiredArgsConstructor
@Slf4j @Slf4j
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
private final CookieProvider cookieProvider;
@ExceptionHandler(CustomException.class) @ExceptionHandler(CustomException.class)
public ResponseEntity customExceptionHandler(CustomException ce) { public ResponseEntity customExceptionHandler(CustomException ce) {
HttpStatus status = ce.getStatus(); HttpStatus status = ce.getStatus();
@@ -23,6 +31,16 @@ public class GlobalExceptionHandler {
.body(errorResult); .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) @ExceptionHandler(BindException.class)
public ResponseEntity bindExceptionHandler(BindException exception) { public ResponseEntity bindExceptionHandler(BindException exception) {
return getValidationErrorBody(exception); return getValidationErrorBody(exception);

View File

@@ -2,10 +2,13 @@ package com.justpickup.userservice.global.security;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.justpickup.userservice.domain.jwt.service.RefreshTokenServiceImpl; 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.LoginRequest;
import com.justpickup.userservice.global.dto.Result;
import com.justpickup.userservice.global.utils.CookieProvider;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseCookie;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@@ -16,6 +19,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
@@ -32,6 +36,7 @@ public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFil
private final AuthenticationManager authenticationManager; private final AuthenticationManager authenticationManager;
private final JwtTokenProvider jwtTokenProvider; private final JwtTokenProvider jwtTokenProvider;
private final RefreshTokenServiceImpl refreshTokenServiceImpl; private final RefreshTokenServiceImpl refreshTokenServiceImpl;
private final CookieProvider cookieProvider;
// login 리퀘스트 패스로 오는 요청을 판단 // login 리퀘스트 패스로 오는 요청을 판단
@Override @Override
@@ -69,14 +74,20 @@ public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFil
refreshTokenServiceImpl.updateRefreshToken(Long.valueOf(userId), jwtTokenProvider.getRefreshTokenId(refreshToken)); refreshTokenServiceImpl.updateRefreshToken(Long.valueOf(userId), jwtTokenProvider.getRefreshTokenId(refreshToken));
Map<String, String> tokens = Map.of( // 쿠키 설정
"access_token", accessToken, ResponseCookie refreshTokenCookie = cookieProvider.createRefreshTokenCookie(refreshToken);
"refresh_token", refreshToken
); Cookie cookie = cookieProvider.of(refreshTokenCookie);
response.setContentType(APPLICATION_JSON_VALUE); response.setContentType(APPLICATION_JSON_VALUE);
response.addCookie(cookie);
new ObjectMapper().writeValue(response.getOutputStream(), tokens); // body 설정
Map<String, String> tokens = Map.of(
"access_token", accessToken
);
new ObjectMapper().writeValue(response.getOutputStream(), Result.createSuccessResult(tokens));
} }
@Override @Override

View File

@@ -1,8 +1,9 @@
package com.justpickup.userservice.global.security; package com.justpickup.userservice.global.security;
import com.justpickup.userservice.domain.jwt.service.RefreshTokenServiceImpl; 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.domain.user.service.UserService;
import com.justpickup.userservice.global.utils.CookieProvider;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
@@ -23,6 +24,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final BCryptPasswordEncoder bCryptPasswordEncoder; private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final JwtTokenProvider jwtTokenProvider; private final JwtTokenProvider jwtTokenProvider;
private final RefreshTokenServiceImpl refreshTokenServiceImpl; private final RefreshTokenServiceImpl refreshTokenServiceImpl;
private final CookieProvider cookieProvider;
private final UserService userService; private final UserService userService;
@@ -34,7 +36,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
LoginAuthenticationFilter loginAuthenticationFilter = LoginAuthenticationFilter loginAuthenticationFilter =
new LoginAuthenticationFilter(authenticationManagerBean(), jwtTokenProvider, refreshTokenServiceImpl); new LoginAuthenticationFilter(authenticationManagerBean(), jwtTokenProvider, refreshTokenServiceImpl, cookieProvider);
loginAuthenticationFilter.setFilterProcessesUrl("/login"); loginAuthenticationFilter.setFilterProcessesUrl("/login");
http.csrf().disable(); http.csrf().disable();
@@ -45,7 +47,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
http.logout() http.logout()
.logoutUrl("/logout") .logoutUrl("/logout")
.deleteCookies(""); .deleteCookies("refresh-token");
http.oauth2Login() http.oauth2Login()
.defaultSuccessUrl("http://just-pickup.com:8000/customer-frontend-service/") .defaultSuccessUrl("http://just-pickup.com:8000/customer-frontend-service/")

View File

@@ -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;
}
}

View File

@@ -1,4 +1,4 @@
package com.justpickup.userservice.domain.jwt.utils; package com.justpickup.userservice.global.utils;
import io.jsonwebtoken.*; import io.jsonwebtoken.*;
@@ -16,12 +16,9 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
@Component @Component
@RequiredArgsConstructor
@Slf4j @Slf4j
public class JwtTokenProvider { public class JwtTokenProvider {
private final UserDetailsService userDetailsService;
@Value("${token.access-expired-time}") @Value("${token.access-expired-time}")
private long ACCESS_EXPIRED_TIME; private long ACCESS_EXPIRED_TIME;
@@ -60,29 +57,24 @@ public class JwtTokenProvider {
.compact(); .compact();
} }
public Authentication getAuthentication(String email) {
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
}
public String getUserId(String token) { public String getUserId(String token) {
try { return getClaimsFromJwtToken(token).getSubject();
return getClaimsFromJwtToken(token).getBody().getSubject();
} catch (ExpiredJwtException expiredJwtException) {
return expiredJwtException.getClaims().getSubject();
}
} }
private Jws<Claims> getClaimsFromJwtToken(String token) { private Claims getClaimsFromJwtToken(String token) {
return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token); try {
return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
} catch (ExpiredJwtException e) {
return e.getClaims();
}
} }
public String getRefreshTokenId(String token) { public String getRefreshTokenId(String token) {
try { return getClaimsFromJwtToken(token).get("value").toString();
return getClaimsFromJwtToken(token).getBody().get("value").toString(); }
} catch (ExpiredJwtException expiredJwtException) {
return expiredJwtException.getClaims().get("value").toString(); public List<String> getRoles(String token) {
} return (List<String>) getClaimsFromJwtToken(token).get("roles");
} }
public boolean validateJwtToken(String token) { public boolean validateJwtToken(String token) {

View File

@@ -26,6 +26,10 @@ spring:
hibernate: hibernate:
default_batch_fetch_size: 1000 default_batch_fetch_size: 1000
redis:
host: 127.0.0.1
port: 6379
eureka: eureka:
client: client:
service-url: service-url:
@@ -39,8 +43,6 @@ logging:
level: level:
com.justpickup: DEBUG com.justpickup: DEBUG
# jpa query, parameter 로그 (p6spy) # jpa query, parameter 로그 (p6spy)
decorator.datasource.p6spy: decorator.datasource.p6spy:
enable-logging: true enable-logging: true
@@ -48,4 +50,6 @@ decorator.datasource.p6spy:
token: token:
access-expired-time: 3600000 access-expired-time: 3600000
refresh-expired-time: 604800000 refresh-expired-time: 604800000
secret: my-secret secret: my-secret
refresh-token-name: refresh-token
access-token-name: access-token