关于freemarker:漏洞通知JeecgBoot-修复Freemarker模板注入漏洞-漏洞危害等级高危

Freemarker模板注入导致近程命令执行, 近程攻击者可利用该破绽调用在零碎上执行任意命令。JeecgBoot官网已修复,倡议大家尽快降级至相干底层依赖和源码 一、破绽形容Freemarker模板注入导致近程命令执行, 近程攻击者可利用该破绽调用在零碎上执行任意命令。破绽危害等级:高危 二、影响范畴minidao-spring-boot-starter 版本 < 1.9.2jimureport-spring-boot-starter 版本 < 1.6.1codegenerate 版本 < 1.4.4hibernate-re 版本 < 3.5.3jeewx-api 版本 < 1.5.2drag-free 版本 < 1.0.2三、修复计划,降级依赖版本和源码目前该破绽曾经修复,受影响用户可降级到以下版本minidao-spring-boot-starter >= 1.9.2jimureport-spring-boot-starter >= 1.6.1codegenerate >= 1.4.4hibernate-re >= 3.5.3jeewx-api >= 1.5.2drag-free >=1.0.2JeecgBoot修复PR:https://github.com/jeecgboot/jeecg-boot/commit/acb48179ab00e167747fa4a3e4fd3b94c78aeda5https://github.com/jeecgboot/jeecg-boot/commit/baf4b96b3fcffa183e19b87485f5fb8388bb36ae

August 15, 2023 · 1 min · jiezi

关于freemarker:记录MVC项目部署时的CDN缓存问题

概述本文将剖析在公布前后端未分离我的项目(freemaker)时遇到的CDN缓存问题,次要有以下两个问题: 页面申请获取的html外面却是旧版本号的script链接script脚本链接是新版本号但拉取到的却是旧脚本代码问题剖析1、页面申请获取的html外面却是旧版本号的script链接 问题剖析前首先咱们要晓得以下知识点: (1)freemaker我的项目的页面是后端服务将ftl解决成html返回的 (2)部署时会遍历ftl文件,对所有的script链接打上版本号 // 构建前 supplierQuoteDetailPaging.ftl<!DOCTYPE html> <head> <script type="text/javascript" src="${Global.getConfig("web.app.static.url")}/js/supplierQuoteDetail.js"></script> </head></html>// 在`Jenkins`构建后会对申请动态脚本的`url`加上版本号 supplierQuoteDetailPaging.ftl<!DOCTYPE html> <head> <script type="text/javascript" src="${Global.getConfig("web.app.static.url")}/js/supplierQuoteDetail.js?version=1638706227856"></script> </head></html>(3)后端是集群服务,部署采纳滚动公布,也就是说部署时节点服务是一批一批来更新的,直到集群中所有的实例都更新成新版本,而不是一次性全量更新 当我的项目还是部署时,因为服务采纳滚动公布,因而在这个期间新服务和旧服务会同时存在。如果在这个阶段拜访页面,页面接口可能命中旧服务,也可能命中新服务,当命中旧服务时,申请失去的html外面script链接打上的是旧版本号;当部署实现时,群中所有的实例都更新成新版本,页面申请命中新服务,申请失去的html外面script链接打上的是新版本号。 解决方案:待我的项目部署实现后刷新页面就能够了 2、script脚本链接是新版本号但拉取到的却是旧脚本代码 失常来说,部署我的项目后,浏览器依据新版本号去申请CDN上的动态脚本文件,如果CDN缓存中没有对应新版本号对应的脚本文件,则会向后端服务拉取新脚本,而后CDN在做一次缓存,前面的脚本申请间接由CDN返回。 然而,如果部署还未实现浏览器就去拜访了,此时这个阶段新服务和旧服务是同时存在的,当新版本号对应的脚本在CDN上找不到时,就会去服务申请,恰好申请命中的是旧服务(服务响应跟版本号无关),旧服务返回旧的脚本,而后CDN缓存新版号对应的旧脚本,这样后续每次申请拉取到的都是CDN上缓存的就脚本,因而就呈现了上述问题。 解决方案:重新部署一遍,待部署实现后再去拜访页面

December 6, 2021 · 1 min · jiezi

关于freemarker:FreeMarker笔记

正文<#-- 正文内容 --><#-- 正文也能够多行内容 -->根本应用<html>Hello ${name} 或,#{name}</html>if判断<#if age gt 10 > 年龄大于10</#if>lt代替<,lte代替<=,gt代替>,gte代替>= 遍历List<#list userList as user> 昵称:${user.nickname}</#list>遍历Map<#list map?keys as key> ${map[key]}</#list>注:map的key只反对String类型,不反对int long等。 assign 创立或替换一个顶层变量<#assign name="Harlan">I'm ${name}<#assign info={"mobile":"xxxxxx","address":"china"} >my mobile is ${info.mobile}, my address is ${info.address}

September 28, 2021 · 1 min · jiezi

关于freemarker:Spring-Boot-引入freemarker

1 Maven引库<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> <version>2.5.5</version></dependency>application.properties 增加配置# 模板文件门路,当初配置在resources下的templates文件夹中spring.freemarker.tempalte-loader-path=classpath:/templatesspring.freemarker.cache=falsespring.freemarker.charset=UTF-8spring.freemarker.check-template-location=truespring.freemarker.content-type=text/htmlspring.freemarker.expose-request-attributes=truespring.freemarker.expose-session-attributes=truespring.freemarker.request-context-attribute=requestspring.freemarker.suffix=.ftl应用示例@RestController@RequestMapping("admin/user")public class AdminUserController extends BaseController{ @Autowired private UserService userService; @GetMapping("/userView") public ModelAndView userView() { ModelAndView modelAndView = new ModelAndView("user/user"); return modelAndView; } @GetMapping("/addUserView") public ModelAndView addUserView() { ModelAndView modelAndView = new ModelAndView("user/userAdd"); return modelAndView; } @GetMapping("/updateUserView") public ModelAndView updateUserView(@RequestParam Long id) { ModelAndView modelAndView = new ModelAndView("/admin/userUpdate"); modelAndView.addObject("user", userService.getUserDetail(id)); return modelAndView; }}

September 28, 2021 · 1 min · jiezi

关于freemarker:记录FM项目页面跳转问题

背景在2021/08/30下午三四点左右,组内测试同学发现蓬莱环境(灰度环境)博世我的项目的主流程呈现问题,具体表现在公布询价页点击“备货询价”以及询价单列表页点击“单号”没有跳转到指定的页面,而是跳到了询价首页 剖析过程首先,配置好蓬莱环境的本地域名解析配置,让本地拜访昆仑环境(正式环境)的域名时走的是蓬莱环境 用管理员权限关上powerShell,到hosts目录 cd C:\Windows\System32\Drivers\etc输出notepad hosts,关上hosts,增加124.71.23.127 os3.rbtssws.com.cn而后,拜访蓬莱环境的博世我的项目,复现问题发现点击“备货询价”或者“单号”时产生了重定向 而重定向的链接是去申请受权,那么为什么会产生这样的重定向呢?跳转页面理当不会一下子session就过期了呀,而后排查request headers中的cookies字段发现没有携带session,而昆仑环境携带了session 难道是浏览器cookie中没有存储吗?然而排查并不是这个问题,那么会不会因为跳转的域名和cookie中的domain不统一呢? 点击“备货询价”跳转的链接: 点击“单号”跳转的链接: 的确发现跳转的域名存在问题,多了一个数字2,很无语!!! 再深刻排查发现页面跳转门路的域名是读取的配置,该配置在工程部署的时候注入的 总结线上问题是因为跳转的门路域名有问题,导致跳转申请没有携带cookie,网关拦挡就会重定向去申请受权,而申请受权的域名是正确的携带了cookie,网关判断是通过的,就默认跳到了首页

August 30, 2021 · 1 min · jiezi

关于freemarker:FreeMarker

FreeMarker次要内容FreeMarker 概述FreeMarker概念 FreeMarker 是一款 模板引擎: 即一种基于模板和要扭转的数据, 并用来生成输入文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 是一个Java类库。 FreeMarker 被设计用来生成 HTML Web 页面,特地是基于 MVC 模式的应用程序,将视图从业务逻辑中抽离解决,业务中不再包含视图的展现,而是将视图交给 FreeMarker 来输入。尽管 FreeMarker 具备一些编程的能力,但通常由 Java 程序筹备要显示的数据,由 FreeMarker 生成页面,通过模板显示筹备的数据(如下图): FreeMarker不是一个Web利用框架,而适宜作为Web利用框架一个组件。 FreeMarker与容器无关,因为它并不知道HTTP或Servlet。FreeMarker同样能够利用 于非Web应用程序环境。 FreeMarker更适宜作为Model2框架(如Struts)的视图组件,你也能够在模板中应用 JSP标记库。 FreeMarker个性通用指标 可能生成各种文本:HTML、XML、RTF、Java 源代码等等 易于嵌入到你的产品中:轻量级;不须要 Servlet 环境 插件式模板载入器:能够从任何源载入模板,如本地文件、数据库等等 你能够按你所需生成文本:保留到本地文件;作为 Email 发送;从 Web 应用程序发送它返回给 Web 浏览器 弱小的模板语言 所有罕用的指令:include、if/elseif/else、循环构造 在模板中创立和扭转变量 简直在任何中央都能够应用简单表达式来指定值 命名的宏,能够具备地位参数和嵌套内容 名字空间有助于建设和保护可重用的宏库,或将大工程分成模块,而不用放心名字抵触 输入转换块:在嵌套模板片段生成输入时,转换HTML本义、压缩、语法高亮等等;你能够定义本人的转换 通用数据模型 FreeMarker不是间接反射到Java对象,Java对象通过插件式对象封装,以变量形式在模板中显示 你能够应用形象(接口)形式示意对象(JavaBean、XML文档、SQL查问后果集等等),通知模板开发者应用办法,使其不受技术细节的打搅 为Web筹备 在模板语言中内建解决典型Web相干工作(如HTML本义)的构造 ...

December 1, 2020 · 6 min · jiezi

Spring-Boot-整合-Freemarker50-多行配置是怎么省略掉的

Spring Boot2 系列教程接近完工,最近进入修修补补阶段。Freemarker 整合貌似还没和大家聊过,因此今天把这个补充上。 <!--more--> 已经完工的 Spring Boot2 教程,大家可以参考这里: 干货|最新版 Spring Boot2.1.5 教程+案例合集Freemarker 简介这是一个相当老牌的开源的免费的模版引擎。通过 Freemarker 模版,我们可以将数据渲染成 HTML 网页、电子邮件、配置文件以及源代码等。Freemarker 不是面向最终用户的,而是一个 Java 类库,我们可以将之作为一个普通的组件嵌入到我们的产品中。 来看一张来自 Freemarker 官网的图片: 可以看到,Freemarker 可以将模版和数据渲染成 HTML 。 Freemarker 模版后缀为 .ftl(FreeMarker Template Language)。FTL 是一种简单的、专用的语言,它不是像 Java 那样成熟的编程语言。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。 好了,这是一个简单的介绍,接下来我们来看看 Freemarker 和 Spring Boot 的一个整合操作。 实践在 SSM 中整合 Freemarker ,所有的配置文件加起来,前前后后大约在 50 行左右,Spring Boot 中要几行配置呢? 0 行! 1.创建工程首先创建一个 Spring Boot 工程,引入 Freemarker 依赖,如下图: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>工程创建完成后,在 org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration 类中,可以看到关于 Freemarker 的自动化配置: ...

July 5, 2019 · 2 min · jiezi

如何通过-Freemark-优雅地生成那些花里胡哨的复杂样式-Excel-文件

欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 《Java 核心知识整理&面试.pdf》资源链接!!个人网站: https://www.exception.site/essay/how-to-create-complex-style-excel-with-freemark 一、背景小哈最近这段时间开始负责一个新的产品:下载中心。啥玩意这是? 产品的目的其实就是统一管控各业务组文件下载功能(包括一些海量数据的导出,文件合并上传等),项目组不用自己再去实现各式各样的文件(PDF, Word, Excel)生成, 统一对接下载中心,由下载中心统一完成文件的生成、合并、上传、下载流程。 问题来了,这里面包括一些复杂文件的生成,如带有复杂样式的 Excel 文件,比如下面这个样子的: 这种复杂样式的 Excel, 如果说放到各个业务线去实现还是好办的,因为站在各个业务组的角度,场景变化不会太多,按照文件格式,代码写死即可。 但是站在下载中心的角度,因为需要对接各个业务中心,每个业务中心生成的样式都不一样,不可能每个业务组接进来,我都得定制的写一套生成代码吧!这显然也不合常理! 那么,有没有什么一劳永逸的办法呢?答案是肯定的! 二、实现思路要说实现方式,你的脑海里可能第一会想到传统的 Apache poi,jxl ,亦或者是阿里出品 EasyExcel 等等。 PS: 关于阿里的 EasyExcel, 小哈之前有分享过 ,没看过的小伙伴们,可以看下《7 行代码优雅地实现 Excel 文件生成&下载功能》。 对于这种复杂样式,要是用 Apache poi, jxl, 阿里 EasyExcel 去实现,不可避免的,代码肯定会非常繁琐。 有没有啥优雅(偷懒的)的方式呢? 其实我们可以通过视图引擎 Freemark、Velocity 来帮我们生成复杂样式 Excel 文件,无需关心花里胡哨的复杂样式,只关注于填充数据即可。接下来,我们以 Freemark 作为示例来讲解,如何生成这个复杂样式的 Excel 文件。 拓展阅读: 什么是 Freemark ?FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。 其实,对于Java 后端来说,它更常被用来服务端动态渲染 html 页面返回给浏览器。前些年还比较火热,近些年因为前后端分离的火热,也开始慢慢淡出视野了。 ...

June 8, 2019 · 2 min · jiezi

Freemark使用记录

对空值做处理对输出的空值做处理,无返回值输出name的值: ${name}。如果name为null,就会报错。${name!}。如果name为null,就不会报错,什么也没输出${name!"默认值"}。如果name为null,就输出”默认值”字符串${name!100}。如果name为null,就输出100输出user.name的值: ${(user.name)!"默认值”},如果user或name为null,就输出默认值${user.name!"默认值”},如果user为null会报错,如果name为null,就输出默认值。输出user.birthday的值: ${user.birthDate?string("yyyy-MM-dd")},如果user或birthday为空,就会报错。${(user.birthDate?string("yyyy-MM-dd"))!},如果user或birthday为null,什么也没输出使用default内建函数来处理:${user.name?default('vakin')} (较繁琐)测试是否为null,返回boolean值product.color??将只测试color是否为null(product.color)??将测试product和color是否存在null值得注意的是??与?的区别??是判断对象是否为空,例如:<#if object??> object对象不为空(即object存在)</#if> ?后面要加内建函数名,例如:<#if object?exists> object对象不为空(即object存在)</#if> 逻辑语句条件判断 <#if condition> ...<#elseif condition2> ...<#elseif condition3> ......<#else> ...</#if>condition, condition2, 等:将被计算成布尔值的表达式。elseif 和 else 是可选的。循环 <#list sequence as item> Part repeated for each item<#else> Part executed when there are 0 items</#list>else 部分是可选的,意思是sequence的size为0的时候执行的语句, 而且仅仅从 FreeMarker 2.3.23 版本开始支持。sequence: 将我们想要迭代的项,算作是序列或集合的表达式item: 循环变量 的名称 (不是表达式)在标签之间的多个 "parts" 可以是任意的FTL (包括嵌套的 list)注意事项??与?的区别 ??是判断对象是否为空,例如:<#if object??> object对象不为空(即object存在)</#if>?后面要加内建函数名,例如:<#if object?exists> object对象不为空(即object存在)</#if>

May 13, 2019 · 1 min · jiezi

从官方文档去学习之FreeMarker

一、前言上一篇 <从现在开始,试着学会用官方文档去学习一个技术框架> 提倡大家多去从官方文档学习技术,没有讲到具体的实践,本篇就拿一个案例具体的说一说,就是FreeMarker,选择这个框架没什么特别的含义,最近要用,就拿这个做个典型。二、套路上篇文章最后说到技术学习没有套路,无招胜有招,无招即是有招,解读一下实际上就是说 本身还是有些招式套路,但是要灵活运用,不要什么都往上套,应该忘掉固有的套路,让其化为你的一种本能,见招拆招。 下面就介绍一种常规学习套路给大家,如下图:下面就根据上面的套路结合FreeMarker官网溜一遍,来学习FreeMarker,重点在前4部分,后面两部分需要一些积累和经验后才更容易上手,所以本篇不会重点讲三、实践3.1 了解框架 首先我们应该了解FreeMarker是用来干什么的,这时候我看打开官网 https://freemarker.apache.org/ 上图来自官网首页上方的导航,依次为 Home(主页)、Manual(手册)、JavaApi(API接口)、Contribute(贡献)、 Report a Bug(反馈BUG)、 Download(下载) , 单词不认识,找工具翻译一下,没有别的办法,上篇也提到了。从字面意思理解可以看出跟我们有关的就是标红的那4个,好的来看看首页的介绍(大部分框架首页都会有简要介绍,说明框架的用途): 这是首页的两段介绍,自行翻译一下,这里就不在翻译了,解读一下,我们从这两段内容中可以得到如下信息:(1)这是一个Java模板引擎(2)用模板语言(FTL)编写(3)基本思想:java或其他编程语言准备数据,FreeMarker显示数据,配合官方给的图,更直观(4)在模板中,您关注的是如何显示数据,而在模板之外,您关注的是显示什么数据,这也是所有模板引擎解决的核心问题 数据与显示分离(5)用于MVC模式,有助于分离Java开发人员和web设计人员,设计人员不会在模板中面对复杂的逻辑,并且可以在不需要程序员更改或重新编译代码的情况下更改页面的外观,这里就说明了模板的好处,面试问你,为什么用模板技术啊,结合上面的一点就可以完美回答这个问题了(6)不依赖Servlet,也就是web环境和非Web环境都可以使用(7)更多细节内容看 manual (手册)通过解读官方的文档,我们就可以得到以上7个方面的信息。也就了解了Freemarker的作用、基本思想、好处、应用环境,这些东西,相信在很多FreeMarker的教程中,都不会这么详细,而且上面的信息基本上只要你把英文翻译过来都可以直观看到,我并没有做太多总结性归纳。 这是基本内容,首页还标出了一些特性我们再来看一下解读一下得到的信息如下(1)强大的模板语言,支持条件块、迭代、赋值、字符串和算术操作等(2)零依赖,任何输出格式,可以从任何地方加载模板(3)支持国际化(4)支持xml数据模型,也就是可以将xml数据填充到模板上(5)支持java对象暴露在模板,简单理解模板中可以调用对象的方法结合上面的基本内容和特性部分的内容,相信大家对freemarker有了较为完整的认识,包括能不能满足自己的一些场景,也会有一些基本的判断,方便技术选型。3.2 Helloworld看完基本内容,有了大致了解后,是不是迫不及待想去敲代码试一下,小阶段的输出成果更容易促使学习的动力。看文档首页显然没有告诉我们怎么用,怎么去开始写代码,但提到了更多内容看manual,那我们就点到manual去看一下从这里我看看到了Getting Started(开始),大部分的官网都会有Getting Started,点进去看一下,这里要多做一件事,写代码肯定要使用freemarler的jar,我们就从download去拿一下,进到download可以看到,我们可以直接下freemarker.jar、源码,或者通过maven引入,这里大家随意,导入工程,然后回到刚才的地方,看quick start三块内容(1) 模板 + 数据模型 = 输出(2) 一睹数据模型(3) 一睹模板分别点进去看一下,可以了解到(1) 模板有表达式和指令(2) 数据模型是树形结果但是我们没有看到代码,只看到一段模板代码和最终的结果输出,这里没有怎么办?我们再扫一眼目录,发现这里还有个Getting Started(一些框架官网没有直接给demo,我们可以去框架托管代码(github、gitee)的地方去找一下,基本上都会有demo)然后看一下它的分类是 Programmer’s Guide(程序员指南),那就应该是这里了,点进去看一下,可以看到,除过最后最后的 Putting all together, 其他部分是使用模板的每一个步骤,分为:创建配置实例 -> 创建数据模型 -> 获取模板 -> 合并数据模型到模板,这也是使用freemaker的基本步骤,分步骤的就不看了,我们直接看最后的Putting all together(所有合到一起的内容)测试类import freemarker.template.;import java.util.;import java.io.;public class Test { public static void main(String[] args) throws Exception { / ———————————————————————— / / You should do this ONLY ONCE in the whole application life-cycle: / / Create and adjust the configuration singleton / Configuration cfg = new Configuration(Configuration.VERSION_2_3_27); cfg.setDirectoryForTemplateLoading(new File("/where/you/store/templates")); cfg.setDefaultEncoding(“UTF-8”); cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); cfg.setLogTemplateExceptions(false); cfg.setWrapUncheckedExceptions(true); / ———————————————————————— / / You usually do these for MULTIPLE TIMES in the application life-cycle: / / Create a data-model / Map root = new HashMap(); root.put(“user”, “Big Joe”); Product latest = new Product(); latest.setUrl(“products/greenmouse.html”); latest.setName(“green mouse”); root.put(“latestProduct”, latest); / Get the template (uses cache internally) / Template temp = cfg.getTemplate(“test.ftlh”); / Merge data-model with template / Writer out = new OutputStreamWriter(System.out); temp.process(root, out); // Note: Depending on what out is, you may need to call out.close(). // This is usually the case for file output, but not for servlet output. }}数据模型/* * Product bean; note that it must be a public class! */public class Product { private String url; private String name; // As per the JavaBeans spec., this defines the “url” bean property // It must be public! public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } // As per the JavaBean spec., this defines the “name” bean property // It must be public! public String getName() { return name; } public void setName(String name) { this.name = name; }}模板 test.ftlh<html><head> <title>Welcome!</title></head><body> <h1>Welcome ${user}!</h1> <p>Our latest product: <a href="${latestProduct.url}">${latestProduct.name}</a>!</body></html>相信不出意外,复制粘贴到自己的工程里,就可以正常运行了,至此一个helloworld就算OK了,对于代码的含义,可以直接查javaApi, 可以看分步骤的内容有解释。至此我们对freemarker应该有了更直观的认识和理解,也基本知道了使用的套路3.3 熟悉配置为什么要熟悉配置,很多框架的功能支持、 性能调优实际上做的就是配置参数的调整,所以说比较重要,我们来看一下,回到manual首页,找配置相关的字眼,发现就是它了吧,大概点进去看一下更多的图就不贴了,总之我们指导这里就是介绍配置属性的地方,详细内容大家自己看一看,这里有一个比较重要的点就是模板加载器,大家自己查阅熟悉一下。3.4 更多API熟悉完配置,我们需要了解一些API的使用,这里就不展开了,点到javaApi,自行查阅,这里就不展开说明了。3.4 表达式、指令这里的内容在上面的套路图中没标,那个图我表达的是一个通用的套路,这里算套路图中[更多基础API]一种吧。表达式、指令是对与模板来说的,也就是输出数据的一些命令。浏览文档相关内容找模板、表达式、指令相关的关键字,就可以锁定相关内容主要在单纯的表达式、指令使用都很简单,这里不多讲了。3.5 高级用法所谓高级用法也就是一些提升性能的配置、自定义扩展、一些指令、表达式之类的,这需要大家熟悉基本的使用、配置、api和文档的相关介绍。这里暂不展开,相信有了前面的基础掌握一些技巧和方法,这里来说不是难事。3.6 原理、源码这里针对有兴趣,想深入学习的人,写到这是为了保证套路的完整性,这里也不展开,也不是本篇内容的重点。四、总结本篇重点是带大家从官网去学习一个技术框架,至少用到一些配置、api知道怎么去查,要学习一个新技术的时候大概需要怎么做,还提了一个基本的学习套路,希望能给需要的人一些帮助。没看上一篇的建议看一下。Over。 ...

January 27, 2019 · 2 min · jiezi