지돌이의 블로그 입니다!

저는 Spring-boot에서 jsonrpc4j Client을 사용할 때 서버쪽에서 Exception을 throw하면 Client쪽에서는 제대로 된 Exception을 받지 못하고 UndeclaredThrowableException이 발생되며 해당 UndeclaredThrowableException의 getCause()을 통해 진짜 Exception을 받아와도 진짜 Exception Class으로 Casting하려고 하면 ClassCastException이 발생했습니다.


이에 대한 원인은 RPC을 호출하는 쪽(Spring-boot)의 ClassLoader는 RestartClassLoader인데 반에

jsonrpc4j에서 Exception을 resovle하는 쪽은 LauncherClassLoader이었기 때문이었습니다.


이를 해결하기 위해 Custom ExceptionResolver을 생성해서 사용하고 있습니다.


public <T> T getInterface(Class<T> interfaceClass, URL url) {
final ExceptionResolver exceptionResolver = new MyExceptionResolver(getClass().getClassLoader());
JsonRpcHttpClient jsonRpcHttpClient
= new JsonRpcHttpClient(url);
jsonRpcHttpClient.setExceptionResolver(exceptionResolver);
return ProxyUtil.createClientProxy(getClass().getClassLoader(), interfaceClass, jsonRpcHttpClient);
}

public class MyExceptionResolver extends DefaultExceptionResolver {
private final ClassLoader classLoader;

public MyExceptionResolver(ClassLoader classLoader) {
super();
this.classLoader = classLoader;
}

@Override
protected Class<? extends Throwable> resolveThrowableClass(String typeName) throws ClassNotFoundException {
Class<?> clazz
;
try {
clazz = Class.
forName(typeName, true, this.classLoader);
if (!Throwable.class.isAssignableFrom(clazz)) {
//logger.warn("Type does not inherit from Throwable {}", clazz.getName());
} else {
return clazz.asSubclass(Throwable.class);
}
}
catch(ClassNotFoundException e) {
//logger.warn("Unable to load Throwable class {}", typeName);
throw e;
} catch(Exception e) {
//logger.warn("Unable to load Throwable class {}", typeName);
}
return null;
}
}


Comment +0

 spring-cloud-starter-netflix-zuul 을 이용하여 Gateway 사용시 매번 Request마다


Set-Cookie: SESSION=YjlmYjEzNzgtZTAzYy00NjEzLWJkYzItYTY5YTFiY2I3NDQy
Set-Cookie: SESSION=NDg1NGYwZDktYTM5ZC00YjBjLThiNDMtZjA0ZWZkYmFjYjQ1

이런식으로 세션 쿠키가 한개 이상이 매번 새로 생성되는 문제가 있었습니다..


일단 설정은...

sessionCreationPolicy은 Gateway에서만 ALWAYS으로, 나머지 Endpoint service에서는 NEVER으로 되어있고


Gateway에서 Zuul Filter으로 아래와 같이 Session ID을 넘겨주도록 했습니다.


public class SessionCookieFilter extends ZuulFilter {

@Autowired
private SessionRepository sessionRepository;

@Override
public String filterType() {
return "pre";
}

@Override
public int filterOrder() {
return 0;
}

@Override
public boolean shouldFilter() {
return true;
}

@Override
public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
HttpSession httpSession = request.getSession();
Session session = sessionRepository.findById(httpSession.getId());

context.addZuulRequestHeader("Cookie", "SESSION=" + httpSession.getId());
return null;
}
}


Spring-boot 소스를 뒤지고 뒤져.. 어디서 이런 현상이 발생하는지 찾아보았는데 (사실 처음엔 Zuul문제인 줄 알았습니다..ㅠㅠ 그래서..)


RequestContext의 addZuulResponseHeader에 브레이크포인터를 걸어놓고 디버깅 해 보았는데


SimpleHostRoutingFilter의 forward 자체에서 Set-Cookie 헤더가 날라왔습니다..


결국 End-point service 애플리케이션에서 Set-Cookie을 했다는 건데 찾아보니


SessionRepositoryFilter의 getRequestedSession 메서드에서  

private S getRequestedSession() {
if (!this.requestedSessionCached) {
List<String> sessionIds = SessionRepositoryFilter.this.httpSessionIdResolver
.resolveSessionIds(this);

resolveSessionIds 에서 세션 ID을 읽어오지 못했습니다.


@Override
public List<String> readCookieValues(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
List<String> matchingCookieValues = new ArrayList<>();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (this.cookieName.equals(cookie.getName())) {
String sessionId = (this.useBase64Encoding
? base64Decode(cookie.getValue()) : cookie.getValue());
if (sessionId == null) {
continue;
}

보니까 useBase64Encoding이 true라서 인코딩되지 않은 상태로 Gateway에서 날라온 SESSION cookie가 문제였습니다..


결국 SessionCookieFilter 에서 Base64 인코딩해서 던저줍니다~


(근데도 아직 가끔 중간에 랜덤하게? Set-Cookie으로 세션이 다시 생성되는 문제가...)


이런..

Comment +0

spring-boot-admin내부에서 dependency가 제대로 설정되어있지 않는듯 하다...



    implementation('org.springframework.boot:spring-boot-starter-reactor-netty')


추가해주면 해결된다...^^



Comment +0

1. @EnableResourceServer 어노테이션을 설정하면 ResourceServerConfiguration.class 을 Import하게 됩니다.



* 어디선가  authenticationManager가 OAuth2AuthenticationManager 클래스로 설정됩니다.


2. tokenService와 tokenStore을 가져옵니다.



3. 인증시

OAuth2AuthenticationProcessingFilter -> doFilter ->

  tokenExtractor = new BearerTokenExtractor();

  Authentication authentication = tokenExtractor.extract(request); // new PreAuthenticatedAuthenticationToken(tokenValue, "");

  if (authentication != null)

    Authentication authResult = authenticationManager.authenticate(authentication);


여기서 authenticationManager는 OAuth2AuthenticationManager인거 같음








Comment +0