• [问题求助] 【CDM云数据迁移】http作业失败
    【功能模块】使用CDM,将第三方语料库文件(url:https://openslr.magicdatatech.com/resources/33/data_aishell.tgz,文件大小15G)迁移至OBS桶【操作步骤&问题现象】1、新建两个连接:http连接器(连接第三方语料库数据),obs连接2、新建作业并运行3、报错fail,提示无法连接至url。但该url可以手动在浏览器打开并下载【截图信息】【日志信息】(可选,上传日志内容或者附件)2022-04-21 17:22:00.248|INFO|cdm-job-submit-pool1|||o.a.s.d.job.JobSubmissionContext creating job request 2022-04-21 17:22:01.004|INFO|cdm-job-submit-pool1|||o.a.s.c.file.FileFromInitializer Keep dir structure is true. 2022-04-21 17:22:01.004|INFO|cdm-job-submit-pool1|||o.a.s.c.file.FileToInitializer running SFTP Connector TO initializer. 2022-04-21 17:22:33.318|ERROR|cdm-job-submit-pool1|||o.a.s.c.h.client.HttpConnectorClient failed to connect to us.openslr.org/46.101.158.64:443 2022-04-21 17:22:33.336|ERROR|cdm-job-submit-pool1|||o.a.s.d.job.JobSubmissionContext submit failed. org.apache.sqoop.common.SqoopException: HTTP_CONNETOR_1453:Can't connect url [https://us.openslr.org/resources/33/data_aishell.tgz]. at org.apache.sqoop.connector.http.client.HttpConnectorClient.checkURLConnection(HttpConnectorClient.java:184) at org.apache.sqoop.connector.http.client.HttpConnectorClient.checkInputDir(HttpConnectorClient.java:168) at org.apache.sqoop.connector.file.configuration.LinkConfiguration.getInputPaths(LinkConfiguration.java:296) at org.apache.sqoop.connector.file.TraveralFileLister.getFileBeanList(TraveralFileLister.java:72) at org.apache.sqoop.connector.file.TraveralFileLister.init(TraveralFileLister.java:98) at org.apache.sqoop.connector.file.FileUtils.getFromSchema(FileUtils.java:79) at org.apache.sqoop.connector.file.FileFromInitializer.getSchema(FileFromInitializer.java:63) at org.apache.sqoop.connector.file.FileFromInitializer.getSchema(FileFromInitializer.java:22) at org.apache.sqoop.driver.job.JobInitiator.getSchemaForConnector(JobInitiator.java:560) at org.apache.sqoop.driver.job.JobInitiator.createJobRequest(JobInitiator.java:346) at org.apache.sqoop.driver.job.JobSubmissionContext.submitInternal(JobSubmissionContext.java:179) at org.apache.sqoop.driver.job.JobSubmissionContext.submit(JobSubmissionContext.java:135) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) 2022-04-21 17:22:33.344|ERROR|cdm-job-submit-pool1|||o.a.s.d.job.JobSubmissionContext submit failed. org.apache.sqoop.common.SqoopException: HTTP_CONNETOR_1453:Can't connect url [https://us.openslr.org/resources/33/data_aishell.tgz]. at org.apache.sqoop.connector.http.client.HttpConnectorClient.checkURLConnection(HttpConnectorClient.java:184) at org.apache.sqoop.connector.http.client.HttpConnectorClient.checkInputDir(HttpConnectorClient.java:168) at org.apache.sqoop.connector.file.configuration.LinkConfiguration.getInputPaths(LinkConfiguration.java:296) at org.apache.sqoop.connector.file.TraveralFileLister.getFileBeanList(TraveralFileLister.java:72) at org.apache.sqoop.connector.file.TraveralFileLister.init(TraveralFileLister.java:98) at org.apache.sqoop.connector.file.FileUtils.getFromSchema(FileUtils.java:79) at org.apache.sqoop.connector.file.FileFromInitializer.getSchema(FileFromInitializer.java:63) at org.apache.sqoop.connector.file.FileFromInitializer.getSchema(FileFromInitializer.java:22) at org.apache.sqoop.driver.job.JobInitiator.getSchemaForConnector(JobInitiator.java:560) at org.apache.sqoop.driver.job.JobInitiator.createJobRequest(JobInitiator.java:346) at org.apache.sqoop.driver.job.JobSubmissionContext.submitInternal(JobSubmissionContext.java:179) at org.apache.sqoop.driver.job.JobSubmissionContext.submit(JobSubmissionContext.java:135) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
  • [问题求助] ServiceComb打开Tls开关,如何能支持常规非CSE服务间访问的HTTP请求
    ServiceComb打开Tls开关,如何能支持普通非CSE服务间访问的HTTP请求?打开sslEnabled之后,CSE服务间访问确实是走tls,但是常规http请求也是需要https,可否配置部分url走http的策略?
  • [问题求助] Http 和 Https 区别
    Http 和 Https 区别
  • [技术干货] http协议那些事[转载]
    1. HTTP协议1.1 http 技术背景为啥叫超文本传输协议呢# 技术由来 在互联网早期的时候,我们输入的信息只能保存在本地,无法和其他电脑进行交互。我们保存的信息通常都以文本即简单字符的形式存在,文本是一种能够被计算机解析的有意义的二进制数据包。而随着互联网的高速发展,两台电脑之间能够进行数据的传输后,人们不满足只能在两台电脑之间传输文字,还想要传输图片、音频、视频,甚至点击文字或图片能够进行超链接的跳转,那么文本的语义就被扩大了,这种语义扩大后的文本就被称为超文本(Hypertext)。HTML 就是最常⻅的超⽂本了,它本身只是纯⽂字⽂件,但内部⽤很多标签定义了图⽚、视频等的链接,再经过浏览器的解释,呈现给我们的就是⼀个⽂字、有画⾯的⽹⻚了。 # SO,HTTP 是⼀个在计算机世界⾥专⻔在「两点」之间「传输」⽂字、图⽚、⾳频、视频等「超⽂本」数据的「约定和 规范」。 # 浏览器 浏览器使用的是HTTP协议的主要载体。随着网景 大战结束,浏览器迅速发展,至今已有很多浏览器。1.2 http 基本概念HTTP 是什么HTTP 是超⽂本传输协议,也就是HyperText Transfer Protocol。HTTP的名字「超⽂本协议传输」,它可以拆成三个部分:超⽂本、传输、协议URI/URL# URI的全称是(Uniform Resource ldentifier),中文名称是统一资源标识符,使用它就能够唯一地标记互联网上资源 # URL的全称是 ((Uniform Resource Locator) ,中文名称是统一资源定位符,也就是我们俗称的网址,它实际上是URI的一个子集。 # URN 通过名字来标识或识别资源。常见的字段有哪些?Host 字段:客户端发送请求时,⽤来指定服务器的域名 Content-Length 字段:表明本次回应的数据⻓度。 Connection 字段:常⽤于客户端要求服务器使⽤TCP 持久连接,以便其他请求复⽤ HTTP/1.1 版本的默认连接都是持久连接,但为了兼容⽼版本的HTTP,需要指定 Connection ⾸ 部字段的值为Keep-Alive。 Content-Type 字段:⽤于服务器回应时,告诉客户端,本次数据是什么格式 Content-Type: text/html; charset=utf-8 发送的是网页,使用的是utf8格式 客户端请求的时候,可以使⽤ Accept 字段声明⾃⼰可以接受哪些数据格式。 Accept: */* 客户端声明⾃⼰可以接受任何格式的数据。 Content-Encoding 字段 :说明数据的压缩⽅法。表示服务器返回的数据使⽤了什么压缩格式1.3 http 常见状态码5大状态码301 Moved Permanently 永久移动,请求的资源已被永久的移动到新URI; 302 Found 临时移动,资源临时被移动,客户端应继续使用原有URI; 304 Not Modified 文件未修改,请求的资源未修改,服务器返回此状态码时,常用于缓存; 400 Bad Request 客户端请求的语法错误,服务器无法解析或者访问; 401 Unauthorized 请求要求用户的身份认证; 402 Payment Required 此状态码保留,为以后使用; 403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求; 404 Not Found 服务器没有该资源,请求的文件找不到; 405 Method Not Allowed 客户端请求中的方法被禁止; 500 Internal Server Error 服务器内部错误,无法完成请求; 502 Bad Gateway 服务器返回错误代码或者代理服务器错误的网关; 503 Service Unavailable 服务器无法响应客户端请求,或者后端服务器异常; 504 Gateway Time-out 网关超时或者代理服务器超时; 505 HTTP Version not supported 服务器不支持请求的HTTP协议的版本,无法完成处理。 1.4 http 报文介绍http 协议主要有三大部分组成: 起始行、请求头、实体每个报文的其实行 都是由 3个字段组成:、方法: http、ftp等URL:统一资源定位符 俗称 网址http版本:3.5 http 请求URL 介绍我们以访问http://www.example.com:80/path/to/myfile.html? key1=value1&key2=value2#SomewhereInTheDocument 为例。1. http://告诉浏览器使用何种协议。对于大部分Web资源,通常使用HTTP协议或其安全版本,HTTPS协议。另外,浏览器也知道如何处理其他协议。例如,mailto:协议指示浏览器打开邮件客户端;ftp:协议指示浏览器处理文件传输。 2. www.example.com既是一个域名,也代表管理该域名的机构。它指示了需要向网络上的哪一台主机发起请求。当然,也可以直接向主机的 IP address 地址发起请求。但直接使用IP地址的场景并不常见。 3. 端口。两个主机之间要发起TCP连接需要两个条件,主机+端口。它表示用于访问Web 服务器上资源入口。 4.路径。 /path/to/myfile.html是 Web服务器上资源的路径。以端口后面的第一个/开始,到?号之前结束,中间的每一个/都代表了层级(上下级)关系。这个URL的请求资源是一个html页面。 5.参数。?key1=value1&key2=value2是提供给Web服务器的额外参数。如果是GET请求,一般带有请求URL参数,如果是POST请求,则不会在路径后面直接加参数。这些参数是用&符号分隔的键/值对列表。key1 = value1是第一对,key2 = value2是第二对参数 6.锚点.#SomewhereInTheDocument是资源本身的某一部分的一个锚点。锚点代表资源内的一种"“书签",它给予浏览器显示位于该“加书签”点的内容的指示。例如,在HTML文档上,浏览器将滚动到定义锚点的那个点上;在视频或音频文档上,浏览器将转到锚点代表的那个时间。值得注意的是#号后面的部分,也称为片段标识符,永远不会与请求一起发送到服务器。 3.6 http 版本HTTP/1.0第一个在通讯中指定版本号的HTTP协议版本,常用于代理服务器。 成为了面向事务的应用层协议,该协议需要每请求一次响应建立并拆除一次连接, 引入了POST和HEAD命令 相对于一版本在一定程度上保障的数据的传输安全,是一个典型的串行连接事务。 缺点: 串行处理,效率低下HTTP/1.1引入持久连接机制并被默认采用,且更好的配合代理服务器工作 还支持管道方式同一连接下同时发送多个请求,以降低线路负载,提高传输速度 新增方法: PUT、PATCH、OPTIONS、DELETE 缺点: 同一TCP连接里,所有通信按次序进行,服务器只能顺序处理回应,如果前面处理过慢,会有许多请求排队,造成队头阻塞(Head-of-line blocking)HTTP/2.0HTTP/1.1 与HTTP/2.0 区别1、HTTP/2.0 采用二进制格式,而非文本格式 2、HTTP/2.0是完全的多路复用,非有序并阻塞的,只需要一个链接即可实现并行 3、使用报头压缩,HTTP/2降低了开销 4、HTTP/2让服务器可以将响应主动 "推送"到客户缓存中4. HTTPS协议4.1 https 协议介绍HTTPS(全称是 Hyper Text Transfer Protocol over SecureSocket Layer)是身披SSL/TLS 外壳的 HTTP。它在 HTTP 之上利用 SSL/TLS 建立安全的信道,加密数据传输。它被广泛用于互联网上安全敏感的通讯,例如电商、支付等应用对称加密:加密与解密用同一套密钥,如 DES、3DES 和 AES 等非对称加密:加密和解密所使用的密钥不同,如 RSA、DSA等HTTPS 是由 HTTP 和SSL/TLS协议构建的更为安全的网络协议TLS 记录层协议规定数据的最大大小限制为16KB 字节4.2 Cookie 和Session 会话HTTP协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;Session和Cookie的主要目的就是为了弥补HTTP的无状态特性。客户端请求服务端,服务端会为这次请求开辟一块内存空间,这个对象便是Session对象,存储结构为ConcurrentHashMap 。Session 弥补了HTTP无状态特性,服务器可以利用Session存储客户端在同一个会话期间的一些操作记录。
  • [技术干货] 【转载】RESTful API简介
    在学习RESTful 风格接口之前,即使你不知道它是什么,但你肯定会好奇它能解决什么问题?有什么应用场景?听完下面描述我想你就会明白:随着互联网和移动设备的发展,人们对Web应用的使用需求也增加,传统的动态页面由于低效率而渐渐被HTML+JavaScript(Ajax)的前后端分离所取代,并且安卓、IOS、小程序等形式客户端层出不穷,客户端的种类出现多元化,而客户端和服务端就需要接口进行通信,但接口的规范性就又成了一个问题: 所以一套结构清晰、符合标准、易于理解、扩展方便让大部分人都能够理解接受的接口风格就显得越来越重要,而RESTful风格的接口(RESTful API)刚好有以上特点,就逐渐被实践应用而变得流行起来。现在,RESTful是目前最流行的接口设计规范,在很多公司有着广泛的应用,其中Github 的API设计就是很标准的RESTful API,你可以参考学习。在开发实践中我们很多人可能还是使用传统API进行请求交互,很多人其实并不特别了解RESTful API,对RESTful API的认知可能会停留在:面向资源类型的是一种风格(误区)接口传递参数使用斜杠(/)分割而不用问号(?)传参。而其实一个很大的误区不要认为没有查询字符串就是RESTful API,也不要认为用了查询字符串就不是RESTful API,更不要认为用了JSON传输的API就是RESTful API。本教程将带你了解RESTful并用SpringBoot实战RESTful API,在实现本案例前,你需要保证你的电脑上拥有IDEA用来编写项目代码拥有Postman模拟请求进行测试拥有关系数据库MySQL 5.7拥有navicat对MySQL进行管理REST与RESTful API的关系REST(英文:Representational State Transfer,简称REST,直译过来表现层状态转换)是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。需要注意的是REST并没有一个明确的标准,而更像是一种设计的风格,满足这种设计风格的程序或接口我们称之为RESTful(从单词字面来看就是一个形容词)。所以RESTful API 就是满足REST架构风格的接口。REST架构特征大致有以下几个主要特征:以资源为基础 :资源可以是一个图片、音乐、一个XML格式、HTML格式或者JSON格式等网络上的一个实体,除了一些二进制的资源外普通的文本资源更多以JSON为载体、面向用户的一组数据(通常从数据库中查询而得到)。统一接口: 对资源的操作包括获取、创建、修改和删除,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。换言而知,使用RESTful风格的接口但从接口上你可能只能定位其资源,但是无法知晓它具体进行了什么操作,需要具体了解其发生了什么操作动作要从其HTTP请求方法类型上进行判断。具体的HTTP方法和方法含义如下:GET(SELECT):从服务器取出资源(一项或多项)。POST(CREATE):在服务器新建一个资源。PUT(UPDATE):在服务器更新资源(客户端提供完整资源数据)。PATCH(UPDATE):在服务器更新资源(客户端提供需要修改的资源数据)。DELETE(DELETE):从服务器删除资源。当然也有很多在具体使用的时候使用PUT表示更新。从请求的流程来看,RESTful API和传统API大致架构如下:URI指向资源:URI = Universal Resource Identifier 统一资源标志符,用来标识抽象或物理资源的一个紧凑字符串。URI包括URL和URN,在这里更多时候可能代指URL(统一资源定位符)。RESTful是面向资源的,每种资源可能由一个或多个URI对应,但一个URI只指向一种资源。无状态:服务器不能保存客户端的信息, 每一次从客户端发送的请求中,要包含所有必须的状态信息,会话信息由客户端保存, 服务器端根据这些状态信息来处理请求。 当客户端可以切换到一个新状态的时候发送请求信息, 当一个或者多个请求被发送之后, 客户端就处于一个状态变迁过程中。 每一个应用的状态描述可以被客户端用来初始化下一次的状态变迁。RESTful API设计规范既然了解了RESTful的一些规则和特性,那么具体该怎么去设计一个RESTful API呢?要从URL路径、HTTP请求动词、状态码和返回结果等方面详细考虑。至于其他的方面例如错误处理、过滤信息等规范这里就不详细介绍了。URL设计规范URL为统一资源定位器 ,接口属于服务端资源,首先要通过URL这个定位到资源才能去访问,而通常一个完整的URL组成由以下几个部分构成:URI = scheme "://" host ":" port "/" path [ "?" query ][ "#" fragment ]scheme: 指底层用的协议,如http、https、ftphost: 服务器的IP地址或者域名port: 端口,http默认为80端口path: 访问资源的路径,就是各种web 框架中定义的route路由query: 查询字符串,为发送给服务器的参数,在这里更多发送数据分页、排序等参数。fragment: 锚点,定位到页面的资源我们在设计API时URL的path是需要认真考虑的,而RESTful对path的设计做了一些规范,通常一个RESTful API的path组成如下:/{version}/{resources}/{resource_id}version:API版本号,有些版本号放置在头信息中也可以,通过控制版本号有利于应用迭代。resources:资源,RESTful API推荐用小写英文单词的复数形式。resource_id:资源的id,访问或操作该资源。当然,有时候可能资源级别较大,其下还可细分很多子资源也可以灵活设计URL的path,例如:/{version}/{resources}/{resource_id}/{subresources}/{subresource_id}此外,有时可能增删改查无法满足业务要求,可以在URL末尾加上action,例如/{version}/{resources}/{resource_id}/action其中action就是对资源的操作。从大体样式了解URL路径组成之后,对于RESTful API的URL具体设计的规范如下:不用大写字母,所有单词使用英文且小写。连字符用中杠"-"而不用下杠"_"正确使用 "/"表示层级关系,URL的层级不要过深,并且越靠前的层级应该相对越稳定结尾不要包含正斜杠分隔符"/"URL中不出现动词,用请求方式表示动作资源表示用复数不要用单数不要使用文件扩展名HTTP动词在RESTful API中,不同的HTTP请求方法有各自的含义,这里就展示GET,POST,PUT,DELETE几种请求API的设计与含义分析。针对不同操作,具体的含义如下:GET /collection:从服务器查询资源的列表(数组) GET /collection/resource:从服务器查询单个资源 POST /collection:在服务器创建新的资源 PUT /collection/resource:更新服务器资源 DELETE /collection/resource:从服务器删除资源在非RESTful风格的API中,我们通常使用GET请求和POST请求完成增删改查以及其他操作,查询和删除一般使用GET方式请求,更新和插入一般使用POST请求。从请求方式上无法知道API具体是干嘛的,所有在URL上都会有操作的动词来表示API进行的动作,例如:query,add,update,delete等等。而RESTful风格的API则要求在URL上都以名词的方式出现,从几种请求方式上就可以看出想要进行的操作,这点与非RESTful风格的API形成鲜明对比。在谈及GET,POST,PUT,DELETE的时候,就必须提一下接口的安全性和幂等性,其中安全性是指方法不会修改资源状态,即读的为安全的,写的操作为非安全的。而幂等性的意思是操作一次和操作多次的最终效果相同,客户端重复调用也只返回同一个结果。上述四个HTTP请求方法的安全性和幂等性如下:HTTP Method安全性幂等性解释GET安全幂等读操作安全,查询一次多次结果一致POST非安全非幂等写操作非安全,每多插入一次都会出现新结果PUT非安全幂等写操作非安全,一次和多次更新结果一致DELETE非安全幂等写操作非安全,一次和多次删除结果一致状态码和返回数据服务端处理完成后客户端也可能不知道具体成功了还是失败了,服务器响应时,包含状态码和返回数据两个部分。状态码我们首先要正确使用各类状态码来表示该请求的处理执行结果。状态码主要分为五大类:1xx:相关信息2xx:操作成功3xx:重定向4xx:客户端错误5xx:服务器错误每一大类有若干小类,状态码的种类比较多,而主要常用状态码罗列在下面:200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。202 Accepted -:表示一个请求已经进入后台排队(异步任务)204 NO CONTENT - [DELETE]:用户删除数据成功。400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。401 Unauthorized -:表示用户没有权限(令牌、用户名、密码错误)。403 Forbidden - 表示用户得到授权(与401错误相对),但是访问是被禁止的。404 NOT FOUND -:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。500 INTERNAL SERVER ERROR -:服务器发生错误,用户将无法判断发出的请求是否成功。返回结果针对不同操作,服务器向用户返回数据,而各个团队或公司封装的返回实体类也不同,但都返回JSON格式数据给客户端。
  • [问题求助] 鲲鹏镜像网址:http://arm.cloud-onlinelab.cn/为啥打不开了
    【功能模块】鲲鹏镜像网址:http://arm.cloud-onlinelab.cn/为啥打不开了【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] IIS PHP fastcgi模式 pathinfo取值错误任意代码执行漏洞修复方法
    描述:目标存在任意代码执行漏洞。漏洞形成原因:PHP默认配置有误,IS+PHP fastcgi模式在解析PHP文件时存在缺陷,导致任意文件会被服务器以PHP格式解析,从而导致任意代码执行。验证方式:打开目标页面后在原URL后添加/test.php示例:在http://www.xxx.com/123.jpg后输入/test.php并回车,使用抓包工具查看响应头信息,如果包含PHP/x.x.x即可证明漏洞存在。部分浏览器免费抓包工具:360浏览器:工具-->开发人员工具IE浏览器:工具-->F12开发人员工具Firefox:搜索下载Firebug插件-->F12呼出Chrome:点击右上角的菜单-->工具-->开发者工具危害:攻击者可以利用该漏洞直接在网站执行任意代码,可能直接控制网站服务器,盗取网站数据,影响网站的正常运营。最近360提示这个IIS+PHP fastcgi模式 pathinfo取值错误任意代码执行漏洞,这里根据提示修复了漏洞,具体方法如下以下是360给出的解决方案一、更换PHP默认的Fastcgi模式为ISAPI模式(只能运行于Windows环境)1.下载PHP的ZIP文件包,下载地址http://www.php.net(注意版本要对应)2.将sapi目录中的:php4isapi.dll复制到c:\php目录中3.进入虚拟主机管理平台的"网站管理"-"虚拟主机"--服务器设置中,修改PHP的影射,将原来的:.php,C:\PHP\php.exe,5,GET,HEAD,POST,TRACE|改成:.php,C:\PHP\php4isapi.dll,5,GET,HEAD,POST,TRACE|4.(IIS 6才需要)打开IIS管理器,点击Web服务扩展,点击php的属性,“要求的文件”---添中--选中“C:\PHP\php4isapi.dll”,确定后,PHP就可以调用。 脚本之家小编评语:一般不建议变更为ISAPI模式,效率很低.2003 php5.2.17可以使用isapi方式,2008 则建议直接修改php.ini配置文件把cgi.fix_pathinfo值改为0 就行了.二、在条件允许的情况下(咨询网站工程师),更改php.ini中的配置参数cgi.fix_pathinfo值为0建议用这个,但需要用服务器管理权限的,一般是vps或主机脚本之家小编评语:推荐这种方式,不论iis还是apache、nginx都很方便三、针对iis的解决方案此处客户的环境是windows server 2008R2的IIS,这里我在‘处理程序映射’里找到php的双击进入此界面进入‘请求限制’确定后就可以了。测试:在服务器上根目录新建一个phpinfo()的JPG文件test.jpg,访问http://www.xxx.com/test.jpg/1.php(test.jpg后面的php名字随便写),如果有漏洞则可以看到phpinfo()的信息,反之会返回404错误。四、使用360网站卫士Nginx pathinfo取值错误任意代码执行漏洞漏洞形成原因:由于Nginx默认配置有误,从而导致了一个任意代码执行漏洞.受影响版本:nginx 0.5.*nginx 0.6.*nginx 0.7 <= 0.7.65nginx 0.8 <= 0.8.37危害:黑客可以利用该漏洞直接在网站执行任意代码,从而有可能直接控制网站服务器,盗取网站数据,影响网站的正常运营。解决方案:升级Nginx至最新版本,官方网站:http://nginx.org/ 。转载自https://www.jb51.net/hack/552161.html
  • [技术交流] SpringCloud项目接入华为云微服务引擎CSE
    ### 一、SpringCloud项目架构介绍 #### 1.项目地址: [https://gitee.com/caichunfu/dtse-practice-microservice](https://gitee.com/caichunfu/dtse-practice-microservice) #### 2.项目技术栈如下: 1. 注册中心:Eureka 2. 网关:zuul 3. 声明式调用:feign 4. 存储:OBS、RDS for MySql ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20222/18/1645173154462340346.png) #### 3.项目结构: 1. dtse-commons:项目的公共工具类 2. dtse-eureka-server:eureka注册中心集群 3. dtse-feign-client:feign调用客户端 4. dtse-obs-storage:服务端,完成对obs的操作,包括上传和删除obs对象 5. dtse-system:消费端,实现用户登录、查询用户头像、更新用户头像(调用服务端请求) 6. dtse-zuul-getway:项目网关 ### 二、SpringCloud Huawei改造方案 #### 1.替换技术栈对比 | SpringCloud | SpringCloud-Huawei | | ------------------ | ------------------------------------------------------------ | | Eureka注册中心 | [华为云CSE微服务引擎](https://support.huaweicloud.com/devg-cse/cse_devg_0010.html) | | zuul网关 | SpringCloud Huawei Getway | | SpringCloud config | [CSE Config](https://support.huaweicloud.com/devg-cse/cse_devg_0022.html) | #### 2.SpringCloud Huawei技术文档 [https://github.com/huaweicloud/spring-cloud-huawei](https://github.com/huaweicloud/spring-cloud-huawei) #### 3.SpringCloud Huawei基础Demo [https://github.com/huaweicloud/spring-cloud-huawei-samples/tree/master/basic](https://github.com/huaweicloud/spring-cloud-huawei-samples/tree/master/basic) #### 4.SpringCloud Huawei版本对照 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20222/18/1645174723327212698.png) #### 5.改造前设置 1. 下载本地CSE,下载地址:[https://support.huaweicloud.com/devg-cse/cse_devg_0036.html](https://support.huaweicloud.com/devg-cse/cse_devg_0036.html) 2. 将maven镜像改为华为镜像仓库: ``` <mirror> <id>huaweicloud</id> <mirrorOf>*</mirrorOf> <url>https://repo.huaweicloud.com/repository/maven/</url> </mirror> ``` 3. 项目版本选定 - SpringBoot:2.3.7.RELEASE - SpringCloud:Hoxton.SR9 - SpringCloud Huawei:1.8.0-Hoxton #### 6.改造方案 1. 移除微服务项目里所有的eureka依赖,删掉application.yml里的eureka的配置 ``` <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> ``` 2. 导入SpringCloud Huawei依赖 - 父工程里导入 ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> ``` - 子工程里导入 ```xml <dependency> <groupId>com.huaweicloud</groupId> <artifactId>spring-cloud-starter-huawei-service-engine</artifactId> </dependency> ``` 3. 添加bootstrap.yaml配置文件 ```yaml spring: application: name: #微服务名 cloud: servicecomb: discovery: enabled: true address: http://127.0.0.1:30100 appName: basic-application serviceName: ${spring.application.name} version: 0.0.1 healthCheckInterval: 30 config: serverAddr: http://127.0.0.1:30110 serverType: kie ``` 4. 解决启动中的报错 4.1. 需要编写IClientConfig配置类: ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20222/18/1645176166364496834.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20222/18/1645176147975776732.png) 4.2. 配置类添加 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20222/18/1645176207306600820.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20222/18/1645176216784964848.png) 4.3. 微服务里无数据库配置: ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20222/18/1645176276185922690.png) 需要在启动类上加`@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})` 4.4. feign调用报错 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20222/18/1645176345773477730.png) SpringCloud Huawei当前版本对负载均衡支持不够友好,openfeign自带负载均衡,建议使用RestTemplate *以下解决方法来自本站的帖子[SpringCloud项目接入华为云微服务引擎CSE(一)](https://bbs.huaweicloud.com/forum/thread-178657-1-1.html)* 4.5. 修改openfeign调用为RestTemplate - 在dtse-system--usercontroller--UpdateIconImage路径对应的方法里注释掉openfeign调用代码 - 添加RestTemplate方式调用的代码 ```java //请求url String url = "http://xxxx:xxxx/xx/x"; ​ //构造请求头 HttpHeaders httpHeaders = new HttpHeaders(); HttpHeaders headers = httpHeaders; headers.setContentType(MediaType.MULTIPART_FORM_DATA); ​ //FileSystemResource将文件变成流以发送 File file = MultipartFileToFile.multipartFileToFile(multipartFile); FileSystemResource fileSystemResource = new FileSystemResource(file); ​ //构造请求体,使用LinkedMultiValueMap MultiValueMap<String, Object> resultMap = new LinkedMultiValueMap<>(); resultMap.add("file", fileSystemResource); resultMap.add("obsParamsJson", obsParamsJson); ​ //HttpEntity封装整个请求报文 HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(resultMap, headers); ​ //postForObject发送请求体 String objURL = restTemplate.postForObject(url, httpEntity, String.class); ``` **MultipartFile转File:** ```java import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; ​ public class MultipartFileToFile { /** * MultipartFile 转 File * * @param file * @throws Exception */ public static File multipartFileToFile(MultipartFile file) throws Exception { File toFile = null; if (file.equals("") || file.getSize() <= 0) { file = null; } else { InputStream ins = null; ins = file.getInputStream(); toFile = new File(file.getOriginalFilename()); inputStreamToFile(ins, toFile); ins.close(); } return toFile; } //获取流文件 private static void inputStreamToFile(InputStream ins, File file) { try { OutputStream os = new FileOutputStream(file); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); ins.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 删除本地临时文件 * @param file */ public static void delteTempFile(File file) { if (file != null) { File del = new File(file.toURI()); del.delete(); } } } ``` 4.6 网关改造 - 删除原项目zuul的依赖,添加springcloudgateway的依赖和springcloudhuawei提供的网关依赖 ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.huaweicloud</groupId> <artifactId>spring-cloud-starter-huawei-service-engine-gateway</artifactId> </dependency> ``` - 添加网关配置文件(注意uri不要用lb地址,当前版本涉及负载均衡的均有问题,暂时用固定地址) ```yaml spring: main: allow-bean-definition-overriding: true cloud: gateway: # 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。) routes: #路由标识(id:标识,具有唯一性) - id: dtse-system-route # 目标服务地址(uri:地址,请求转发后的地址) #uri: lb://dtse-system #不要用lb地址 uri: http://localhost:9090 filters: args: # 路由条件(predicates:断言,匹配 HTTP 请求内容) predicates: - Path=/** urifiler: login-uri: /login ``` - 定义全局过滤器实现鉴权 ```java @Component @Slf4j public class RouteConfiguration implements GlobalFilter, Ordered { ​ @Autowired JwtUtil jwtUtil; @Autowired URIFilter uriFilter; ​ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ​ ServerHttpRequest request = exchange.getRequest(); RequestPath path = request.getPath(); ​ // 2、登陆请求放行 if(path.value().contains(uriFilter.getLoginuri().get(0))){ System.out.println("登陆请求路经:request.getPath() = " + path.value()); log.info("登录"); return chain.filter(exchange); } //3、非登陆请求用户权限校验 String authorization = request.getHeaders().getFirst("Authorization"); if (!StringUtils.isEmpty((authorization))) { System.out.println("非登陆请求路径:request.getPath() = " + path.value()); //2、获取请求头中Token信息 String token = authorization.replace("Bearer", ""); ​ //3、Token校验 Claims claims = jwtUtil.parseToken(token) ; ​ //4、获取用户id,并将用户id传送到后端 if (claims == null) { try { throw new Exception(String.valueOf(ResultCode.UNAUTHENTICATED)); } catch (Exception e) { e.printStackTrace(); } return null; } String id = claims.getId(); ​ //5、添加用户请求头 request.mutate().header("userId",id).build(); return chain.filter(exchange); } ​ return chain.filter(exchange); } ​ @Override public int getOrder() { return 0; } } ``` - urifiler配置类 ```java @ConfigurationProperties(prefix = "urifiler", ignoreUnknownFields = false) @Data @Component public class URIFilter { private List<String> loginuri; } ``` 4.7 配置中心 在浏览器输入http://127.0.0.1:30103/,进入cse可视化界面,选择配置列表,创建配置项 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20222/21/1645427195901258209.png)
  • [技术干货] 【故障注入第五期】DemonCAT架构原理指导
    这期我们将会介绍一点技术底层的东西,每一期都希望能给大家带来满满的干货,废话不多说,进入正题!DemonCAT架构原理:整体描述计算全栈故障注入工具DemonCAT由如下架构图中的三部分组成,分别是 DemonCAT Portal、DemonCAT Cente和DemonCAT SUT(其中DemonCAT SUT中的主要部分称为DemonCAT Core,它是DemonCAT的核心部分)。Center、Portal以及SUT(Core)三部分通过网络协议HTTP或者SSH配合进行工作。Portal是DemonCAT使用时的主要入口,为用户提供交互UI,包括数据列表展示、输入文本框等内容:Portal 通过HTTP协议向Center发送请求,Center 对Portal的请求进行处理,当Center接收到Portal的故障注入请求后,Center 将根据请求内容,组装故障注入命令,通过SSH协议下发故障注入命令到SUT,下发完成后再将操作对象与结果入库保存;SUT在接收到故障注入命令后,执行命令解析工具(CFE),依据命令解析结果调用相应的故障注入程序,完成故障注入。PortalPortal是以Center为后端的Web页面,支持图形化的操作。与Center 配合起来可以大大降低故障注入的学习成本与复杂度。目前支持环境管理、故障模式库管理、任务管理以及工具信息获取的相关功能。Portal整体划分为5 个模块,其中Index为整个工具的总览与索引,通过该Index可快速导航至其他模块。“环境管理”为基本信息模块,主要提供故障注入的目标系统信息,它包含了环境的IP、账号、工具部署路径等内容。用户可在此模块录入被测环境信息、检测环境是否可连接、故障注入工具部署等功能。“故障模式库管理”同样为基本信息模块,故障模式信息由系统预置,代表着当 前系统支持的故障模式,可供用户查询、导出。 “任务管理”为核心的故障注入模块,通过关联环境信息与故障模式信息,创建 一条故障注入任务。任务运行前可在页面上,选择获取目标环境相关网卡、CPU 核数、 磁盘挂载点等故障注入的参数信息,并依据选择的这些参数信息与故障模式,拼接故 障注入命令,任务运行时由 Center 将拼接好的故障注入命令下发至 SUT 之中,完成故 障注入。 “报告管理”为故障注入之后提供结果性的分析说明,主要用于指导分析被测系 统的可靠性。CenterCenter 是一个利用 python 语言开发的后端 HTTP 服务,具有清晰的模块划分与松 耦合,结合 Core 可以远程的为目标环境下发故障注入的命令,运行故障注入程序。 Center 的整体模块大致与 Portal 相对应,每一个前端模块都有与之对应的后端模块, 降低架构之间的耦合度,Center 模块的功能执行都由 Portal 发起 HTTP 请求触发。 Center 被划分为四个模块,其中“Controller 模块”主要负责故障任务的调度、创建 以及工具部署等与故障注入业务紧密相关的核心功能。 “故障模式库管理”主要提供故障模式的维护、导入/导出、CRUD 等操作接口。该 模块相对独立,因此单独作为一个模块单独维护。“报告管理”提供了报告数据采集触发、报告分析、报告生成以及报告查看等基 本功能。 “通信模块”则为上述三个模块服务,例如,为任务执行的命令下发提供支持, 为报告数据采集提供上报接口。 “MySQL 数据库”与“编译内核库”,前者为数据持久化提供支持,后者为不同的 被测环境提供工具支持。CoreCore 是以 CFE 为故障注入程序入口(CFE 是由 2012 可靠性技术实验室内源的故障 注入框架程序),在CFE原有故障注入程序基础上,自主扩展开发 Linux OS、容器、 计算芯片、AI 应用级故障注入能力。Core 主要分为两大部分,一部分是故障注入入口程序(Dispatcher),另一部分是各个模块的故障注入程序。 Dispatcher 起到命令解析与分发的作用,SUT 从 Center 收到故障注入命令后,调用Dispatcher进行命令解析,Dispatcher 依据解析结果调用对应的故障注入程序,完成故障注入。 故障注入程序包含了从硬件至上层软件的注入能力,每一个模块的故障注入程序都可单独运行,不强依赖于 Dispatcher,具备松耦合的结构形式;也正是基于此可以 轻松集成其他故障注入工具至 Core 之中。DemonCAT 旨在提供计算产业功能最完备、使用最灵活、故障模式最丰富的故障注 入软件工具,为鲲鹏产品和生态的可靠性领先提供技术及能力支撑。我们从下期开始就会介绍故障注入工具DemonCAT的功能、使用方法、FAQ等内容,记得关注哦~
  • [技术交流] SpringCloud项目接入华为云微服务引擎CSE(二)
    目录1.    业务架构说明2.    开发环境3.    本地CSE开发环境搭建    3.1.    下载本地CSE    3.2.    解压,双击cse.exe    3.3.    登录CSE控制台4.    接入前准备    4.1.    下载实验工程源码,使用IDEA打开    4.2.    IDE 配置    4.3.    Maven配置5.    服务端CSE接入    5.1.    主pom修改    5.2.    dtse-feign-client接入    5.3.    dtse-obs-storage接入    5.4.    dtse_system接入    5.5.    dtse-zuuL-gateway网关接入6.    前端服务接入7.    其他接入中问题记录    1. 业务架构说明    项目Gitee地址     后端   https://gitee.com/caichunfu/dtse-practice-microservice     前端   https://gitee.com/HuaweiCloudDeveloper/dtse-practice-frontEnd/tree/devCSE改造前:    • 微服务包含4个微服务模块:zuul-gateway模块、Eureka注册中心、dtse-system模块、obs-storage模块;其中dtse-system模块、obs-storage模块是业务模块。    • 用户发送请求,微服务网关(zuul-gateway) 过滤器根据请求URI,路由和负载均衡请求到不同服务;同时利用JWT进行token校验请求的合法性。    • Eureka注册中心管理zuul-gateway、dtse-system、obs-storage微服务状态;    • dtse-system与obs-storage之间通过feign进行内部接口调用改造技术路径    • 引入使用spring-cloud-huawei    • 使用华为云CSE服务替换Eureka注册中心的功能    • 使用Spring Cloud Gateway替换zuul网关基线版本选择    查看 spring-cloud-huawei官网地址:    https://github.com/huaweicloud/spring-cloud-huawei     通过实践master分支与openFeign存在兼容问题,所以本次实践以Hoxton为基线版本,Hoxten与openFeign不存在兼容性问题    由于Spring Cloud Huawei与zuul调试中发现有兼容问题,所以将网关替换成Spring Cloud Gateway2. 开发环境JDKOpenjdk 1.8.0_312Maven3.6.3IDEA2021.2.2CSELocal-CSE-2.1.3-windows-amd64.zipspring-boot2.3.5.RELEASEspring-cloudHoxton.SR9spring-cloud-huawei1.8.0-Hoxton3. 本地CSE开发环境搭建3.1. 下载本地CSE    https://cse-bucket.obs.cn-north-1.myhuaweicloud.com/LocalCSE/Local-CSE-2.1.3-windows-amd64.zip 3.2. 解压,双击cse.exe3.3. 登录CSE控制台    http://127.0.0.1:30103 4. 接入前准备4.1. 下载实验工程源码,使用IDEA打开    源码地址 https://gitee.com/caichunfu/dtse-practice-microservice 4.2. IDE 配置4.3. Maven配置    使用华为云Maven5. 服务端CSE接入改造5.1. 主pom改造5.1.1. 主pom引入依赖5.1.2. 使用CSE做为注册中心,删除相关模块和依赖    a、删除eureka-server    b、引入华为spring cloud` <properties> <java.version>1.8</java.version> <spring-boot.version>2.3.5.RELEASE</spring-boot.version> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> <spring-cloud-huawei.version>1.8.0-Hoxton</spring-cloud-huawei.version> </properties>`<!-- configure user spring cloud / spring boot versions --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- configure spring cloud huawei version --> <dependency> <groupId>com.huaweicloud</groupId> <artifactId>spring-cloud-huawei-bom</artifactId> <version>${spring-cloud-huawei.version}</version> <type>pom</type> <scope>import</scope> </dependency>5.2. dtse-feign-client接入5.2.1. 引入openfeign版本<dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-openfeign-core</artifactId> <version>3.0.3</version> </dependency>5.3. dtse-obs-storage接入5.3.1. Pom文件处理5.3.1.1. 删除zuul依赖    删除spring-cloud-starter-netflix-zuul,增加spring-cloud-starter-netflix-ribbon <!--wmh add--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> <version>2.2.0.RELEASE</version> <scope>compile</scope> <exclusions> <exclusion> <artifactId>ribbon-transport</artifactId> <groupId>com.netflix.ribbon</groupId> </exclusion> <exclusion> <artifactId>rxnetty</artifactId> <groupId>io.reactivex</groupId> </exclusion> </exclusions> <optional>true</optional> </dependency> <!-- wmh delete <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> -->5.3.2. 引入CSE配置5.3.3. 处理com.netflix.client.config.IClientConfig‘ that could not be found问题    出现这种问题是因为启动类找不到(扫描)其他类的路径,处理方法有多种,我这边使用的是方法二    方法一:把启动类放在其他类文件包的同一级,而不要放在上一级    方法二:在启动类的标签中加入启动扫描的路径如下:    方法三: new个IClientConfig类,不过需要初始化,不然会出现空指针5.3.3.1. 方法二指定扫描路径:    SpringBootApplication指定扫描路径@SpringBootApplication(scanBasePackages = {"com.huaweicloud.controller","com.huaweicloud.service","com.huaweicloud.commons","com.huaweicloud.persistent"}) @EnableFeignClients @EnableDiscoveryClient public class OBSStorageMain { public static void main(String[] args) { SpringApplication.run(OBSStorageMain.class, args); } }5.3.3.2. 方法三增加config类:    IClientConfig 类,重点来了,就是这个类,如果不自己定义(openFeign 是可以自定义这个类的,然后自己初始化),那么就千万不要自己去创建一个 bean 出来,然后自己加上注解定义成配置类如下:@Configuration public class IClientConfig { @Bean public DefaultClientConfigImpl iClientConfig(){ return new DefaultClientConfigImpl(); } }    这玩意千万不要在程序里自己创建出来,可能很多初学者不是很懂,一开始有配置了这个,结果又只是单纯的 return 了一个没有任何属性的 DefaultClientConfigImpl 对象,然后 openFeign 就会使用你创建的这个对象,结果去初始化的时候,就会在源码里面报空指针异常,把这玩意去掉,基本就可以了,如果要自己定义,那记得把里面该有的属性都要初始化值。5.4. Dtse_system接入5.4.1. Pom文件处理    删除eureka client,引入华为service engine <!-- <dependency>--> <!-- <groupId>org.springframework.cloud</groupId>--> <!-- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>--> <!-- </dependency>--> <dependency> <groupId>com.huaweicloud</groupId> <artifactId>spring-cloud-starter-huawei-service-engine</artifactId> </dependency>删除netflix-hystrix <!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>-->5.4.2. 引入CSE配置5.4.3. 处理com.netflix.client.config.IClientConfig‘ that could not be found问题package com.huaweicloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication(scanBasePackages = {"com.huaweicloud.controller","com.huaweicloud.commons","com.huaweicloud.service","com.huaweicloud.persistent"}) @EnableFeignClients //开启feign客户端调用支持 public class SystemMain { public static void main(String[] args) { SpringApplication.run(SystemMain.class, args); } }5.5. Dtse-zuuL-gateway网关接入    使用spring cloud huawei与zuul有兼容性问题,所以切换到Spring Cloud Gateway5.5.1. Pom文件处理    spring-boot-starter-web排除spring-webmvc包,删除spring-cloud-starter-netflix-zuul包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </exclusion> </exclusions> </dependency>    删除eureka-client包 <!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>-->    引入spring-cloud-starter-gateway和huawei-service-engine-gateway <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.huaweicloud</groupId> <artifactId>spring-cloud-starter-huawei-service-engine-gateway</artifactId> </dependency>5.5.2. 引入CSE配置    修改网关配置5.5.3. 修改网关全局过滤器package com.huaweicloud.filter; import com.huaweicloud.commons.outhUtils.JwtUtil; import com.huaweicloud.commons.response.ResultCode; import com.huaweicloud.config.URIFilter; import io.jsonwebtoken.Claims; import org.apache.servicecomb.foundation.common.utils.JsonUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.lang.annotation.Annotation; @Component public class RouteConfiguration implements GlobalFilter, Ordered { @Autowired JwtUtil jwtUtil; @Autowired URIFilter uriFilter; private String writeJson(Object o) { try { return JsonUtils.writeValueAsString(o); } catch (Exception ee) { ee.printStackTrace(); } return null; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("access filter......"); ServerHttpRequest request = exchange.getRequest(); RequestPath path = request.getPath(); System.out.println("收到请求路经:request.getPath() = " + path.value()); // 2、登陆请求放行 if(path.value().contains(uriFilter.getLoginuri().get(0))){ System.out.println("登陆请求路经:request.getPath() = " + path.value()); return chain.filter(exchange); } //3、非登陆请求用户权限校验 String authorization = request.getHeaders().getFirst("Authorization"); if (!StringUtils.isEmpty((authorization))) { System.out.println("非登陆请求路径:request.getPath() = " + path.value()); //2、获取请求头中Token信息 String token = authorization.replace("Bearer", ""); //3、Token校验 Claims claims = jwtUtil.parseToken(token) ; //4、获取用户id,并将用户id传送到后端 if (claims == null) { try { throw new Exception(String.valueOf(ResultCode.UNAUTHENTICATED)); } catch (Exception e) { e.printStackTrace(); } return null; } String id = claims.getId(); //5、添加用户请求头 request.mutate().header("userId",id).build(); return chain.filter(exchange); } return chain.filter(exchange); } @Override public int getOrder() { return 0; } }5.5.4. 处理com.netflix.client.config.IClientConfig‘ that could not be found问题package com.huaweicloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class},scanBasePackages = {"com.huaweicloud.config","com.huaweicloud.filter","com.huaweicloud.commons"}) @EnableDiscoveryClient public class SpringCloudGatewayMain { public static void main(String[] args) throws Exception { SpringApplication.run(SpringCloudGatewayMain.class, args); } }6. 前端服务接入    修改vue.config.js,配置服务网关服务的端口    修改login.Vue,通过网关经过systemmain统一接入,所以修改登录url    修改图片上传接口,和获取用户信息接口7. 其他接入中问题记录7.1. 方便openFeign调试,openFeign调试,增加Feign日志类config 增加类FeignConfigurationpackage com.huaweicloud.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfiguration { /** * 日志级别 * * @return */ @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }7.2. Idea 编译报错:Ambiguous method call. Both...    IDEA Settings... > Plugins > 搜索已安装的插件Hrisey Plugins > 禁用该插件 7.3. gateway报错org. springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodec Configurer' that could not be found      spring-cloud-starter-gateway依赖与mvc是不兼容的,如果要引用spring-boot-starter-web需要把mvc排除 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </exclusion> </exclusions> </dependency>7.4. OpenFeign调用报错com.netflix.client.ClientException: Load balancer does not have available server for client: DTSE-OBS-STORAGE    yml 文件里面的服务名,要和 @FeignClient(value = "xxx") 里面的 xxx 一样,切记别弄错,大小写也要一致
  • [数据管理] 使用http delete请求来删除文件有时会失败
    http会返回The remote server returned an error : (403) Forbidden,然后重试又可能会成功.因为我是要通过代码去删除多个文件,比如删除100个文件,其中可能有30个会删除失败,然后再尝试删除这30个,又会剩下10个上传失败.我设置的过期时间是10分钟,代码也没有修改过,接口参数,Key什么的应该都是对的,不知道怎么样才能解决这个403的问题.目前我只能多次重试直到全部删除,但是太麻烦了.
  • [技术交流] SpringCloud项目接入华为云微服务引擎CSE(一)
    1.项目Gitee地址https://gitee.com/caichunfu/dtse-practice-microservice2.运行环境JDK1.8Maven3.6.3本地CSE引擎下载地址:https://support.huaweicloud.com/devg-cse/cse_devg_0036.html3.注意事项踩过的一些坑:3.1依赖导入报错需要把Maven的中央仓库地址改为华为中央仓库地址,修改setting.xml文件      <mirror>          <id>huaweicloud</id>          <mirrorOf>*,!HuaweiCloudSDK</mirrorOf>          <url>https://repo.huaweicloud.com/repository/maven/</url>     </mirror>3.2版本问题官网地址:https://github.com/huaweicloud/spring-cloud-huawei1)使用Hoxton分支,项目启动时ribbon的启动类会报错,建议使用master分支com.huaweicloud.servicecomb.discovery.ribbon.ServiceCombRibbonClientConfiguration required a bean of type 'com.netflix.client.config.IClientConfig' that could not be found.2)master分支1.8.0-2020.0.x与网关存在兼容性问题,建议使用1.6.1-2020.0.x版本3)master分支需要使用 springcloud 2020.x 的版本,这个版本移除了Netflix相关的组件,所以需要对组件进行替换3.3本次测试使用的版本   <properties>       <spring-boot.version>2.5.3</spring-boot.version>       <spring-cloud.version>2020.0.4</spring-cloud.version>       <spring-cloud-huawei.version>1.6.1-2020.0.x</spring-cloud-huawei.version>       <servicecomb.version>2.5.0</servicecomb.version>       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>   </properties>4.引入依赖4.1父工程引入 <dependencyManagement>      <!-- configure spring cloud huawei version -->      <dependency>        <groupId>com.huaweicloud</groupId>        <artifactId>spring-cloud-huawei-bom</artifactId>        <version>${spring-cloud-huawei.version}</version>        <type>pom</type>        <scope>import</scope>      </dependency>    </dependencies>  </dependencyManagement>4.2子工程引入,并删除eureka相关依赖<dependency>  <groupId>com.huaweicloud</groupId>  <artifactId>spring-cloud-starter-huawei-service-engine</artifactId></dependency>5.创建bootstrap.yml文件spring: application:   name: #微服务名 cloud:   servicecomb:     discovery:       enabled: true       address: http://127.0.0.1:30100       appName: #应用名       serviceName: ${spring.application.name}       version: 0.0.1       healthCheckInterval: 30     config:       serverAddr: http://127.0.0.1:30110       serverType: kie6.启动类添加注解@EnableDiscoveryClient启动本地微服引擎,访问http://127.0.0.1:30103/进入微服务引擎管理界面,查看服务是否注册成功7.启动前遇到的问题7.1报错信息:could not be registered. A bean with that name has already been defined in URL ​Action:​Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true​无法注册。URL中已经定义了一个具有该名称的bean​考虑重命名一个bean,或者通过设置Spring实现重写。主要的允许bean定义覆盖=true解决方案:yml配置文件添加配置spring: main:   allow-bean-definition-overriding: true7.2报错信息:Action:​Correct the classpath of your application so that it contains a single, compatible version of io.micrometer.core.instrument.distribution.DistributionStatisticConfig$Builder解决方案:pom.xml里面的依赖包有重复,需要将重复的依赖包删除7.3报错信息:此问题只会在使用Honxton版本时出现,建议使用master分支1.6.1-2020.0.x版本Parameter 0 of method ribbonServerList in com.huaweicloud.servicecomb.discovery.ribbon.ServiceCombRibbonClientConfiguration required a bean of type 'com.netflix.client.config.IClientConfig' that could not be found.​Action:​Consider defining a bean of type 'com.netflix.client.config.IClientConfig' in your configuration.解决方案:IClientConfig 类,这个类定义时,不能直接 return 一个没有任何属性的 DefaultClientConfigImpl 对象, openFeign 会在源码里面使用这个对象,报空指针异常,如果要自己定义,需要初始化里面该有的属性@Configurationpublic class IClientConfig {   @Bean   public DefaultClientConfigImpl iClientConfig(){           //网上很多的错误写法       //return new DefaultClientConfigImp();               //加上getClientConfigWithDefaultValues初始化参数       return DefaultClientConfigImpl.getClientConfigWithDefaultValues();   }}8.Feign远程调用使用SpringCloudHuawei做远程调用时会报错,可能兼容性存在问题为了验证问题还原了SpringCloud项目,openfeign调用不会报错报错信息:org.apache.servicecomb.service.center.client.exception.OperationException: get service instances list fails, statusCode = 400; message = Bad Request; content = {"errorCode":"400012","errorMessage":"Micro-service does not exist","detail":"Consumer[30b75156753bc55385a7ae74d0611c77fc5f7522][development/dtse-practice-microservice/dtse-system/0.0.1] find provider[development/dtse-practice-microservice/default/0+] failed, provider does not exist"}​Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: default解决---暂未解决:已对接后端技术专家,暂未解决,解决后会更新进度9.改为RestTemplate方式调用前端参数为MultipartFile和JSON,请求类型为POST    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)    public String upLoadOneFile(@RequestPart("file") MultipartFile file, @RequestParam("obsParamsJson") String obsParamsJson) throws IOException {                OBSStorageParams obsParams = JSON.parseObject(obsParamsJson, OBSStorageParams.class);​        String objURL = obsService.uploadOneFile(file.getInputStream(), obsParams);        return objURL;   }RestTemplate调用代码        //请求url        String url = "http://xxxx:xxxx/xx/x";​        //构造请求头        HttpHeaders httpHeaders = new HttpHeaders();        HttpHeaders headers = httpHeaders;        headers.setContentType(MediaType.MULTIPART_FORM_DATA);​        //FileSystemResource将文件变成流以发送        File file = MultipartFileToFile.multipartFileToFile(multipartFile);        FileSystemResource fileSystemResource = new FileSystemResource(file);​        //构造请求体,使用LinkedMultiValueMap        MultiValueMap<String, Object> resultMap = new LinkedMultiValueMap<>();        resultMap.add("file", fileSystemResource);        resultMap.add("obsParamsJson", obsParamsJson);​        //HttpEntity封装整个请求报文        HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(resultMap, headers);​        //postForObject发送请求体        String objURL = restTemplate.postForObject(url, httpEntity, String.class);MultipartFile转File import org.springframework.web.multipart.MultipartFile; import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;import java.io.OutputStream;​public class MultipartFileToFile {    /**     * MultipartFile 转 File     *     * @param file     * @throws Exception     */    public static File multipartFileToFile(MultipartFile file) throws Exception {        File toFile = null;        if (file.equals("") || file.getSize() <= 0) {            file = null;       } else {            InputStream ins = null;            ins = file.getInputStream();            toFile = new File(file.getOriginalFilename());            inputStreamToFile(ins, toFile);            ins.close();       }        return toFile;   }    //获取流文件    private static void inputStreamToFile(InputStream ins, File file) {        try {            OutputStream os = new FileOutputStream(file);            int bytesRead = 0;            byte[] buffer = new byte[8192];            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {                os.write(buffer, 0, bytesRead);           }            os.close();            ins.close();       } catch (Exception e) {            e.printStackTrace();       }   }    /**     * 删除本地临时文件     * @param file     */    public static void delteTempFile(File file) {    if (file != null) {        File del = new File(file.toURI());        del.delete();   }}}10.网关改造官网的说明:Spring Cloud Huawei Hoxton分支只提供Spring Cloud Gateway基于Ribbon的负载均衡,及其配套的基于流量治理和灰度发布功能。 Spring Cloud Huawei master(2020.0.x版本)分支只提供Spring Cloud Gateway基于Spring Cloud LoadBalance的负载均衡, 及其配套的基于流量治理和灰度发布功能。建议Spring Cloud Gateway升级到2020.0.x版本。由于原项目使用的网关为Zuul,需要改为Spring Cloud Gateway10.1删除原项目zuul的依赖,添加springcloudgateway的依赖和springcloudhuawei提供的网关依赖<dependency>   <groupId>org.springframework.cloud</groupId>   <artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency>   <groupId>com.huaweicloud</groupId>   <artifactId>spring-cloud-starter-huawei-service-engine-gateway</artifactId></dependency>10.2添加网关配置文件spring: main:   allow-bean-definition-overriding: true cloud:   gateway:    # 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)     routes:     #路由标识(id:标识,具有唯一性)       - id: dtse-system-route        # 目标服务地址(uri:地址,请求转发后的地址)         uri: lb://dtse-system         filters:             args: # 路由条件(predicates:断言,匹配 HTTP 请求内容)         predicates:           - Path=/**urifiler: login-uri: /login10.3定义全局过滤器实现鉴权package com.huaweicloud.filter;​import com.huaweicloud.commons.outhUtils.JwtUtil;import com.huaweicloud.commons.response.ResultCode;import com.huaweicloud.config.URIFilter;import io.jsonwebtoken.Claims;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.gateway.filter.GatewayFilterChain;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.core.Ordered;import org.springframework.http.server.RequestPath;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;​@Component@Slf4jpublic class RouteConfiguration implements GlobalFilter, Ordered {​    @Autowired    JwtUtil jwtUtil;    @Autowired    URIFilter uriFilter;​    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {​        ServerHttpRequest request = exchange.getRequest();        RequestPath path = request.getPath();​        // 2、登陆请求放行        if(path.value().contains(uriFilter.getLoginuri().get(0))){            System.out.println("登陆请求路经:request.getPath() = " + path.value());            log.info("登录");            return chain.filter(exchange);       }        //3、非登陆请求用户权限校验        String authorization = request.getHeaders().getFirst("Authorization");        if (!StringUtils.isEmpty((authorization))) {            System.out.println("非登陆请求路径:request.getPath() = " + path.value());            //2、获取请求头中Token信息            String token = authorization.replace("Bearer", "");​            //3、Token校验            Claims claims = jwtUtil.parseToken(token) ;​            //4、获取用户id,并将用户id传送到后端            if (claims == null) {                try {                    throw new Exception(String.valueOf(ResultCode.UNAUTHENTICATED));               } catch (Exception e) {                    e.printStackTrace();               }                return  null;           }            String id = claims.getId();​            //5、添加用户请求头            request.mutate().header("userId",id).build();            return chain.filter(exchange);       }​        return chain.filter(exchange);   }​    @Override    public int getOrder() {        return 0;   }}10.4urifiler配置类@ConfigurationProperties(prefix = "urifiler", ignoreUnknownFields = false)@Data@Componentpublic class URIFilter {   private List<String> loginuri;}11.配置中心的使用访问http://127.0.0.1:30103/进入微服务本地引擎管理界面选择配置列表,创建配置项
  • [问题求助] 【MapReduce服务】【HTTP REST API】创建目录
    【HTTP REST API】curl创建目录【操作步骤&问题现象】在客户端执行:curl -i -X PUT --negotiate -u: "http://host56:25002/webhdfs/v1/huawei?user.name=pocuser&op=MKDIRS"【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [API集成编排] 【ADC产品】【REST出站】出站请求HTTP的head中有个sign值,要求使用MD5(时间戳,app-id),如何每次请求自
    【ADC产品】【REST出站】出站请求HTTP的head中有个sign值,要求使用MD5(时间戳,app-id),如何每次请求自动计算出时间戳,和MD5(时间戳,app-id)?
  • [技术干货] http和https的关系与优缺点等区别分析(附图解)
    最近网站全部开启了https,但看很多原来开始https的网站现在都回归http了,不得不找一下相关资料,实在搞不懂现在什么情况注意⚠️https协议需要到ca申请证书,一般免费证书很少,需要交费。http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443HTTP的缺点HTTP主要有这些不足:通信使用明文,内容可能被窃听不验证通信方身份,因此有可能遭遇伪装无法验证报文的完整性,所有有可能已篡改HTTP + 加密 + 认证 + 完整性保护 = HTTPSHTTPS是身披SSL外壳的HTTP通常情况下HTTP是直接和TCP层进行通信的。当使用SSL(安全套阶字)时,则演变成HTTP先和SSL通信,SSL再和TCP通信的了。加密技术讲解SSL前,科普一下加密方法,SSL采用的是一种叫做公开密钥加密的加密处理方式对称加密加密和解密用的一个密钥的方式称为对称加密,也叫做共享密钥加密对称加密在发送加密信息时也需要将密钥发送给对方,但这样可以被攻击者截取,就不安全啦~非对称加密非对称加密又称作公开密钥加密,它很好的解决了对称加密密钥被截取的问题。非对称加密采用一对非对称的密钥,一把叫做私有密钥,一把叫做共有密钥。使用非对称加密,发送密文一方使用对方的共有密钥进行加密处理,对方收到加密信息后,再使用自己的私有密钥进行解密。HTTPS采用混合加密机制HTTPS采用对称加密和非对称加密所混合的加密机制。若密钥能安全交换,那么有可能仅考虑非对称加密。但是非对称加密与对称加密相比,处理速度相对较慢。公开密钥的认证使用数字证书认证机构和其颁布的公开密钥证书进行认证。即让第三方独立机构进行验证。私有密钥是保存在服务器端的~注意⚠️:认证是要钱的!!!HTTPS安全通信机制下图是完整的HTTPS的通信过程为什么HTTPS不是那么普及1.加密通信与纯文本通信相比,消耗更多的CPU和内存资源2.购买证书是要钱的!3.少许对客户端有要求的情况下,会要求客户端也必须有一个证书.这里客户端证书,其实就类似表示个人信息的时候,除了用户名/密码, 还有一个CA 认证过的身份. 应为个人证书一般来说上别人无法模拟的,所有这样能够更深的确认自己的身份目前少数个人银行的专业版是这种做法,具体证书可能是拿U盘作为一个备份的载体HTTPS 一定是繁琐的1.本来简单的http协议,一个get一个response. 由于https 要还密钥和确认加密算法的需要.单握手就需要6/7 个往返,任何应用中,过多的round trip 肯定影响性能.2.接下来才是具体的http协议,每一次响应或者请求, 都要求客户端和服务端对会话的内容做加密/解密,尽管对称加密/解密效率比较高,可是仍然要消耗过多的CPU,为此有专门的SSL 芯片. 如果CPU 信能比较低的话,肯定会降低性能,从而不能serve 更多的请求,加密后数据量的影响. 所以,才会出现那么多的安全认证提示网站使用https的好处与坏处今天我们重点讨论一下网站使用https的好处与坏处,如果有对https原理不了解的小伙伴,可以参考百恒之前写的http和https的关系与区别(附图解)。https的好处1、SEO方面谷歌曾在2014年8月份调整搜索引擎算法,并称“比起同等http网站,采用https加密的网站在搜索结果中的排名将会更高。”百度也于去年也在站长平台声明,https有一定的排名优待。2、安全性尽管https并非绝对安全,掌握根证书的机构、掌握加密算法的组织同样可以进行中间人形式的公司,但https仍是现行架构下最安全的解决方案,主要有以下几个好处:(1)、使用https协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;(2)、https协议是由SSL+http协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。(3)、https是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。https的坏处虽然说https有很大的优势,但其相对来说,还是有些不足之处,具体来说,有以下2点:1、SEO方面据ACM CoNEXT数据显示,使用https协议会使页面的加载时间延长近50%,增加10%到20%的耗电,此外,https协议还会影响缓存,增加数据开销和功耗,甚至已有安全措施也会受到影响也会因此而受到影响。而https协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方案几乎起不到什么作用。最关键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行。2、经济方面(1)、SSL证书需要去,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。(2)、SSL证书通常需要绑定IP,不能再同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗(SSL有扩展可以部分解决这个问题,但是比较麻烦,而且要求浏览器、操作系统支持,Windows XP就不支持这个扩展,考虑到XP的装机量,这个特性几乎没用)。(3)、https连接缓存不如http高效,大流量网站如非必要也不会采用,流量成本太高。(4)、https连接服务器端资源占用高很多,支持访客稍多的网站需要投入更大的成本,如果全部采用https,基于大部分计算资源闲置的假设的VPS的平均成本会上去。(5)、https协议握手阶段比较费时,对网站的响应速度有负面影响,如非必要,没有理由牺牲用户体验。当然了,现在https已经趋于成熟,很多缺点是可以优化和弥补的。比如:打开速度问题完全可以通过CDN加速解决,很多IDC也在着手推出免费证书和一站式https搭建服务,不久https成本将会大大缩小!转载自https://www.jb51.net/yunying/622931.html