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.后记
这个问题当时搞了我一下午,看来对这些框架原理还不是特别熟,不过能解决这个,还是有点小收获滴。