课程名称:Spring Cloud / Alibaba 微服务架构实战
课程章节:第7章-缓存 HTTP 请求 Body 的全局过滤器
课程讲师:张勤一
课程内容:
1. 缓存 HTTP 请求 Body 的全局过滤器
package com.imooc.ecommerce.filter;
import com.imooc.ecommerce.constant.GatewayConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class GlobalCacheRequestBodyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
boolean isLoginOrRegister = exchange.getRequest().getURI().getPath().contains(GatewayConstant.LOGIN_URI)
|| exchange.getRequest().getURI().getPath().contains(GatewayConstant.REGISTER_URI);
if (null == exchange.getRequest().getHeaders().getContentType() || !isLoginOrRegister) {
return chain.filter(exchange);
}
return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
DataBufferUtils.retain(dataBuffer);
Flux<DataBuffer> cacheFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return cacheFlux;
}
};
return chain.filter(exchange.mutate().request(requestDecorator).build());
});
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE + 1;
}
}
package com.imooc.ecommerce.constant;
public class GatewayConstant {
public static final String LOGIN_URI = "/e-commerce/login";
public static final String REGISTER_URI = "/e-commerce/register";
public static final String AUTHORITY_CENTER_TOKEN_URL_FORMAT = "http://%s:%s/ecommerce-authority-center/authority/token";
public static final String AUTHORITY_CENTER_REGISTER_URL_FORMAT = "http://%s:%s/ecommerce-authority-center/authority/register";
}
2. 登录、注册、鉴权全局过滤器
package com.imooc.ecommerce.filter;
import com.alibaba.fastjson.JSON;
import com.imooc.ecommerce.constant.CommonConstant;
import com.imooc.ecommerce.constant.GatewayConstant;
import com.imooc.ecommerce.utils.JwtTokenParseUtil;
import com.imooc.ecommerce.vo.JwtToken;
import com.imooc.ecommerce.vo.LoginUserInfo;
import com.imooc.ecommerce.vo.UsernameAndPassword;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.message.TokenParser;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
@Component
public class GlobalLoginOrRegisterFilter implements GlobalFilter, Ordered {
private LoadBalancerClient loadBalancerClient;
private RestTemplate restTemplate;
public GlobalLoginOrRegisterFilter(LoadBalancerClient loadBalancerClient, RestTemplate restTemplate) {
this.loadBalancerClient = loadBalancerClient;
this.restTemplate = restTemplate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
if(request.getURI().getPath().contains(GatewayConstant.LOGIN_URI)){
String token = getTokenFromAuthorityCenter(request, GatewayConstant.AUTHORITY_CENTER_TOKEN_URL_FORMAT);
response.getHeaders().add(CommonConstant.JWT_USER_INFO_KEY, null == token ? "null" : token);
response.setStatusCode(HttpStatus.OK);
return response.setComplete();
}
if (request.getURI().getPath().contains(GatewayConstant.REGISTER_URI)) {
String token = getTokenFromAuthorityCenter(request, GatewayConstant.AUTHORITY_CENTER_REGISTER_URL_FORMAT);
response.getHeaders().add(CommonConstant.JWT_USER_INFO_KEY, null == token ? "null" : token);
response.setStatusCode(HttpStatus.OK);
return response.setComplete();
}
HttpHeaders httpHeaders = request.getHeaders();
String token = httpHeaders.getFirst(CommonConstant.JWT_USER_INFO_KEY);
LoginUserInfo loginUserInfo = null;
try {
LoginUserInfo userInfo = JwtTokenParseUtil.parseUserInfoFromToken(token);
} catch (Exception e) {
throw new RuntimeException(e);
}
if(null == loginUserInfo) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE + 2;
}
private String parseBodyFromRequest(ServerHttpRequest request) {
Flux<DataBuffer> body = request.getBody();
AtomicReference<String> atomicReference = new AtomicReference<>();
body.subscribe(dataBuffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());
DataBufferUtils.release(dataBuffer);
atomicReference.set(charBuffer.toString());
});
return atomicReference.get();
}
private String getTokenFromAuthorityCenter(ServerHttpRequest request, String uriFormat) {
ServiceInstance serviceInstance = loadBalancerClient.choose(CommonConstant.AUTHORITY_CENTER_SERVICE_ID);
log.info("Nacos Client Info: [{}], [{}], [{}]", serviceInstance.getServiceId(), serviceInstance.getInstanceId(),
JSON.toJSON(serviceInstance.getMetadata()));
String requestUri = String.format(uriFormat, serviceInstance.getHost(), serviceInstance.getPort());
UsernameAndPassword requestBody = JSON.parseObject(parseBodyFromRequest(request), UsernameAndPassword.class);
log.info("login request url and body: [{}], [{}]", requestUri, JSON.toJSON(requestBody));
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
JwtToken token = restTemplate.postForObject(requestUri, new HttpEntity<>(JSON.toJSONString(requestBody), httpHeaders), JwtToken.class);
if (null != token){
return token.getToken();
}
return null;
}
}
课程截图:
打开App,阅读手记