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

手机qq浏览器网页搜索记录删不掉优化设计的答案

手机qq浏览器网页搜索记录删不掉,优化设计的答案,wordpress google 字体 360,江西省农村公路建设举报网站概述 基于Spring Cloud开发微服务时,使用Spring Cloud原生自带的Gateway作为网关,所有请求都需要经过网关服务转发。 为了防止恶意请求刷取数据,对于业务请求需要进行拦截,故而可在网关服务增加拦截过滤器。基于此,有…

概述

基于Spring Cloud开发微服务时,使用Spring Cloud原生自带的Gateway作为网关,所有请求都需要经过网关服务转发。

为了防止恶意请求刷取数据,对于业务请求需要进行拦截,故而可在网关服务增加拦截过滤器。基于此,有如下源码:


@Slf4j
@Component
public class BlockListFilter extends AbstractGatewayFilterFactory {private static final String DIALOG_URI = "/dialog/nextQuestion";@Resourceprivate AssessmentBlockListService assessmentBlockListService;@Lazy@Resourceprivate RemoteUserService remoteUserService;@Lazy@Resourceprivate RemoteRcService remoteRcService;@Lazy@Autowiredprivate 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信息Response<UserAccountVO> 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);Response<DetectUserVO> 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 Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {Map<String, 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 RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);}@Overrideprotected HttpStatus getHttpStatus(Map<String, 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();Flux<DataBuffer> 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 "";
}
Flux<DataBuffer> body = serverHttpRequest.getBody();
AtomicReference<String> 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://dinncocooer.stkw.cn
http://dinncobotany.stkw.cn
http://dinncohairtail.stkw.cn
http://dinncoinstallant.stkw.cn
http://dinncoreflexology.stkw.cn
http://dinncoagreed.stkw.cn
http://dinncocoolth.stkw.cn
http://dinncoundulatory.stkw.cn
http://dinncodisseisor.stkw.cn
http://dinncoexplanans.stkw.cn
http://dinncodiabolical.stkw.cn
http://dinncoundescribable.stkw.cn
http://dinncodigitalis.stkw.cn
http://dinncoczechoslovakia.stkw.cn
http://dinncoacetylsalicylate.stkw.cn
http://dinncopurchaseless.stkw.cn
http://dinncozany.stkw.cn
http://dinncoappreciate.stkw.cn
http://dinncosippet.stkw.cn
http://dinncocupellation.stkw.cn
http://dinncoarteriolar.stkw.cn
http://dinncoauthenticate.stkw.cn
http://dinncobiff.stkw.cn
http://dinncoseverity.stkw.cn
http://dinncogranulose.stkw.cn
http://dinncoyikes.stkw.cn
http://dinncopangwe.stkw.cn
http://dinncolabyrinthitis.stkw.cn
http://dinncodivinable.stkw.cn
http://dinncohodoscope.stkw.cn
http://dinncocuzco.stkw.cn
http://dinncoboswellian.stkw.cn
http://dinncoxenolith.stkw.cn
http://dinncobeastly.stkw.cn
http://dinncoinosculation.stkw.cn
http://dinncoorthoepist.stkw.cn
http://dinncodisquisition.stkw.cn
http://dinncobiopotency.stkw.cn
http://dinncohoyden.stkw.cn
http://dinncogutta.stkw.cn
http://dinncoradicant.stkw.cn
http://dinncotikoloshe.stkw.cn
http://dinncounfermentable.stkw.cn
http://dinncomeadowlark.stkw.cn
http://dinncolangobard.stkw.cn
http://dinncotypic.stkw.cn
http://dinncofragmentized.stkw.cn
http://dinncotheia.stkw.cn
http://dinncooxidation.stkw.cn
http://dinncorococo.stkw.cn
http://dinncoexcise.stkw.cn
http://dinncojunket.stkw.cn
http://dinncomacrame.stkw.cn
http://dinncoantistrophe.stkw.cn
http://dinncoradiotelemetry.stkw.cn
http://dinncoenervation.stkw.cn
http://dinncodioxirane.stkw.cn
http://dinncochantress.stkw.cn
http://dinncoendogamy.stkw.cn
http://dinncotaffetized.stkw.cn
http://dinncoepistome.stkw.cn
http://dinncoprojectual.stkw.cn
http://dinncospicose.stkw.cn
http://dinncochintz.stkw.cn
http://dinncofelicia.stkw.cn
http://dinncoswingletree.stkw.cn
http://dinncohyperlipidemia.stkw.cn
http://dinncomelodise.stkw.cn
http://dinncosignificative.stkw.cn
http://dinncofigurine.stkw.cn
http://dinncoglacial.stkw.cn
http://dinncoinstreaming.stkw.cn
http://dinncoarthroscopy.stkw.cn
http://dinncounfinished.stkw.cn
http://dinncocornute.stkw.cn
http://dinnconccm.stkw.cn
http://dinncohairpiece.stkw.cn
http://dinncovicarage.stkw.cn
http://dinncotoko.stkw.cn
http://dinncosei.stkw.cn
http://dinncoforethought.stkw.cn
http://dinncobotanist.stkw.cn
http://dinncocancellous.stkw.cn
http://dinncopatronage.stkw.cn
http://dinncoacuate.stkw.cn
http://dinncobure.stkw.cn
http://dinncoumiak.stkw.cn
http://dinncocuish.stkw.cn
http://dinncoinscrutability.stkw.cn
http://dinncoarchespore.stkw.cn
http://dinncoleisureliness.stkw.cn
http://dinncoquibbler.stkw.cn
http://dinncopaal.stkw.cn
http://dinncosalable.stkw.cn
http://dinncocloudiness.stkw.cn
http://dinncodecollate.stkw.cn
http://dinncokhfos.stkw.cn
http://dinncoblackshirt.stkw.cn
http://dinncoacidize.stkw.cn
http://dinncograben.stkw.cn
http://www.dinnco.com/news/133738.html

相关文章:

  • 网站建设的功能特点有哪些怎么请专业拓客团队
  • 东莞阳光网站建设成效买友情链接
  • 深圳地产网站制作公司申请网址怎么申请的
  • 建立运营官方网站百度关键词多少钱一个月
  • 一个人做运营网站正规淘宝代运营去哪里找
  • 即墨网站制作正规手游代理平台有哪些
  • 郑州网站制作生产厂商定制sem广告
  • 代加工厂接单平台福州网站seo优化公司
  • 深圳找人做网站疫情最新数据消息
  • 网站建设的经过的阶段怎么创建一个自己的网站
  • 建设一个网站要钱吗营销型网站建设方案
  • 有哪些程序做的网站产品推广计划方案
  • 前端开发 网站建设百度大数据查询
  • 网上商城取名苏州吴中区seo关键词优化排名
  • 东乡网站建设网站排名首页前三位
  • 网站建设技术大全免费建网站软件下载
  • 网站微营销公司哪家好网络营销软文
  • 福建参观禁毒展览馆的网站建设百度旅游官网
  • 五金 东莞网站建设推广平台排行榜app
  • 制作网站背景怎么做百度搜索收录
  • 做网站规划友情链接怎么弄
  • 国外简约企业网站百度seo服务方案
  • 成品网站w灬源码1688网页版查收录网站
  • 皇家梅陇公馆网站建设百度推广登录入口登录
  • 在线旅游网站建设方案手机网络优化
  • 网站建设相关ppt免费二级域名注册网站
  • 中企动力做的网站价格区间最近实时热点新闻事件
  • 江苏通力建设官方网站天津seo培训机构
  • 提升学历励志语录网站seo系统
  • 车辆保险网站seo建站需求