1. 遇到的問題
測試人員發現,在用戶查看自己的一個收藏功能時,顯示用戶沒有登錄。
2.問題原因
收藏服務A的一個功能,方法裏面調用了另外一個服務B的接口,由於還沒有做單點登錄系統,需要在同一個註冊中心上註冊的服務之間傳遞header參數裏面的一個token,導致服務B裏面的方法接受的請求header裏面沒有token,因此服務B的方法拋出異常【用戶未登錄】,然後把結果鏈式傳遞到了服務A,最終給用戶顯示【用戶未登錄】
3.解決思路
方法1.做一個單點登錄系統,目前人手不夠,還沒時間研究研究,
方法2.通過feign調用其他服務的時候,把服務A的header參數傳遞到服務B( 目前通過2解決掉)
4.步驟
①eureka註冊中心上現在有三個服務 :網關gateway-service,用戶user-service 收藏collect-service
②前端調用對應的服務其實都是經過了gateway-service,前置過濾器來判斷token信息,存儲到一次請求HttpServletRequest中了,並把token信息存儲到redis裏面緩存起來 ,gateway-service中的guolv之後存儲token信息邏輯如下:
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest req = ctx.getRequest();
String token = req.getHeader("token");
log.info("===========登錄令牌 = {}", token);
if (StringUtils.isNotBlank(token)) {
String json = stringRedisTemplate.opsForValue().get("token" + token);
log.info("===========登錄用戶緩存信息:{}", json);
if (StringUtils.isNotBlank(json)) {
ctx.setSendZuulResponse(true);
ctx.setResponseStatusCode(HttpStatus.SC_OK);
try {
ctx.addZuulRequestHeader("USER_REDIS_KEY",
URLEncoder.encode(JSONObject.toJSONString(json), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}
// 返回錯誤提示信息
log.error("===========請求失敗401");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
String responseBody = JSON.toJSON(Result.error(ResponseCode.NO_ACCESS)).toString();
ctx.setResponseBody(responseBody);
ctx.getResponse().setContentType("application/json;charset=UTF-8");
return null;
}
③由於之前在使用feign調用的其他服務時候,直接使用的對方的服務名來調用,沒有經過網關服務,其實想要通過feign之間 的服務調用經過網關,很簡單,把服務名都配置成網關服務名即可,讓eureka找到gateway-service後,然後讓gate-service來幫你找對應的實例 ,collect-service中 feignClient接口如下:
package com.client;
import com.util.Result;
import com.form.UserCollectCheckWxappForm;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
@FeignClient("gateway-service") //之前這裏寫的是user-service,換成網關服務gate-service,這樣就經過網關了
public interface UserServiceClient {
// 這裏想要調用的服務加上user-service即可
@PostMapping(value = "user-service/easysale/wxapp/collect/v1/checkHasCollect")
Result checkHasCollect(UserCollectCheckWxappForm userCollectCheckWxappForm);
}
④collect-service中 配置feign攔截器,使其傳遞header中token參數
package com.config;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.Objects;
@Slf4j
@Configuration
public class FeignConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
log.info("house-server, attributes:{}",attributes);
if (Objects.isNull(attributes)) return;
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
log.info("house-server, headerNames:{}",headerNames);
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
requestTemplate.header(name, values);
}
}
}
}
⑤還有一點別忘了,在yml文件配置熔斷策略,SEMAPHORE,這能保障在一次鏈路請求中........(其實這裏我還也還沒搞清楚,後續再聊。。。)
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 60000
⑥重啓網關服務gateway-service,和collect-service 測試ok,解決測試問題
5.後記
這個問題當時搞了我一下午,看來對這些框架原理還不是特別熟,不過能解決這個,還是有點小收穫滴。