当前位置: 首页 > news >正文

互动营销网站建设营销网站 建设 高端

互动营销网站建设,营销网站 建设 高端,河南建设工程信息网推荐中项网,徐州模板建站系统概述 基于Spring Cloud开发微服务时#xff0c;使用Spring Cloud原生自带的Gateway作为网关#xff0c;所有请求都需要经过网关服务转发。 为了防止恶意请求刷取数据#xff0c;对于业务请求需要进行拦截#xff0c;故而可在网关服务增加拦截过滤器。基于此#xff0c;有…概述 基于Spring Cloud开发微服务时使用Spring Cloud原生自带的Gateway作为网关所有请求都需要经过网关服务转发。 为了防止恶意请求刷取数据对于业务请求需要进行拦截故而可在网关服务增加拦截过滤器。基于此有如下源码 Slf4j Component public class BlockListFilter extends AbstractGatewayFilterFactory {private static final String DIALOG_URI /dialog/nextQuestion;Resourceprivate AssessmentBlockListService assessmentBlockListService;LazyResourceprivate RemoteUserService remoteUserService;LazyResourceprivate RemoteRcService remoteRcService;LazyAutowiredprivate RemoteOAuthService remoteOAuthService;Value(${blockListSwitch:true})private Boolean blockListSwitch;Overridepublic GatewayFilter apply(Object config) {return (exchange, chain) - {ServerHttpResponse response exchange.getResponse();response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);ServerHttpRequest serverHttpRequest exchange.getRequest();String uri serverHttpRequest.getURI().getPath();HttpHeaders httpHeaders serverHttpRequest.getHeaders();// 只处理相关urlif (blockListSwitch StringUtils.equalsIgnoreCase(uri, DIALOG_URI)) {String token httpHeaders.getFirst(Authorization);String pureToken StringUtils.replaceIgnoreCase(token, Bearer , );BaseUserInfo baseUserInfo UserUtil.getBaseUserInfo(pureToken);if (baseUserInfo null) {log.warn(传入的token非法{}, token);return chain.filter(exchange);}// 从JWT token中解析用户信息(不含mobile等敏感信息)String channel baseUserInfo.getChannel();String userKey baseUserInfo.getUserkey();UserParam userParam new UserParam();userParam.setKey(userKey);userParam.setChannel(channel);// Feign远程请求user服务获取mobile信息ResponseUserAccountVO userAccountResponse remoteUserService.baseQuery(userParam);if (userAccountResponse.getCode() ! 0) {log.warn(未获取到userKey{}的用户信息, userKey);return chain.filter(exchange);}UserAccountVO userAccountVO userAccountResponse.getData();log.info(blocklist filter user{}, JsonUtil.beanToJson(userAccountVO));String mobile userAccountVO.getMobile();if (StringUtils.isNotBlank(userKey)) {// 具体的拦截业务逻辑this.process(uri, userKey, mobile, channel, pureToken);}return chain.filter(exchange);}return chain.filter(exchange);};}private String process(String uri, String userKey, String mobile, String channel, String token) {DetectUserDTO detectUserDTO new DetectUserDTO();detectUserDTO.setChannel(channel);detectUserDTO.setMobile(mobile);detectUserDTO.setUserKey(userKey);detectUserDTO.setUri(uri);ResponseDetectUserVO detectUserVOResponse remoteRcService.detectUser(detectUserDTO);if (detectUserVOResponse.getCode() ! 0) {log.warn(mobile{} 风控接口返回异常{}, mobile, JsonUtil.beanToJson(detectUserVOResponse));return null;}DetectUserVO detectUserVO detectUserVOResponse.getData();if (detectUserVO.getIsInAllowList()) {log.info(在白名单中放行);} else if (detectUserVO.getIsInBlockList()) {log.info(在黑名单中拦截处理...);this.logout(token);return 当前手机号问诊次数已达今日上限;}return null;}private void logout(String token) {// 强制下线,踢出登录态LogoutDto logoutDto new LogoutDto();logoutDto.setToken(pureToken);remoteOAuthService.logout(logoutDto);} }上面的代码只是实现判断流量请求URL进而判断用户是否触发风控黑名单机制如果触发黑名单则踢出登录态强制用户下线即用户不能使用App。 需求 异常捕获 上面的踢出登录态做法过于简单粗暴App有若干个功能模块某些请求URL触发黑名单机制就强制用户下线不能使用App其他功能。应该允许用户使用除了触发黑名单模块的其他模块功能。当然对于是否强制下线的交互设计每个人有每个人的看法。 就我遇到的情况来细说痛点在于前端所谓大前端概念含iOS和安卓App同学针对此种情况直接返回统一的错误页【抱歉请返回再试一次】并且这个页面没有任何UI设计仅仅是前面提到的一句提示文案。用户看到这个文案会重新登录因为后端做了踢出登录态逻辑App成功后经过网关校验发现用户依旧触发黑名单然后再次踢出登录态陷入死循环。 基于此做如下改造不踢出登录态可以继续使用其他模块功能在使用某个触发黑名单机制的模块时后端返回错误码前端弹窗提示【无法使用此功能】。 故而对上面的代码进行调整 String msg this.process(uri, userKey, mobile, channel, pureToken,); if (StringUtils.isNotBlank(msg)) {return Mono.error(new CustomException(BlockTypeEnum.getCodeByMsg(msg), msg)); }本地启动Gateway网关服务postman模拟请求得到接口返回数据 咦怎么Code不是枚举类里定义的错误文案msg对应的错误码而是500呢 看代码发现有个继承DefaultErrorWebExceptionHandler的JsonErrorWebExceptionHandler类那这个类也需要调整 Slf4j public class JsonErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {public JsonErrorWebExceptionHandler(ErrorAttributes errorAttributes,ResourceProperties resourceProperties,ErrorProperties errorProperties,ApplicationContext applicationContext) {super(errorAttributes, resourceProperties, errorProperties, applicationContext);}Overrideprotected MapString, Object getErrorAttributes(ServerRequest request, boolean includeStackTrace) {MapString, Object errorAttributes new HashMap(8);Throwable error super.getError(request);errorAttributes.put(message, error.getMessage());if (error instanceof CustomException) {errorAttributes.put(code, ((CustomException) error).getCode());} else {errorAttributes.put(code, HttpStatus.INTERNAL_SERVER_ERROR.value());}errorAttributes.put(method, request.methodName());errorAttributes.put(path, request.path());log.warn(网关异常,path:{},method:{},message:{}, request.path(), request.methodName(), error.getMessage());return errorAttributes;}Overrideprotected RouterFunctionServerResponse getRoutingFunction(ErrorAttributes errorAttributes) {return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);}Overrideprotected HttpStatus getHttpStatus(MapString, Object errorAttributes) {// 这里其实可以根据errorAttributes里面的属性定制HTTP响应码return HttpStatus.INTERNAL_SERVER_ERROR;} }经过调整后断点调试万事大吉 但是 后来在和前端联调时才发现有点不对劲 如上图枚举类里有几个code每个code都有对应的系统功能模块黑名单msg。之前没有注意到右上角的接口状态码是500这个500表示请求未成功。也就是说接口未成功接口响应码是自定义的code也没啥用。status必须得是200、204、202这种才行。 总结一下虽然使用Mono.error()返回业务自定义错误信息但错误代码9996被转为500。 继续排查。找到下面参考2里的文章做如下调整 String msg this.process(uri, userKey, mobile, channel, pureToken,); if (StringUtils.isNotBlank(msg)) {// 对应200,表明接口请求是成功的,但是触发业务异常错误码exchange.getResponse().setStatusCode(HttpStatus.OK);return exchange.getResponse().writeWith(Flux.just(exchange.getResponse().bufferFactory().wrap(JSON.toJSONString(msg).getBytes()))); }调试结果右上角的Status变成200 注意看Postman可以返回中文msg。 发布测试环境后在Chrome浏览器里却发现返回数据乱码 IDEA调整截图如上没有乱码问题console控制台打印也是正常 乱码不是什么大问题研发这么多年遇到无数次。继续排查做如下调整解决问题 return exchange.getResponse().writeWith(Flux.just(exchange.getResponse().bufferFactory().wrap(JSON.toJSONString(msg).getBytes(StandardCharsets.UTF_8))));获取请求体 前面提到我们的App有几个功能模块其中一个模块的请求全部是dialog/nextQuestion接口。用户恶意刷数据多次请求此接口就会触发黑名单。但是在我们的交互设计上又希望用户从其他功能模块切换到此功能时可以请求一次此接口。那怎么判断是第一次呢那就需要解析requestBody。 根据下面的参考1文章增加如下代码 public String resolveBodyForDialog(ServerHttpRequest serverHttpRequest) {String uri serverHttpRequest.getURI().getPath();// 只有某些请求才解析if (!StringUtils.equalsAnyIgnoreCase(uri, dialog/nextQuestion)) {return ;}StringBuilder sb new StringBuilder();FluxDataBuffer body serverHttpRequest.getBody();body.subscribe(buffer - {byte[] bytes new byte[buffer.readableByteCount()];buffer.read(bytes);DataBufferUtils.release(buffer);String bodyString new String(bytes, StandardCharsets.UTF_8);sb.append(bodyString);});return sb.toString(); }本地调试时没有问题可以获取到requestBody 但是测试环境却又问题 详细的错误日志 500 Server Error for HTTP POST /api/open/dialog/nextQuestion io.netty.handler.codec.EncoderException: io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1 at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:107) at io.netty.channel.CombinedChannelDuplexHandler.write(CombinedChannelDuplexHandler.java:348) at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:716) at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:708) at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:791) at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:701) at reactor.netty.channel.MonoSendMany$SendManyInner.run(MonoSendMany.java:286) at reactor.netty.channel.MonoSendMany$SendManyInner.trySchedule(MonoSendMany.java:368) at reactor.netty.channel.MonoSendMany$SendManyInner.onSubscribe(MonoSendMany.java:221) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90) at reactor.core.publisher.FluxContextStart$ContextStartSubscriber.onSubscribe(FluxContextStart.java:97) at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) at reactor.core.publisher.MonoSubscriberContext.subscribe(MonoSubscriberContext.java:47) at reactor.core.publisher.FluxSourceMonoFuseable.subscribe(FluxSourceMonoFuseable.java:38) at reactor.core.publisher.FluxMapFuseable.subscribe(FluxMapFuseable.java:63) at reactor.core.publisher.Flux.subscribe(Flux.java:7921) at reactor.netty.channel.MonoSendMany.subscribe(MonoSendMany.java:81) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:153) at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) at reactor.core.publisher.Mono.subscribe(Mono.java:3848) at reactor.netty.NettyOutbound.subscribe(NettyOutbound.java:305) at reactor.core.publisher.MonoSource.subscribe(MonoSource.java:51) at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.onStateChange(HttpClientConnect.java:441) at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:470) at reactor.netty.resources.PooledConnectionProvider$DisposableAcquire.onStateChange(PooledConnectionProvider.java:512) at reactor.netty.resources.PooledConnectionProvider$PooledConnection.onStateChange(PooledConnectionProvider.java:451) at reactor.netty.channel.ChannelOperationsHandler.channelActive(ChannelOperationsHandler.java:62) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:225) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:211) at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:204) at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelActive(CombinedChannelDuplexHandler.java:414) at io.netty.channel.ChannelInboundHandlerAdapter.channelActive(ChannelInboundHandlerAdapter.java:69) at io.netty.channel.CombinedChannelDuplexHandler.channelActive(CombinedChannelDuplexHandler.java:213) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:225) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:211) at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:204) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelActive(DefaultChannelPipeline.java:1396) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:225) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:211) at io.netty.channel.DefaultChannelPipeline.fireChannelActive(DefaultChannelPipeline.java:906) at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.fulfillConnectPromise(AbstractEpollChannel.java:618) at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.finishConnect(AbstractEpollChannel.java:651) at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.epollOutReady(AbstractEpollChannel.java:527) at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:422) at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:333) at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:906) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)具体研究这篇文章做如下调整 String uri serverHttpRequest.getURI().getPath(); // 只有某些请求才解析 if (!StringUtils.equalsAnyIgnoreCase(uri, DIALOG_URI)) {return ; } FluxDataBuffer body serverHttpRequest.getBody(); AtomicReferenceString bodyRef new AtomicReference(); body.subscribe(buffer - {CharBuffer charBuffer StandardCharsets.UTF_8.decode(buffer.asByteBuffer());bodyRef.set(charBuffer.toString()); }); return bodyRef.get();上面的报错消失。问题虽然是解决但是不明就里。 后面又发现一个乱码问题针对如下requestBody {stateId: DASHBOARD,answer: {transitionId: GET_HEALTH_ADVICE,label: 开始评估症状} }bodyRef.get()获取到的中文数据乱码。参考3解决方法 String encoding System.getProperty(file.encoding); CharBuffer charBuffer Charset.forName(encoding).decode(buffer.asByteBuffer()); bodyRef.set(charBuffer.toString());参考 Spring Cloud Gateway读取RequestBody数据使用Spring Cloud-Gateway WebFlux抛出指定错误代码和信息从String获得ByteBuffer、从ByteBuffer获得CharBuffer的正确姿势
http://www.dnsts.com.cn/news/57693.html

相关文章:

  • 商城网站建设特点有哪些公司简介ppt案例
  • 网站建设的行业动态安阳住房与城乡建设局官方网站
  • 瑞安外贸网站建设网站设计与建设
  • 中国建设教育协会培训中心网站清水模板
  • 成品网站1688入口网页版怎样景区门户网站建设大数据分析
  • 网站建设描述怎么写网站建设实训课指导书网站版式分析
  • 河北廊坊做网站深圳小程序app开发
  • 网站建设和托管哪家好软件代理
  • 建站工具缺点wordpress 网店模板
  • 自助下单网站怎么做重要的网站建设
  • 做微商加入什么移动电商网站聚名网域名备案
  • 外贸网站推广技巧视频链接制作
  • 网站建设项目计划书苏州好的网站公司名称
  • 网站设计命名规范seo tdk
  • 云南做网站报价网站开发公司 广告词
  • 怎么下载建设银行网站什么是erp企业管理系统
  • 最好的小型 网站开发系统合肥网站建设黄页
  • 建设部考试网站用网站
  • 深圳做企业网站多少钱先做网站主页还是先上架宝贝
  • 自贡普通网站建设费用移动微网站建设
  • 黄浦区网站建设互联网招聘网站排名
  • 西安网站制作西安搜推宝黄山旅游官方平台
  • 常州seo建站江门网站开发公司
  • 东莞中小型网站建设呼和浩特网络推广公司
  • 做网站 源码树莓派可以做网站的服务器吗
  • 微信公众号搭建网站做装修效果图的网站有哪些软件下载
  • 汉口网站建设公司顺丰电子商务网站建设
  • 备案做电影网站营销网站的优势有哪些
  • 青海移动网站建设常州网站建设优质商家
  • 设计素材类网站开发策划书网站搭建好之后提示网页走丢了