FefulClient с состоянием (сеанс между клиентом и сервером) - PullRequest
0 голосов
/ 22 октября 2018

Я боролся с @FeignClient для Spring Cloud.У нас есть корневая WAR «шлюза», которая вызывает «служебные» сервисы WAR, где мы кэшируем метаданные, относящиеся к роли пользователя (эти данные должны быть достаточно большими, чтобы запрашивать их при каждом вызове, и достаточно маленькими, чтобы хранить их в оперативной памяти).

Мне нужно, чтобы клиент мог обрабатывать Set-Cookie, а затем помещать JSESSIONID на сторону сервера, пока сеанс клиента еще активен.Так что я считаю, что мне нужно хранить все идентификаторы сеанса на стороне сервера в некотором bean-объекте в области сеанса для конкретного пользователя шлюза.Например,

"client1" => "8D06349922CD77D1CE68F78F4FAE04C5" 
"client2" => "another session id"

. Пройдя по Интернету, я нашел решения для ApacheHttpClient и Spring RestTemplate.Единственное, что есть в Feign, - это возможность получить @RequestHeader («Cookie») в качестве аргумента для удаленной функции.

Что ж, я написал несколько грубый код, который добился цели:

HttpMessageConverter httpMessageConverter = new HttpMessageConverter() {
            @Override
            public Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
                HttpHeaders headers = inputMessage.getHeaders();
                logger.info("headers: {}", headers);
                List<String> setCookie = headers.get("Set-Cookie");
                logger.info("setCookie: {}", setCookie);
                if(setCookie != null){
                    String jsId = setCookie.get(0).split(";")[0].split("=")[1];
                    logger.info("jsId Object read: {}", jsId);
                    cookies.setSessionId("key", jsId);
                }
                return delegate.read(clazz,inputMessage);
            }
};

.....

RequestInterceptor jsessionFeignRequestInterceptor = new RequestInterceptor() {
    @Override
    public void apply(RequestTemplate template) {
        String sessionId = cookies.getSessionId("key");
        logger.info("THE JSESSIONID: {}", sessionId);
        if(sessionId != null) {
            template.header("Cookie", "JSESSIONID=" + sessionId);
        }
    }
};

....

 Feign.builder()
    .encoder(new SpringEncoder(new HttpMessageConverters(httpMessageConverter)))
    .requestInterceptor(jsessionFeignRequestInterceptor)
    .requestInterceptor(oauth2FeignRequestInterceptor)........

Однако, на мой взгляд, это выглядит странно.

Возможно, есть какой-то другой "Подходящий вариант "для достижения аналогичной функциональности?

ПРИМЕЧАНИЕ: мы используем OAuth2 Spring Auth Server.

Спасибо.

1 Ответ

0 голосов
/ 28 мая 2019

Я сделал нечто подобное, используя OkHttpClient с пользовательским перехватчиком.

Builder builder = Feign
    .builder()
    .client(new OkHttpClient(new okhttp3.OkHttpClient.Builder().addInterceptor(new SessionIdRequestInterceptor()).build()));

OkHttpClient также поддерживает интерфейс Authenticator, но он вызывается только при статусе 401 http.Так как мой сервер аутентификации перенаправляет на страницу входа с 200 или 302, я должен был сделать пользовательскую проверку аутентификации, следовательно, перехватчик.Вот моя реализация:

class SessionIdRequestInterceptor implements okhttp3.Interceptor {

    @Override
    public okhttp3.Response intercept(Chain chain) throws IOException {
        okhttp3.Request authenticatedRequest = authenticateRequest(chain.request());
        okhttp3.Response response = chain.proceed(authenticatedRequest);
        if (isAuthenticated(response)) {
            saveSessionId(response);
        } else if (jSessionId != null) {
            jSessionId = null;
            response.body().close();
            authenticatedRequest = authenticateRequest(authenticatedRequest);
            response = chain.proceed(authenticatedRequest);
        }
        if (!isAuthenticated(response)) {
            throw new PermissionDeniedException("Failed Authentication");
        }
        return response;
    }

    private void saveSessionId(okhttp3.Response response) {
        String setCookie = response.header("Set-Cookie");
        if (setCookie != null) {
            jSessionId = setCookie.split(";")[0].split("=")[1];
        }
    }

    private boolean isAuthenticated(okhttp3.Response response) {
        return !response.request().url().encodedPath().contains("/cas-server/login");
    }

    private okhttp3.Request authenticateRequest(okhttp3.Request request) {
        okhttp3.Request.Builder builder = request.newBuilder();
        if (jSessionId != null) {
            builder.addHeader("Cookie", "JSESSIONID=" + jSessionId);
        } else {
            builder
                .addHeader("Authorization", "Basic " + Base64
                    .getEncoder()
                    .encodeToString((configuration.getUser() + ":" + configuration.getClearPassword()).getBytes()));
        }
        return builder.build();
    }
}
...