feat(customer vue, user-service): customer vue OAuth 인증
- customer vue 인증페이지 구현 - customer OAuth 로그인 구현 - user-service success handling 구현
This commit is contained in:
@@ -31,6 +31,8 @@ dependencies {
|
||||
// https://mvnrepository.com/artifact/io.netty/netty-resolver-dns-native-macos
|
||||
implementation 'io.netty:netty-resolver-dns-native-macos:4.1.68.Final:osx-aarch_64'
|
||||
|
||||
implementation 'javax.xml.bind:jaxb-api:2.3.1'
|
||||
|
||||
compileOnly 'org.projectlombok:lombok'
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
annotationProcessor 'org.projectlombok:lombok'
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
package com.justpickup.customerapigatewayservice.filter;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.justpickup.customerapigatewayservice.security.JwtTokenProvider;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config> {
|
||||
@@ -44,9 +51,7 @@ public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<Auth
|
||||
// JWT 토큰 판별
|
||||
String token = authorizationHeader.replace("Bearer", "");
|
||||
|
||||
if (!jwtTokenProvider.validateJwtToken(token)) {
|
||||
return onError(exchange, "JWT token is not valid", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
jwtTokenProvider.validateJwtToken(token);
|
||||
|
||||
String subject = jwtTokenProvider.getUserId(token);
|
||||
if (false == jwtTokenProvider.getRoles(token).contains("Customer")) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.justpickup.customerapigatewayservice.filter;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
|
||||
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
|
||||
|
||||
public GlobalFilter(){
|
||||
super(Config.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Config config) {
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest(); // reactive포함된거로 import
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
|
||||
log.info("Global com.example.scg.filter baseMessgae: {}", config.getBaseMessage());
|
||||
|
||||
// Global pre Filter
|
||||
if (config.isPreLogger()){
|
||||
log.info("Global Filter Start: request id -> {}" , request.getId());
|
||||
log.info("Global Filter Start: request path -> {}" , request.getPath());
|
||||
}
|
||||
|
||||
// Global Post Filter
|
||||
//Mono는 webflux에서 단일값 전송할때 Mono값으로 전송
|
||||
return chain.filter(exchange).then(Mono.fromRunnable(()->{
|
||||
|
||||
if (config.isPostLogger()){
|
||||
log.info("Global Filter End: response statuscode -> {}" , response.getStatusCode());
|
||||
}
|
||||
}));
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Config {
|
||||
private String baseMessage;
|
||||
private boolean preLogger;
|
||||
private boolean postLogger;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.justpickup.customerapigatewayservice.handler;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
|
||||
List<Class<? extends RuntimeException>> jwtExceptions =
|
||||
List.of(SignatureException.class,
|
||||
MalformedJwtException.class,
|
||||
UnsupportedJwtException.class,
|
||||
IllegalArgumentException.class);
|
||||
Class<? extends Throwable> exceptionClass = ex.getClass();
|
||||
|
||||
Map<String, Object> responseBody = new HashMap<>();
|
||||
if (exceptionClass == ExpiredJwtException.class) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||
responseBody.put("code", "EXPIRED");
|
||||
responseBody.put("message", "Access Token is Expired!");
|
||||
} else if (jwtExceptions.contains(exceptionClass)){
|
||||
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||
responseBody.put("code", "INVALID");
|
||||
responseBody.put("message", "Invalid Access Token");
|
||||
}else{
|
||||
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||
responseBody.put("code", "INVALID");
|
||||
}
|
||||
|
||||
DataBuffer wrap = null;
|
||||
try {
|
||||
byte[] bytes = objectMapper.writeValueAsBytes(responseBody);
|
||||
wrap = exchange.getResponse().bufferFactory().wrap(bytes);
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return exchange.getResponse().writeWith(Flux.just(wrap));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -73,25 +73,12 @@ public class JwtTokenProvider {
|
||||
return (List<String>) getClaimsFromJwtToken(token).get("roles");
|
||||
}
|
||||
|
||||
public boolean validateJwtToken(String token) {
|
||||
public void validateJwtToken(String token) {
|
||||
try {
|
||||
Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
|
||||
return true;
|
||||
} catch (SignatureException e) {
|
||||
log.error("Invalid JWT signature: {}", e.getMessage());
|
||||
return false;
|
||||
} catch (MalformedJwtException e) {
|
||||
log.error("Invalid JWT token: {}", e.getMessage());
|
||||
return false;
|
||||
} catch (ExpiredJwtException e) {
|
||||
log.error("JWT token is expired: {}", e.getMessage());
|
||||
return false;
|
||||
} catch (UnsupportedJwtException e) {
|
||||
log.error("JWT token is unsupported: {}", e.getMessage());
|
||||
return false;
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("JWT claims string is empty: {}", e.getMessage());
|
||||
return false;
|
||||
} catch (SignatureException | MalformedJwtException |
|
||||
UnsupportedJwtException | IllegalArgumentException | ExpiredJwtException jwtException) {
|
||||
throw jwtException;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,25 @@ spring:
|
||||
|
||||
cloud:
|
||||
gateway:
|
||||
default-filters:
|
||||
- name: GlobalFilter
|
||||
args:
|
||||
baseMessage: Spring Cloud Gateway Global Filter
|
||||
preLogger: true
|
||||
postLogger: true
|
||||
globalcors:
|
||||
cors-configurations:
|
||||
'[/**]':
|
||||
allowedOrigins: "http://just-pickup.com:8080"
|
||||
allowedMethods:
|
||||
- GET
|
||||
- POST
|
||||
- DELETE
|
||||
- PUT
|
||||
- OPTIONS
|
||||
- DELETE
|
||||
allowedHeaders: '*'
|
||||
allow-credentials: true
|
||||
routes:
|
||||
- id: owner-frontend-service
|
||||
uri: lb://CUSTOMER-FRONTEND-SERVICE
|
||||
@@ -33,6 +52,7 @@ spring:
|
||||
predicates:
|
||||
- Path=/store-service/**
|
||||
filters:
|
||||
- AuthorizationHeaderFilter
|
||||
- RewritePath=/store-service/(?<segment>.*),/$\{segment}
|
||||
- id: user-service
|
||||
uri: lb://USER-SERVICE
|
||||
@@ -55,6 +75,13 @@ spring:
|
||||
- Method=POST
|
||||
filters:
|
||||
- RewritePath=/user-service/(?<segment>.*),/$\{segment}
|
||||
- id: user-service
|
||||
uri: lb://USER-SERVICE
|
||||
predicates:
|
||||
- Path=/user-service/auth/reissue
|
||||
- Method=GET
|
||||
filters:
|
||||
- RewritePath=/user-service/(?<segment>.*),/$\{segment}
|
||||
- id: user-service
|
||||
uri: lb://USER-SERVICE
|
||||
predicates:
|
||||
|
||||
Reference in New Issue
Block a user