지난 포스트까지 login 토큰을 발급해봤다. 이젠 로그인 이후 다른 서비스를 사용할 때 토큰 값이 유효한지 체크후에 진행할 수 있도록 gateway에서 처리해보자.
login-servie
에서는 자체적으로 filter로 거르고 있기 때문에 gateway에서 정확히 걸러지는지 확인하기 위해 어차피 order-service
를 생성해야되기 때문에 임의로 하나의 서비스를 생성해서 추가해놔보자.
선택한 옵션들은 다음과 같다.
server:
port: 0
spring:
application:
name: order-service
eureka:
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
설정 정보를 입력해주고
아직 config server에 등록하지 않고 사용하려면 gradle에서 config는 주석처리하고 사용하자
@SpringBootApplication
@EnableDiscoveryClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
Eureka에서 찾을수 있도록 설정해준다.
@RestController
@RequestMapping("/")
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello Order Service!";
}
}
이제 요청을 확인할 수 있도록 설정을 해주고
server:
port: 8000
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka
# token 추가해줘야함!!!!
token:
expiration_time: 86400000 #ms단위
secret: 토큰 값
spring:
application: #gateway service 이름름
name: gateway-service
cloud:
gateway: #gateway 설정
routes:
# login service
- id: login-service
uri: lb://LOGIN-SERVICE
predicates:
- Path=/login-service/**
- Method=GET, POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/login-service(?<segment>.*), /$\{segment}
# order service
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/order-service/**
- Method=GET, POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/order-service(?<segment>.*), /$\{segment}
gateway.yml
파일의 내용을 다음과 같이 설정해준다. order service를 추가해주었다.
그리고나서 테스트를 진행해보면 지금은 토큰 값이 없어도 정상적으로 처리되는 것을 확인할 수 있다. 이제 토큰이 없으면 처리 못하도록 수정해보자.
implementation 'io.jsonwebtoken:jjwt:0.9.1'
implementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.1'
의존성 2개를 추가하고
@Component
@Slf4j
@RequiredArgsConstructor
public class AuthFilter extends AbstractGatewayFilterFactory<AuthFilter.Config> {
private final Environment env;
public static class Config{
//설정에 필요한 내용 정의
}
//인증 요청시 확인
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain)->{
ServerHttpRequest request = exchange.getRequest();
if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
//헤더에 AUTHORIZATION key 자체가 존재하지 않을 경우
return onError(exchange, "no AUTHORIZATION header", HttpStatus.UNAUTHORIZED); //401 반환
}
String authorizationHeader = request.getHeaders().get(org.springframework.http.HttpHeaders.AUTHORIZATION).get(0); //AUTHORIZATION key 값으로 value 가져옴
String jwt = authorizationHeader.replace("Bearer", ""); //JWT, OAuth는 Bearer로 붙여서 전송하기로 약속함
if(!isJwtValid(jwt)){
return onError(exchange, "JWT Token is not valid", HttpStatus.UNAUTHORIZED);
}
return chain.filter(exchange);
});
}
//token이 유효한지 확인
private boolean isJwtValid(String jwt) {
boolean returnValue = true;
String subject = null;
try {
subject = Jwts.parser().setSigningKey(env.getProperty("token.secret")) //secret key 값을 통해 parse
.parseClaimsJws(jwt).getBody() //token의 내용을 가져옴
.getSubject();
}catch (Exception e){
returnValue = false;
}
if(subject == null || subject.equals("")){
returnValue = false;
}
return returnValue;
}
//에러 발생시 에러 값을 response
//Mono, Flux -> Spring WebFlux 개념 / 데이터 단위 단일=Mono, 복수=Flux
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
log.error(err);
return response.setComplete(); //Mono 데이터 return
}
}
filter를 추가하자. filter의 내용은 login-service
의 인증과 거의 비슷하지만 조금 다른 부분이라면 WebFlux의 Mon와 ServerHttp를 사용한다는 점이다. 아직 깊게 내용을 봐본적이 없어서 상세히 모르지만 꼭 학습해야할 부분이다!
server:
port: 8000
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka
token:
expiration_time: 86400000 #ms단위
secret: 토큰 값
spring:
application: #gateway service 이름름
name: gateway-service
cloud:
gateway: #gateway 설정
routes:
# login service
- id: login-service
uri: lb://LOGIN-SERVICE
predicates:
- Path=/login-service/**
- Method=GET, POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/login-service(?<segment>.*), /$\{segment}
# order service
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/order-service/**
- Method=GET, POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/order-service(?<segment>.*), /$\{segment}
- AuthFilter #토큰 처리 필터 적용
최종적으로 gateway의 설정 파일이 다음과 같은 내용으로 변경되어야한다.
그럼 이제는 401로 반환하면서
gateway에서는 우리가 설정해놓은 에러 메세지를 출력해준다.
회원가입 후
로그인하여 발급받은 토큰을
적용시켜서 정상적으로 반환받는 것을 확인할 수 있었다!