我一直在与@FeignClient争夺Spring Cloud。 我们有根“网关” WAR来调用“后端”服务WAR,我们在其中缓存用户角色特定的元数据(该数据应该足够大,可以在每次调用时请求它,而要足够小,可以将其存储在RAM内存中)。>
我需要客户端能够处理Set-Cookie,然后在客户端会话处于活动状态时将JSESSIONID放到服务器端。因此,我相信我需要为特定的网关用户将所有服务器端的sessionIds存储在某个会话范围的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。
谢谢。
答案 0 :(得分:0)
通过将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();
}
}