• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

갑자기 127.0.0.1:8000/user-service/** 가 작동하지 않습니다..

22.08.29 17:02 작성 조회수 397

1

안녕하세요 현재 Microservice간 통신의

RestTemplate 사용 강의를 듣던중에 포스트맨으로 테스트를 하는데

POST 방식 요청인

127.0.0.1:8000/user-service/users

127.0.0.1:8000/user-service/login 은 잘됩니다.

<br>

하지만 GET 방식 요청인

127.0.0.1:8000/user-service/users

127.0.0.1:8000/user-service/users/welcome

127.0.0.1:8000/user-service/users/health_check

127.0.0.1:8000/user-service/users/{userId} 가 먹히지 않습니다.

즉 디버깅 브레이크 포인트도 안걸리고 그냥 401 에러가 발생합니다...

즉,

127.0.0.1:8000/user-service/login 으로 얻은 response의 Header에 있는 token을가지고

요청할 때 Bearer Token에 넣어도 401 에러가 발생합니다...

난감합니다...

갑자기 잘 되다가 왜이런지 모르겠네요..

분명 apigateway-service의 application.yml에

get방식은 AuthorizationHeaderFilter 를 걸어줬습니다..

- id: user-service
  uri: lb://USER-SERVICE
  predicates: # 조건절이다.
    - Path=/user-service/**
    - Method=GET
  filters:
    - RemoveRequestHeader=Cookie
    - RewritePath=/user-service/(?<segment>.*), /$\{segment}
    - AuthorizationHeaderFilter

<br>

그런데 정말 이해가 안가는것이

get 방식으로

127.0.0.1:8000/user-service/** 요청을 하면 Bearer Token을 정상적으로 넣어도 401 에러가 발생하는데

get 방식으로 user-service의 자체 포트번호를 넣고

127.0.0.1:67026/** 이런식으로 요청하면 BearerToken을 잘못 넣어도 정상적으로 작동합니다...

대체 왜이런거죠??

<br>

참고로 현재 저는 대칭키 방식을 이용한 암호화를 사용하고 있습니다.(비대칭키 암호화는 적용하지 않았습니다.)

그런데 이것은 user-service에서 get방식으로 요청하는데 문제가 있어보이지는 않습니다..

어떻게 하면 get방식에서 401 에러를 발생하지 않도록 할 수 있을까요?

아니면 더 확인해야할 정보가 뭐가 있을까요?

감사합니다.

<br>

현재

apigateway-service의 application.yml이 아래와 같이 되있고

spring:
  cloud:
    config:
      uri: http://127.0.0.1:8888
      name: ecommerce # ecommerce.yml 파일을 가져온다.

native에 있는 ecommerce.yml 은 다음과 같습니다.

token:
  expiration_time: 86400000
  secret: user_token_native_ecommerce

gateway:
  ip: 192.168.45.163

참고로 token.secret이 user_token_native_ecommerce는 해당 ecommerce.yml 파일 밖에 없기 때문에 경로는 확실합니다. ( 확실히 config-service 의 application의 search-locations 경로 안에 ecommerce.yml 파일이 있습니다.)

대체 뭐가 문제여서 get 방식으로

127.0.0.1:8000/user-service/** 요청이 401 에러가 발생하는 것일까요?

<br>

혹시나해서 UserController도 같이 올려봅니다.

@RestController
@RequestMapping("/")
public class UserController {

    private Environment env;
    private UserService userService;

    @Autowired
    private Greeting greeting;

    @Autowired
    public UserController(Environment env, UserService userService) {
        this.env = env;
        this.userService = userService;
    }

    @GetMapping("/health_check")
    public String status() {
        return String.format("It's Working in User Service"
                + ", port(local.server.port)="+ env.getProperty("local.server.port")
                + ", port(server.port)="+ env.getProperty("server.port")
                + ", token secret="+ env.getProperty("token.secret")
                + ", token expiration time="+ env.getProperty("token.expiration_time"));

    }

    @GetMapping("/welcome")
    public String welcome() {   
        //return env.getProperty("greeting.message");
        return greeting.getMessage();
    }

    @PostMapping("/users") // 제네릭타입으로 반환할 형태도 명시할 수 있다.
    public ResponseEntity<ResponseUser> createUser(@RequestBody RequestUser user) {
        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);

        // RequestUser를 UserDto로 매핑한다.
        UserDto userDto = mapper.map(user, UserDto.class);
        userService.createUser(userDto);

        // userDto 를 ResponseUser 형으로 변경한다.
        ResponseUser responseUser = mapper.map(userDto, ResponseUser.class);

        // rest api 식으로 반환하자
        return ResponseEntity.status(HttpStatus.CREATED).body(responseUser); // 201번 성공코드 반환
    }

    @GetMapping("/users")
    public ResponseEntity<List<ResponseUser>> getUsers() {
        Iterable<UserEntity> userList = userService.getUserByAll();

        List<ResponseUser> result = new ArrayList<>();
        userList.forEach(v -> {
            result.add(new ModelMapper().map(v, ResponseUser.class));
        }); // v를 ResponseUser 클래스로 변경

        return ResponseEntity.status(HttpStatus.OK).body(result);
    }

    @GetMapping("/users/{userId}")
    public ResponseEntity<ResponseUser> getUser(@PathVariable("userId") String userId) {
        UserDto userDto = userService.getUserByUserId(userId);

        ResponseUser returnValue = new ModelMapper().map(userDto, ResponseUser.class);

        return ResponseEntity.status(HttpStatus.OK).body(returnValue);
    }
}

답변 1

답변을 작성해보세요.

0

안녕하세요, 이도원입니다.

발생하신 문제에 대해 잘 정리해서 올려 주셨네요. 원인을 정확하게는 알기 어렵지만, user-service에 직접 요청하는 작업은 실행되나, apigateway-service를 거쳐 실행하는 서비스는 401 오류가 발생한다는 것 같습니다. Token 자체의 문제라기 보다는 user-service에서 apygateway-service로 부터 전달되는 요청 처리가 안되는 거 같습니다. 현 상황으로 확인해 보실 수 있는 부분은, user-service의 WebSecurity에서 apigateway-service의 IP를 확인해 보시면 좋을 것 같습니다. WebSecurity 작업 시 아래 처럼, 요청 URI Pattern 및 IP를 제약하는 부분이 있어서, 여기에서 문제가 생길 것 일수도 있을 것 같습니다.

image

추가로, BearerToken을 잘못 넣어도 정상적으로 작동 되었다는 부분은, 잘못 된 Token을 사용하였다는 의미인가요? 아니면, 현재 발급 된 Token이 아닌, 이전 Token을 사용하셨다는 건가요? 만약 이전 Token을 사용하는 문제였다면, Token 자체가 지급 발급된 것인지에 대해서는 검증하지 않고, 유효한지만 검증하고 있기 때문에, 새로운 Token을 사용하지 않아도 정상 처리가 될 수 있습니다.

작업하시는데 계속 문제가 발생하면, user-service, apigateway-service 코드를 압축하여 아래 이메일로 보내주시면, 제 환경에서도 테스트 해 보도록 하겠습니다.

edowon0623@gmail.com

감사합니다.

리자몽님의 프로필

리자몽

질문자

2022.08.30

천절한 답변 감사합니다.

제가 메일을 보냈는데 조금 성급하게 보냈습니다..

디버깅하데 에러를 검색하는데 문제를 해결했습니다.

디버깅 하다가 user-service의 get방식 요청으로 토큰 검사하는데

io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.

에러가 나왔습니다. 에러를 검색해보니까

각 마이크로 서비스의 secret_token 값을 같게 해줘야 되는데 다르게 설정되어 있어서 문제가 발생했습니다.

현재 apigateway-service는 ecommerce.yml 을 가져오게 설정이 돼있었고

spring:
  cloud:
    config:
      uri: http://127.0.0.1:8888
      name: ecommerce # ecommerce.yml 파일을 가져온다.

user-service는 user-service.yml을 가져오도록 설정이 돼있었습니다.

spring:
  cloud:
    config:
      uri: http://127.0.0.1:8888
      name: user-service # user-service.yml 파일을 가져온다.

하지만 user-service.yml파일과

ecommerce.yml 파일의 token.secret 값이 달랐습니다..

token:
  expiration_time: 86400000
  secret: user_token_native_user_service

이 token.secret 값을 같게하니까

기존에 apigateway-service를 사용해서 user-service를 get방식으로 요청하는건 이상없이 잘 되고 있습니다.

결론은 마이크로서비스(user-service)가 apigateway를 거쳐가려면

마이크로서비스와 apigateway의 token의 secret 값을 같게 해줘야 되는군요

채널톡 아이콘