微服務架構的拆分,各模塊之間使用feign組件來進行相互http轉發通信。
前端與后端之間使用springcloud的網關來進行協調。
現在問題出現,用戶的信息如何進行傳遞?
前端請求攜帶請求頭,請求頭中的authorization為攜帶的對應token,這個token如何轉發給各個微服務模塊?
具體流程:前端發送請求->網關進行解析->從springcloud攔截器的exchange.header中解析出token->將token寫入到新的exchange.header中,并且命名為info,這個exchange會被向后傳遞(因為本身springcloud就有多個攔截器,該攔截器處理完之后會傳遞給下一個攔截器 )->根據網關解析的端口號,發送request到對應端口的微服務中->微服務的攔截器對request進行攔截(實際上是一個共有模塊,各個微服務依賴于這個模塊,所以其網絡請求會被該模塊攔截)->讀取傳遞過來請求頭中的info信息,存入threadLocal中。
1.由于所有前端請求都會到達springcloud的攔截器,并且攔截器有多個,只需在攔截器中將需要傳遞的“info”存進網絡請求中,對應的攔截器定義如下:
package com.hxy.hmgateway.filter;import com.hxy.hmgateway.config.AuthProperties;
import com.hxy.hmgateway.utils.JwtTool;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.List;@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final AuthProperties authProperties;private final JwtTool jwtTool;private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();//放行排除路徑String path = request.getPath().toString();if(isExclude(path,authProperties.getExcludePaths())){return chain.filter(exchange);}//1.獲取tokenHttpHeaders headers = request.getHeaders();List<String> tokenList = headers.get("authorization");//2.判別token是否為空String token = null;if (tokenList!=null && !tokenList.isEmpty()){token = tokenList.get(0);}//3.獲取userIdLong userId = null;//4.判別token是否正確try {userId = jwtTool.parseToken(token);} catch (Exception e) {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}//TODO userId的轉發System.out.println("userId:"+userId);String userInfo = String.valueOf(userId);ServerWebExchange swe = exchange.mutate().request(builder ->builder.header("info", userInfo)).build();//5.放行return chain.filter(swe);}private boolean isExclude(String path, List<String> excludePaths) {for (String pathPattern : excludePaths) {if(antPathMatcher.match(pathPattern, path)) return true;}return false;}@Overridepublic int getOrder() {return 0;}
}
?注意這里的exchange.mutate指令用于將info存儲在網絡請求中。
2.該攔截器經過springcloud網關轉發后,首先到達所有微服務公共依賴的模塊,這個模塊會使用springmvc攔截器,用于攔截,info,并將其存入threadlocal中。代碼如下;
package com.hmall.common.intercepter;import com.hmall.common.utils.UserContext;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class UserInfoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String info = request.getHeader("info");if (info == null) return true;Long userId = Long.valueOf(info);UserContext.setUser(userId);return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {UserContext.removeUser();}
}
package com.hmall.common.config;import com.hmall.common.intercepter.UserInfoInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserInfoInterceptor());}
}
為了使得其余微服務能夠掃描到該共有模塊的包,需要將攔截器對應的包添加在如下文件中:
對應的spring.factories文件如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hmall.common.config.MyBatisConfig,\com.hmall.common.config.MvcConfig,\com.hmall.common.config.JsonConfig
最后需要注意,在mvc配置文件中,添加了一個注解
ConditionalOnClass,用于標記僅對使用了該類的模塊生效