SpringSecurity/JWT - MSA환경: Refactoring하기

2024. 4. 9. 02:24· BackEnd
목차
  1. 0. 프로젝트 설명
  2. 1. ApiGateway 패턴의 필요성
  3. 2. ApiGateway 구현 방식 : WebFlux와 Reactor
  4. 2-1. 🤔고민...
  5. 3. yml 파일 설정!
  6. 3-1. 원격 저장소에 있는 yml 파일
  7. 3-2. 로컬에 있는 yml 파일
  8. 4. 전체 로직
  9. 4-1. 로직 고민
  10. 4-2. 전체적인 로직 도식화
  11. 5. 로그아웃/토큰 재발급 처리에 대한 고민
  12. 5-1. 결론
  13. 6. 권한 처리에 대한 고민
  14. 7. 개발 전 환경 설정
  15. 7-1. [apiGateway-service]: GlobalFilter
  16. 7-2. [apiGateway-service]: SecurityConfig
  17. 7-3. [apiGateway-service]: RedisConfig, RedisUtil
  18. 7-4. MS단 : CorsConfig 수정
  19. 8. [user-service] : 회원가입 로직,  로그인 로직, 토큰 재발급 로직
  20. 9. [apiGateway-service] : AccessToken 인가 로직
  21. 9-1. [apiGateway-service] : JwtUtil 작성
  22. 9-2. [apiGateway-service] : AuthorizationHeaderFilter
  23. 9-3. Test
  24. 10. [apiGateway-service / user-service] : 로그아웃 로직
  25. 10-1. [apiGateway-service.yml] : router 설정하기
  26. 10-2. 로직 복습
  27. 10-3. [apiGateway-service] : CustomLogoutFilter
  28. 10-4. [user-service] : JwtLogoutFilter 수정
  29. 10-5. Test
반응형

해당 포스트 (1) ~ (7) 과정의 코드를 리펙토링 한 것이다!

 

Spring Boot에서 JWT 프로젝트 세팅하기 / Filter 테스트 (1)

VERSION springboot : '3.2.3' java : '17' 1. build.gradle에 Dependecy 추가 plugins { id 'java' id 'org.springframework.boot' version '3.2.3' id 'io.spring.dependency-management' version '1.1.4' } group = 'com.example' version = '0.0.1-SNAPSHOT' java { s

jinhos-devlog.tistory.com

수정한 부분만 설명하겠다!

 

0. 프로젝트 설명

해당 구조로 이루어지며,

- config-service에서 원격저장소에 있는 YML 파일을 가져올 것이다.

- username -> email로 대체되었다.

 

정도만 알고 시작해보자.

1. ApiGateway 패턴의 필요성

ApiGateway의 본래 목적은

클라이언트 애플리케이션과 마이크로서비스들 사이에 위치하여 요청을 서비스로 라우팅하는 역방향 프록시로써 사용되는 것.


내부 마이크로서비스의 엔드포인트로 리디렉션! 또한 인가, SSL 종료 및 캐시와 같은 공통 관심사를 처리하는 기능을 제공할 수 있다.

그래서 우리는 "인가" 과정만 API Gateway에서 처리해주도록 결정했다!

 

2. ApiGateway 구현 방식 : WebFlux와 Reactor

자바로 웹 기반 애플리케이션을 개발하기 위해서는 서블릿(Servlet)과 서블릿 컨테이너(Servlet container)를 사용한다!

(우리가 지금까지 했다 Java-Spring이다!)

기존 Spring MVC 모델에 비동기(asynchronous)와 넌블럭킹 I/O(non-blocking I/O) 처리를 맡기려면 너무 큰 변화가 필요했기 때문에

리액티브 프로그래밍(Reactive programming)을 지원하는 새로운 웹 프레임워크를 제공했다!

 

Spring Cloud Gateway는 WebFlux와 Reactor 프로젝트를 기반으로 만들어진 비동기적인 API Gateway이다.
기존의 Spring MVC와는 아예 다른 개념, Servlet 기반의 프로젝트와는 차이가 있다.
netty 서버를 기반으로 동작하며, 이를 통해 비동기적이고 빠른 HTTP 통신을 처리할 수 있다.

따라서 다른 프레임워크를 사용하더라도 비동기적으로 동작하는 마이크로서비스를 구현할 수 있다.
이러한 전제 하에 Spring Cloud Gateway는 스프링에 의존적이지 않고, 다양한 프레임워크와의 통합을 지원한다.

 

2-1. 🤔고민...

그동안 사용하던 Spring MVC에서의 servlet 기반 프로젝트와는 아예 다른 개념이다...

아직 Java-Spring도 잘 못하는데... 이게 무슨 난관인가 싶었다ㅠ

 

마이크로서비스를 위한 API Gateway는 늘어난 Request를 빨리빨리 처리하기위해

nonblocking & aysnchronus 하게 돌아갈 필요가 있다.

 

그래서.. 다른 차선책을 발견했는데..

Spring Cloud Gateway MVC라는 것도 있더라!

 

허나, 우리는 SPOF의 문제도 고려하여 Api Gateway의 성능 개선을 놓칠 수 없었다..

또한, 우리 뒷단 MS 들이 MVC 단의 서비스가 아닌 다른 것이 들어올 수도 있다는 확장성을 고려해서

... Spring Cloud Gateway를 사용하기로 결정했다!



3. yml 파일 설정!

3-1. 원격 저장소에 있는 yml 파일

- database-apiGateway-dev.yml


      
spring:
cloud:
gateway:
default-filters:
- name: GlobalFilter
args:
baseMessage: Spring Cloud Gateway Global Filter
preLogger: true
postLogger: true
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user/signup
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user/(?<segment>.*), /$\{segment}
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user/login
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user/(?<segment>.*), /$\{segment}
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user/reissue
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user/(?<segment>.*), /$\{segment}
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user/logout
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user/(?<segment>.*), /$\{segment}
- AuthorizationHeaderFilter
- CustomLogoutFilter
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user/**
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user/(?<segment>.*), /$\{segment}
- AuthorizationHeaderFilter
RewritePath=/api/(?<segment>.*), /$\{segment}
/api/test 로 요청이 들어올 경우
/api/를 빼고 /test 만 전달
로드밸런서는 기본적으로 라운드 로빈을 사용한다.

- jwt-dev.yml


      
spring:
jwt:
# HS512 알고리즘을 사용할 것이기 때문에 512bit, 즉 64byte 이상의 secret key를 사용
secret: testSecretKey20240316testSecretKey20240316testSecretKey20240316
token:
access-expiration-time: 3600000
refresh-expiration-time: 86400000

해당 jwt.yml은 "user"와, "apiGateway" 서비스 단에만 불러올 것이다.

- redis-dev.yml


      
spring:
data:
redis:
host: localhost
port: 6379

3-2. 로컬에 있는 yml 파일

- apiGateway-service 단의 bootstrap.yml


      
server:
port: 8000
spring:
application:
name: apiGateway-service
profiles:
active: dev
cloud:
config:
uri: http://localhost:8888
name: database-apiGateway, redis, jwt
kafka:
bootstrap-servers: "localhost:9092"
consumer:
group-id: "GroupId"
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka

- user-service 단의 bootstrap.yml


      
server:
port: 8080
spring:
application:
name: user-service # micro service unique ID
profiles:
active: dev
cloud:
config:
uri: http://localhost:8888
name: database-user,redis, jwt
kafka:
bootstrap-servers: "localhost:9092"
consumer:
group-id: "GroupId"
eureka:
client:
register-with-eureka: true # register to eureka server
fetch-registry: true # Getting instances' information from eureka server
service-url:
defaultZone : http://127.0.0.1:8761/eureka #Eureka Server's address

4. 전체 로직

4-1. 로직 고민

API Gateway에서 사용자 인증 및 권한 부여와 같은 보안 기능을 구현.

우리는 권한은 따로 설정하지 않았다.
jwt 토큰에 권한은 인코딩 해두었으니, 필요한 경우 꺼내서 처리하면 될 것 같다.

 

API Gateway로 완전히 옮기기보다는,

API Gateway에서 필요한 요청 전처리(인가)와 백엔드 서비스로의 전달을 담당하는 것이 적절하다고 판단

어차피 endpoint가 User- Service 와야 하며,

User-DB가 필요한

/signup, /login, /reissue 등은 User-Service에서 처리 하기로 했다!

4-2. 전체적인 로직 도식화

 

엑세스토큰을 "신분증"이라고 예를 들면,

어차피 "발급", "재발급", "폐지" 등은 신원확인을 해야하는 [동사무소(User-Service)]에 가서 해야한다.

고로, 해당 로직에 관련된 필터는 [동사무소(User-Service)] 앞 단에 두었다.

 

  • 저, CustomLogoutFilter는 뭐야....???
  • 왜, LofoutFilter가 두 개로 나눠져있어?..
  • 등등은 아래에서 설명하겠다 ㅠㅠ

 

5. 로그아웃/토큰 재발급 처리에 대한 고민

 

해당 글을 쓸 때, [이미 발급된 토큰의 상태 변화] 에 대해 매우 고민을 많이 했다...

 

JWT 토큰 서버 구축하기 (인가/인증) (6) - 로그아웃 처리

JWT 토큰 서버 구축하기 (인가/인증) (5) - RefreshToken으로 토큰 재발급 JWT 토큰 서버 구축하기 (인가/인증) (4) - 인가 처리 필터 JWT 토큰 서버 구축하기 (인가/인증) (3) - 엑세스토큰 발급하기 JWT 토큰

jinhos-devlog.tistory.com

 

그런데, MSA 구조에서..? 이 상태변화를 도대체 어떻게 처리해??????ㅠㅠㅠㅠㅠㅠ 너무 복잡하다..

 

5-1. 결론

1. 로그아웃 처리

기존 JwtLogoutFilter에서 해당 역할을 하였다.

- AccessToken을 블랙리스트 처리하여 redis에 저장한다.

- AccessToken에서 추출한 username을 key로 가지고 있는 value(RefreshToken)을 삭제한다.

 

여기에서

AccessToken을 블랙리스트 처리하여 redis에 저장한다.

부분을 apiGateway로 위임함으로써 이를 해결했다!

 

2. 토큰 재발급

어차피, /reissue에서 토큰 valid 검사는 해주므로, 필터 단의 인가 과정은 모두 풀어주기로 결정했다!

 

 

6. 권한 처리에 대한 고민

해당 경로별 인가 작업은 삭제했다. (우리 서비스단에는 ROLE_USER만 쓰이기 때문)

만약에 필요하다면,

filter를 하나 만들고 apiGateway단에 달아주자!

해당 filter에서 jwt토큰 내의 권한을 꺼내서 작업해주자.

 

apiGateway-service의 router설정에서 AuthorizationHeaderFilter(인가 필터)를 활용하여

대부분 처리할 수 있었다.

모든 MS단의 filter에서는 권한을 열어줬다.
Api-Gateway에서 경로별 인가처리

인가 처리가 필요한 경로에는 AuthorizationHeaderFilter를 달아주고,

필요없는 경로는 달아주지 않았다.

(전체 로직 도식화 참고)

 


7. 개발 전 환경 설정

7-1. [apiGateway-service]: GlobalFilter

위의 yml을 보면

      default-filters:
        - name: GlobalFilter

부분이 있다.


      
@Component
@Slf4j
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
public GlobalFilter() {
super(Config.class);
}
public static class Config {
//Configuration 정보
}
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> {
//Global PRE Filter Start ===========================================================
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
log.info("Global PRE Filter : request id : {}", request.getId());
//Global PRE Filter End =============================================================
//Global POST Filter Start ==========================================================
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
//Mono : WebFlux 비동기 방식 서버 단일값 전달
log.info("Global POST Filter : Response Code : {}",
response.getStatusCode());
})
//Global POST Filter End =============================================================
);
});
}
}
  • 요청과 관련된 작업/ 응답과 관련된 작업이다.
  • CustomFilter 클래스를 사용하여 Spring Cloud Gateway에서 요청과 응답을 조작하고 필터링해준다.
  • (뒤로 요청 보내주고, 앞으로 응답 보내주고!)
  • (큰 역할은 없다..ㅎ)

7-2. [apiGateway-service]: SecurityConfig


      
@Configuration // IoC 빈(bean)을 등록
@EnableWebFluxSecurity // 필터 체인 관리 시작 어노테이션
@RequiredArgsConstructor
public class SecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.csrf(ServerHttpSecurity.CsrfSpec::disable);
return http.build();
}
}

- CORS 문제가 뜬다면 해당 블로그 참고 : https://yoo-dev.tistory.com/4

- API-Gateway에서 처리해주면, 뒷 단 MS에서는 CORS 설정 지워줘야 한다.

7-3. [apiGateway-service]: RedisConfig, RedisUtil

  • RedisConfig와 RedisUtil을 추가하여 RedisTemplete를 사용할 수 있도록 해주자.

7-4. MS단 : CorsConfig 수정

  • 이 또한, API-Gateway로 위임하는 것이 사실 맞다... (보류)
  • CorsConfig에 allowedOriginPatterns.add("http://localhost:8080"); 추가

      
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CorsConfig implements WebMvcConfigurer {
public static CorsConfigurationSource apiConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
ArrayList<String> allowedOriginPatterns = new ArrayList<>();
allowedOriginPatterns.add("http://localhost:8000");
allowedOriginPatterns.add("http://localhost:8080");
allowedOriginPatterns.add("http://localhost:3000");
ArrayList<String> allowedHttpMethods = new ArrayList<>();
allowedHttpMethods.add("GET");
allowedHttpMethods.add("POST");
allowedHttpMethods.add("PUT");
allowedHttpMethods.add("DELETE");
configuration.setAllowedOrigins(allowedOriginPatterns);
configuration.setAllowedMethods(allowedHttpMethods);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}

8. [user-service] : 회원가입 로직,  로그인 로직, 토큰 재발급 로직

기존과 동일하다! (상단 포스팅 참고)

 


9. [apiGateway-service] : AccessToken 인가 로직

 

9-1. [apiGateway-service] : JwtUtil 작성


      
@Slf4j
@Component
public class JwtUtil {
private final SecretKey secretKey;
public JwtUtil(
@Value("${spring.jwt.secret}") String secret) {
secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8),
Jwts.SIG.HS256.key().build().getAlgorithm());
}
// JWT 토큰을 입력으로 받아 토큰의 subject에서 사용자 이메일(email)을 추출
public String getEmail(String token) throws io.jsonwebtoken.security.SignatureException {
return Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(token)
.getPayload()
.getSubject();
}
public Long getExpTime(String token) {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration()
.getTime();
}
// AccessToken 유효성 검사
public boolean validateAccessToken(String token) {
try {
// 구문 분석 시스템의 시계가 JWT를 생성한 시스템의 시계 오차 고려
// 약 3분 허용.
long seconds = 3 *60;
boolean isExpired = Jwts
.parser()
.clockSkewSeconds(seconds)
.verifyWith(secretKey)
.build()
.parseSignedClaims(token)
.getPayload()
.getExpiration()
.before(new Date());
if (isExpired) {
log.info("만료된 JWT 토큰입니다.");
}
// Jwt 통과
log.info("[*] Token Valid");
return !isExpired;
} catch (SecurityException | MalformedJwtException e) {
log.info("잘못된 JWT 서명입니다.");
} catch (ExpiredJwtException e) {
log.info("만료된 JWT 토큰입니다.");
} catch (UnsupportedJwtException e) {
log.info("지원되지 않는 JWT 토큰입니다.");
} catch (IllegalArgumentException e) {
log.info("JWT 토큰이 잘못되었습니다.");
}
return false;
}
}

9-2. [apiGateway-service] : AuthorizationHeaderFilter


      
@Slf4j
@Component
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config> {
private final JwtUtil jwtUtil;
private final RedisUtil redisUtil;
public AuthorizationHeaderFilter(JwtUtil jwtUtil, RedisUtil redisUtil) {
super(Config.class);
this.jwtUtil = jwtUtil;
this.redisUtil = redisUtil;
}
// GatewayFilter 설정을 위한 Config 클래스
public static class Config {
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// HTTP 요청 헤더에서 Authorization 헤더를 가져옴
HttpHeaders headers = request.getHeaders();
if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
return onError(exchange, "HTTP 요청 헤더에 Authorization 헤더가 포함되어 있지 않습니다.", HttpStatus.UNAUTHORIZED);
}
String authorizationHeader = Objects.requireNonNull(headers.get(HttpHeaders.AUTHORIZATION)).get(0);
// JWT 토큰 가져오기
String accessToken = authorizationHeader.replace("Bearer ", "");
log.info("[*] Token exists");
// JWT 토큰 유효성 검사
jwtUtil.validateAccessToken(accessToken);
// logout 처리된 accessToken
if (redisUtil.get(accessToken) != null && redisUtil.get(accessToken).equals("logout")) {
log.info("[*] Logout accessToken");
return onError(exchange, "로그아웃된 토큰입니다.", HttpStatus.UNAUTHORIZED);
}
// JWT 토큰에서 사용자 email 추출
String subject = jwtUtil.getEmail(accessToken);
// 사용자 email를 HTTP 요청 헤더에 추가하여 전달
ServerHttpRequest newRequest = request.mutate()
.header("email", subject)
.build();
return chain.filter(exchange.mutate().request(newRequest).build());
};
}
// Mono(단일 값), Flux(다중 값) -> Spring WebFlux
private Mono<Void> onError(ServerWebExchange exchange, String errorMsg, HttpStatus httpStatus) {
log.error(errorMsg);
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
return response.setComplete();
}
}
  • 그저... 똑같은 코드를 옮긴 것 뿐
  • Mono로 응답하는 부분만 조금 찾아보자!
  • 인가처리가 필요한 경로에 yml에 Filter를 추가해주자!

9-3. Test

Code 돌아가는 순서
log를 통해 확인 가능하다!

 


10. [apiGateway-service / user-service] : 로그아웃 로직

10-1. [apiGateway-service.yml] : router 설정하기

  • apiGateway-service단에서 AuthorizationHeaderFilter와 CustomLogoutFilter를 추가

10-2. 로직 복습

- AccessToken을 블랙리스트 처리하여 redis에 저장한다. 

  -> CustomLogoutFilter에서 처리

 

- AccessToken에서 추출한 username을 key로 가지고 있는 value(RefreshToken)을 삭제한다.

  -> 기존 JwtLogoutFilter에서 여전히 처리

 

10-3. [apiGateway-service] : CustomLogoutFilter


      
@Slf4j
@Component
public class CustomLogoutFilter extends AbstractGatewayFilterFactory<CustomLogoutFilter.Config> {
private final JwtUtil jwtUtil;
private final RedisUtil redisUtil;
public CustomLogoutFilter(JwtUtil jwtUtil, RedisUtil redisUtil) {
super(CustomLogoutFilter.Config.class);
this.jwtUtil = jwtUtil;
this.redisUtil =redisUtil;
}
// GatewayFilter 설정을 위한 Config 클래스
public static class Config {
}
@Override
public GatewayFilter apply(CustomLogoutFilter.Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// HTTP 요청 헤더에서 Authorization 헤더를 가져옴
HttpHeaders headers = request.getHeaders();
if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
return onError(exchange, "No authorization header", HttpStatus.UNAUTHORIZED);
}
String authorizationHeader = Objects.requireNonNull(headers.get(HttpHeaders.AUTHORIZATION)).get(0);
// JWT 토큰 판별
String accessToken = authorizationHeader.replace("Bearer ", "");
log.info("[*] Token exists");
// Logout 블랙리스트 - Redis에 저장
redisUtil.save(
accessToken,
"logout",
jwtUtil.getExpTime(accessToken),
TimeUnit.MILLISECONDS
);
// 사용자 ID를 HTTP 요청 헤더에 추가하여 전달
ServerHttpRequest newRequest = request.mutate()
.build();
return chain.filter(exchange.mutate().request(newRequest).build());
};
}
// Mono(단일 값), Flux(다중 값) -> Spring WebFlux
private Mono<Void> onError(ServerWebExchange exchange, String errorMsg, HttpStatus httpStatus) {
log.error(errorMsg);
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
return response.setComplete();
}
}

10-4. [user-service] : JwtLogoutFilter 수정


      
@RequiredArgsConstructor
@Slf4j
public class JwtLogoutFilter implements LogoutHandler {
private final RedisUtil redisUtil;
private final JwtUtil jwtUtil;
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
try {
log.info("[*] Logout Filter");
String accessToken = jwtUtil.resolveAccessToken(request);
String email = jwtUtil.getEmail(accessToken);
// RefreshToken 삭제
redisUtil.delete(email);
} catch (ExpiredJwtException e) {
log.warn("[*] case : accessToken expired");
try {
HttpResponseUtil.setErrorResponse(response, HttpStatus.UNAUTHORIZED, "세션이 만료되었습니다. 다시 로그인하세요");
} catch (IOException ex) {
log.error("IOException occurred while setting error response: {}", ex.getMessage());
}
}
}
}
  • 마저 RefreshToken 삭제하는 부분을 수행해주자.

10-5. Test

 

 

 

아직, 에러핸들러나 응답을 통일화하지 못했다. 해당 부분을 나중에 수정하겠다!

참고 자료 : 

https://imprint.tistory.com/216

https://velog.io/@tlatldms/API-Gateway-Refresh-JWT-%EC%9D%B8%EC%A6%9D%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-Spring-boot-Spring-Cloud-Gateway-Redis-mysql-JPA-1%ED%8E%B8

 

 

반응형
저작자표시 (새창열림)
  1. 0. 프로젝트 설명
  2. 1. ApiGateway 패턴의 필요성
  3. 2. ApiGateway 구현 방식 : WebFlux와 Reactor
  4. 2-1. 🤔고민...
  5. 3. yml 파일 설정!
  6. 3-1. 원격 저장소에 있는 yml 파일
  7. 3-2. 로컬에 있는 yml 파일
  8. 4. 전체 로직
  9. 4-1. 로직 고민
  10. 4-2. 전체적인 로직 도식화
  11. 5. 로그아웃/토큰 재발급 처리에 대한 고민
  12. 5-1. 결론
  13. 6. 권한 처리에 대한 고민
  14. 7. 개발 전 환경 설정
  15. 7-1. [apiGateway-service]: GlobalFilter
  16. 7-2. [apiGateway-service]: SecurityConfig
  17. 7-3. [apiGateway-service]: RedisConfig, RedisUtil
  18. 7-4. MS단 : CorsConfig 수정
  19. 8. [user-service] : 회원가입 로직,  로그인 로직, 토큰 재발급 로직
  20. 9. [apiGateway-service] : AccessToken 인가 로직
  21. 9-1. [apiGateway-service] : JwtUtil 작성
  22. 9-2. [apiGateway-service] : AuthorizationHeaderFilter
  23. 9-3. Test
  24. 10. [apiGateway-service / user-service] : 로그아웃 로직
  25. 10-1. [apiGateway-service.yml] : router 설정하기
  26. 10-2. 로직 복습
  27. 10-3. [apiGateway-service] : CustomLogoutFilter
  28. 10-4. [user-service] : JwtLogoutFilter 수정
  29. 10-5. Test
'BackEnd' 카테고리의 다른 글
  • SpringSecurity/JWT - 인증 과정에서 발생하는 Exception 처리
  • PUT vs PATCH !? 일부 수정하고싶어... (Boolean, Optional, JsonNullable, MapStruct)
  • JWT 토큰 서버 구축하기 (인가/인증) (7) - 전체 기능 테스트
  • JWT 토큰 서버 구축하기 (인가/인증) (6) - 로그아웃 처리
dog-pawwer
dog-pawwer
성장 중 🌱🌱
dog-pawwer
지노개발일기
dog-pawwer
전체
오늘
어제
  • 분류 전체보기 (117)
    • FrontEnd (4)
      • Android (4)
    • BackEnd (22)
    • Cloud (15)
    • Trouble Shooting (2)
    • Computer Science (52)
      • CS 개인 공부 (19)
      • 알고리즘 (코딩테스트) (1)
      • 프로그래밍언어론 (15)
      • 분산시스템 (5)
      • 정보처리기사 (개인공부용) (3)
    • 강의 (18)
      • 자바-스프링부트-서버개발 (8)
      • UMC (Study) (9)
      • 스프링 부트와 JPA (1)
    • 🚨ERROR (4)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • GitHub

공지사항

인기 글

태그

  • RestAPI
  • 카카오
  • 스프링부트
  • oauth
  • 오어스
  • 카카오 로그인
  • 카카오 로그인 구현
  • java
  • 9-0
  • kakao
  • springboot

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.1
dog-pawwer
SpringSecurity/JWT - MSA환경: Refactoring하기
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.