开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

详解 HTTP20 及 HTTPS 协议

发表于 2021-11-26

开始之前

众所周知, HTTP协议是没有安全加密的协议,因为使用明文传输,所以使用HTTP协议的站点很容易会被窃听、篡改,劫持;而伴随着互联网的发展,网络上承载了越来越多也越来越重要的数据,金融,商业,支付,机密数据等等,数据安全的重要性越来越凸显,越来越多的网站通过启用HTTPS来保障web数据传输的安全性。此外,HTTP2.0 作为新一代的WEB协议,以重量级的新特性带来更好,性能更高的web服务体验。本文基于运维视角在阐述解析HTTP2.0协议相比较HTTP1.1的优点的同时讲述HTTPS协议的原理,并结合实际业务场景作为案例,目的是可以通过本文掌握HTTP2.0及HTTPS协议,了解原理,具备定位排查问题,调优的能力。

一、HTTP1.1 VS HTTP2

严格意义上HTTP2.0和HTTPS并没有什么必然的联系,只是搭配使用更香一些,HTTP2 是1999年HTTP1.1之后的第一次更新。没错,现在还在被大量使用的HTTP1.1协议已经是20年前的东西了,对于互联网的技术变革速度而言,很明显,HTTP1.1已经非常古老。HTTP2具有更好的效率和资源利用率,尤其适用于页面比较重,有大量资源加载的场景(公司的业务属于典型的场景),根据网络上的测试数据,在大量图片、资源需要加载的场景下,HTTP2解决HTTP1.1的线头阻塞(一次请求交互必须等待前一次请求交互的完成)问题相比HTTP1.1可以达到5倍以上的速度提升,目前,淘宝,天猫,京东等平台都已启用HTTP2,如果是页面存在大量惊天资源需要加载的情况,启用HTTP2.0,绝对物超所值。

  1. HTTP2.0新特性

二进制分帧

  • HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。 HTTP / 1 的请求和响应报文,都是由起始行,首部和实体正文(可选)组成,各部分之间以文本换行符分隔。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。虽然HTTP1.1 的纯文本形式看起来一目了然,非常直观,但这只是对人的体验而言,事实上这种方式存在多义性,例如大小写、空白字符、回车换行、多字少字等,程序在处理的时候需要复杂的处理。效率比较低且麻烦,而二进制的方式,只是0和1,可以严格规定字段大小,顺序,标志位等,不存在歧义,提交小,同时也提升了数据在网络中传输的效率。

多路复用

HTTP1.1中一次请求与响应的交互必须要等待前面的请求交互完成,否则后面的只能等待。如果遇到某一个资源加载耗时较久,就会拖累整个站点的加载速度。而在HTTP2.0中,一次链接成功后,只要链接还没断开,那么 client 端就可以在一个链接中并发的发起多个请求,且每个请求的响应不需要等待其他请求。

  • 多路复用,代替原来的序列和阻塞机制。所有就是请求的都是通过一个 TCP连接并发完成。 HTTP 1.x 中,如果想并发多个请求,必须使用多个 TCP 链接,且浏览器为了控制资源,还会对单个域名有 6-8个的TCP链接请求限制。常见的一个情况是,如果一个页面需要加载的静态资源过多,因为只有6-8个并发,所以客户端浏览器的等待时间就会比较久,例如政采云主站。开启页面需要几十个请求来获取静态资源。在这里耗费了不少的时间。

服务器推送

  • HTTP2中服务端可以在发送页面HTML时主动推送其它资源,而不用等到浏览器解析到相应位置,发起请求再响应。例如服务端可以主动把JS和CSS文件推送给客户端,而不需要客户端解析HTML时再发送这些请求。

HTTP1.1的方式,客户端浏览器解析到哪里需要什么资源再加载什么资源,在HTTP2中,服务端可以主动推送,结合业务场景,服务端可以先把关键的首要的资源首先推送给客户端。对用户体验来说,只需要一次HTTP请求,浏览器就得到了首要资源,页面性能大大提升。当然,如果一次性推送了太多的资源,因为浏览器需要处理所有推送过来的资源。反而会拖累性能。所以需要根据业务场景做权衡。

头部压缩

  • HTTP 1.1请求的大小变得越来越大,有时甚至会大于TCP窗口的初始大小,因为它们需要等待带着ACK的响应回来以后才能继续被发送。HTTP/2对消息头采用HPACK(专为http/2头部设计的压缩格式)进行压缩传输,能够节省消息头占用的网络的流量。而HTTP/1.x每次请求,都会携带大量冗余头信息,浪费了很多带宽资源。像cookie这些信息,每个请求都会附带,产生了很多不必要的资源消耗。为了减少这块的资源消耗并提升性能, HTTP/2对这些首部采取了压缩策略:
    • HTTP/2在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送;
    • 首部表在HTTP/2的连接存续期内始终存在,由客户端和服务器共同渐进地更新;
    • 每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值。
  1. ALPN 应用协议协商

  • HTTPS 握手的时候,客户端会首先告诉服务端自己支持的协议,由服务端选择客户端服务端都支持的协议。如果服务端Nginx开启了HTTP2支持,服务端会选择HTTP2协议,否则,服务端就会选择HTTP1.1协议来通讯。

二、SSL/TLS 模型

  1. TLS版本

历史版本的TLS/SSL因为安全漏洞和性能问题已经慢慢成为历史的尘埃,目前应用最为广泛的是TLS1.2版本,而TLS 1.3 是对于TLS1.2的升级,提供更强大的安全性和更高的性能。

  1. 加密套件

  • 加密套件:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA解释
    • 基于TLS协议,使用ECDHE和RSA作为秘钥交换算法,加密算法是AES GCM,秘钥长度128位,哈希算法使用sha256
    • AES-GCM 是目前常用的分组加密算法,但是其有一个缺点就是计算量大,导致性能和电量开销比较大。为了解决这个问题,Intel 推出了名为 AES NI(Advanced Encryption Standard new instructions)的 x86 指令拓展集,从硬件上提供对 AES 的支持。对于支持 AES NI 指令的主机来说,使用 AES-GCM 是最佳选择。AES-GCM的优点在于可以利用多核提高加解密性能。
  1. 对称加密和非对称加密

对称加密算法

对称加密算法是采用单钥解密的加密方法,加密解密都是用这一个密码。

  • 常用,目前公认安全级别最高的加密算法:AES
    • AES的秘钥位数: 128、192、256、512,秘钥长度越长安全性越高但是同时会影响加解密性能。目前来说128位的秘钥长度已经足够安全。
  • 对称加密算法的优点:计算量小、加密速度快、加密效率高。
  • 对称加密算法的缺点:数据传送前,发送方和接收方必须商定好秘钥,想要接收方可以加解密数据就必须要告知秘钥。会带来秘钥管理上的诸多不便。

非对称加密算法

非对称加密算法是有一对公钥、私钥,公钥用于加密,私钥用于解密,当然也可以针对不同场景用私钥加密,公钥解密。

  • 应用广泛的算法:RSA
  • 非对称加密算法的优点:
    • 相对于对称加密,加解密是分开的,一个简单场景,私钥保存,公钥公开,只有持有私钥才可以解密,典型的场景例如SSH认证。
    • 灵活。秘钥管理方便。
  • 非对称加密算法的缺点:
    • 计算量大,加密性能低于对称加密。非对称加密算法的执行效率制约着实际应用,大部分应用在身份验证中

题外话:技术角度分析当年WannaCry勒索病毒中的对称加密和非对称加密的应用场景

当年WannaCry勒索病毒的破坏性毋庸置疑,一夜之间,全球的政府、银行、企业被祸害的不胜枚举,在这种场景下,为什么业界的技术大牛却没能提供有效修复和恢复方案哪?

  • 勒索病毒的实现原理:抛开零日漏洞(这个只跟感染传播途径有关,与加密技术无关)WannaCry在计算机上运行后,会使用AES对称加密加密指定后缀的文件,这里选用AES的原因在于对称加密的计算量比较小,速度快,试想要全盘加密指定后缀的文件,这个量级是比较大的,所以对称加密是比较合适的选择,但是问题来了,对称加密在加密时会暴露解密密码,如果这个密码存在客户端中会被拿到然后直接解开,利用障眼法挑战不了全球的技术大神,如果删除密码,则会导致加密文件永远也无法解密,这样就不能达到勒索的目的。所以WannaCry的开发者又同时利用了RSA非对称加密算法加密了AES的秘钥然后存放在加密文件的文件头部。这样的话,WannaCry的作者只需要掌握RSA的私钥,即可通过私钥先解密文件头部中的AES秘钥,再通过AES秘钥解密文件。那么为什么不直接通过RSA加密文件哪?其实也有一些勒索病毒是这么干的,这样的缺点是加密过程因为非对称加密需要大量的计算所以加密耗时较久,加密过程中大量消耗系统资源也容易被发现。
  • AES的破解难度:暴力破解的话用地球的每一粒沙子做出存储运算的话不夸张的说在太阳膨胀或者地球毁灭之前你都算不出来。简单来说,目前没有可能被爆破。
  1. HTTPS 握手过程

    1. Client-hello 阶段
Client-hello 是TCP链接建立后客户端发送的第一条消息,主要目的是把客户端支持的功能和选项高速服务端。


    + 浏览器中完成地址输入后, 解析域名获得 IP Host 地址, 浏览器会与此 Host 的443(默认, 如果指定其他端口则会连接此端口) 三次握手建立TCP连接,然后进入TLS 握手协议的 Client-hello。这一步骤中浏览器会将客户端支持的加密套件,目标Host等信息发送给服务器, 并会附上一份随机生成的 session ticket1.

1
markdown复制代码 + ALPN协商: 应用层可以协商在安全连接层之上使用什么协议, 避免了额外的往返通讯,  如上图
  • Server-hello阶段
+ 服务器收到浏览器发送来的 TLS 握手请求后, 存储浏览器发送的session ticket1, 然后根据发送来的 host 寻找对应的服务器证书, 然后会将服务器证书, 服务器从Client Hello提供的客户端支持的加密套件清单中按照优先级选择一个双方都支持的套件(如果服务端支持的套件和client支持的套件交集为空则握手失败), 和一份随机生成的 session ticket2 返回给浏览器.

image.png

Client-hello和server-hello的步骤很像是买东西: 客户端: 我有多少钱,能支付宝也能微信付款, 服务端:需要xxx RMB,我们使用支付宝吧。

  • Cipher-spec 阶段

经过Client Hello和Server Hello 客户端和服务端完成了加密套件的协商。进入Cipher-spec 阶段会核验证书的有效性并

+ 浏览器收到服务器返回的证书后, 会验证证书有效性. 验证步骤如下:
    - 验证证书有效期
    - 验证证书域名与实际的host是否匹配。
    - 验证证书吊销状态(CRL+OCSP)确认证书是否被吊销。
    - 验证证书颁发机构, 如果颁发机构是中间证书(基本都是), 再验证中间证书的有效期/颁发机构/吊销状态. 一直验证到最后一层证书, 中途任何一个环节不通过都会提示不信任。
    - 若检查通过, 随机生成一份 session ticket 3 (这是浏览器生成的第二份 ticket), 通过返回证书中的公钥, 用协商的加密算法加密, 返回给服务器.同时浏览器用 session ticket 1(浏览器) & session ticket 2(服务器) & session ticket 3(浏浏览器) 组合成 session key。
  • 内容传输阶段
+ TLS 连接建立完成, 在连接销毁前, 浏览器与服务器的交互数据均通过 session key 来进行对称加密.
  • HTTPS握手过程抓包:

前三行为TCP三次握手,第四行客户端发起Client hello, 第五行服务端ack回复, 第六行Server Hello,第9行Cipher-spec阶段进行证书校验,完成握手之后第13行进入数据交互阶段。

  1. HSTS

通常访问网址的时候我们大多不会刻意的在前面写上https,也很少会关注我们是通过HTTP协议还是HTTPS协议在浏览。而要求https访问的站点,在用户通过http访问的时候大多以重定向的方式重定向到HTTPS地址,而如果我劫持了用户流量,拦截向https的重定向请求,然后担当一个代理的角色,如实转发客户端请求并返回,但是客户端跟中间人的交互采用的是明文的HTTP协议,由于没有建立SSL连接,所以客户端提交的信息都会暴露。基于此问题,是国际互联网工程组织 IETF 发布了HSTS的安全策略机制,强制让浏览器使用HTTPS与站点进行通信。

  • HSTS(HTTP Strict Transport Security)的作用是强制客户端(如浏览器)使用HTTPS与服务器创建连接。HSTS主要是通过服务器发送响应头的方式来控制浏览器操作:
    • 当客户端通过 HTTPS 发出请求时,服务器会在返回的 HTTP 响应头中包含 Strict-Transport-Security 字段(HSTS的开关由服务端控制)。
    • 浏览器接收到这样的信息之后,在一定期限内对该网站的任何请求都会以 HTTPS 发起(浏览器内部307跳转),而不会以 HTTP发起再由服务器重定向到 HTTPS。

三、SSL 证书

SSL证书特指由受信任的数字证书颁发机构(CA)颁发的数字证书。SSL证书具备身份验证(DV/OV/EV)和数据传输加密功能。

  1. CA的公信力

  • 所有人只需要通过开源的openssl就可以很容易的生成根证书然后进行签发,而且几乎不需要花费什么成本。甚至也可以配置到web服务器上提供HTTPS服务,只是不被浏览器信任而已。为什么我们还需要付出价值不菲成本去购买CA机构颁发的证书哪?在证书体系中CA证书的根证书非常重要,假如一个人拿到了CA的根证书私钥,那理论上就可以以上帝视角,解密HTTPS数据,通过中间人劫持,篡改数据。所以目前的CA根证书具有非常高的安全要求,一个CA的机构,想要通过审查,私钥起码有365天 x 24小时无间断的保安巡逻。门禁卡+指纹虹膜等生物识别双重安保,每一次记录都会写入不可修改的日志系统,CA私钥本身物理隔离,CA的加密模组保证私钥从物理上不可被复制只能用来签发。可想而知,个人生成的CA不可能会有这么高的安全级别。
  • CA归根结底是靠公信力得以存活的。如果不被信任,那就和个人签发的证书没有什么分别。一个悲哀的案例:国内的沃通本来是一家有CA资质的受信任的根证书机构,也是全中国唯一的CA,因为违规签发github.com的证书(这个行为的风险是,持有github.com证书的人或者组织,可以解密窃听、劫持github.com)被火狐浏览器,谷歌浏览器停止信任。从而沦为下级代理商。
  1. 机构角色

  • CA 根证书机构 (CA,certification authority) 可存在ROOT CA 一级CA、二级CA…等等。多级CA可以理解成根CA为了分销而将证书的签发权限下发给下级CA,根CA通常不会直接签发证书。
  • RA 证书注册机构(Registration Authority,RA)相当于代理商,代理CA标识和鉴别证书申请者,同意或拒绝证书申请,在某些环境下主动吊销或挂起证书,处理订户吊销或挂起其证书的请求,同意或拒绝用户更新其证书或密钥的请求。但是并不能签发证书。例如阿里云就是一个RA。
  • SRCA 不受信任的CA, 其实私人或者不受信任的机构颁发的证书都是这种类型,早期的12306就是用的这种证书。
  1. 证书颁发过程

  1. 证书类型

DV(Domain validated)

  • 域名验证证书只验证域名,因为只需要验证域名的所有权,颁发速度最快。几乎准实时,Let’s Encrypt的免费证书就是这种类型。

OV(Organization validated)

  • 组织验证证书,除了验证域名之外还会验证组织,例如如果要购买OV证书,不仅会验证域名所有权,还会验证申请证书时填写的组织是否是正确的。所以颁发速度较慢,通常3天左右,价格也比DV证书很多。目前政采云使用的就是此种类型的证书。颁发速度较慢,需要3-5个工作日。

EV(Extended validated)

  • 扩展验证证书,对浏览器非常友好,会直接在浏览器地址栏中显示出组织名称,比较利于品牌建设,也号称是安全级别最高的顶级证书,当然这也是成本最高的证书类型。EV证书的申请核验非常严格,需要5-7个工作日的时间,除DV/OV证书的核验外还要经过第三方审查,法务证明文件等,而且EV没有通配符版本的证书。只能购买单域名版。
  1. 证书链

web服务器例如Nginx在向浏览器发送证书的时候需要发送2个证书,一个是Intermediates证书,一个是域名证书,根证书不需要,因为根证书是操作系统内置的。首先发送域名证书,再发送Intermediates证书,浏览器会负责核验签发Intermediates证书的根证书是不是有效的受信任的。

根证书(root)

  • windows、安卓、linux等主流系统,基本上一年以上才会更新一次根证书库。大部分浏览器使用的是操作系统的根证书库,也有例外,例如火狐浏览器,谷歌浏览器维护的就是自己的CA证书库。一个新的根证书是很难再短时间内获得信任的。

Intermediates证书

  • 通常CA机构不会直接签发域名证书,而是通过授权二级CA来签发证书,而二级CA可以再授权三级CA….这种非CQ直接签发的证书就叫做Intermediates证书。这类证书可以有很多级,没有限制,但是在验证证书有效性的时候,会一层一层的验证,直到找到CA根证书为止。而目前购买的证书中大多Intermediates证书只有一级。例如:

域名证书

  • 域名证书分为公钥和私钥,在浏览器与WEB服务器交互式,WEB服务器会将公钥返回给浏览器。
  1. 证书吊销

  • 证书吊销列表(Certificate Revocation List ) 简称CRL, CRL中包含了:证书颁发机构信息吊销列表失效时间和下一次更新时间。

  • Online Certificate Status Protocol,缩写:OCSP:在线证书状态协议,CRL的替代品,由于OCSP响应包含的信息少于CRL,因此减轻了网络和客户端资源的负担, 需要解析的信息也更少。

四、HTTP2.0时代带来的运维挑战

1. 最佳CP: HTTP2 + HTTPS

https其实就是建构在SSL/TLS之上的 http协议,所以要比较https比http多用多少服务器资源,这个主要看SSL/TLS本身消耗多少服务器CPU资源。网络部分:http使用TCP 三次握手建立连接,客户端和服务器需要交换3个包,https除了 TCP 的三个包,还要加上 ssl握手需要的9个包,所以一共是12个包。CPU部分因为HTTPS有加解密过程,所以会消耗更多的资源。而HTTP2.0 标准中,虽然没有强制提出要使用加密(HTTPS)但是目前主流浏览器,chrome、火狐等都已经公开宣布只支持加密的HTTP2,所以目前互联网上能见到的HTTP2基本都是基于HTTPS协议的。HTTPS保证了传输的安全性却造成了额外的性能开销,而HTTP2的出现正好通过多路复用,头部压缩等特性大大的提升了传输性能。不得不说,HTTP2 + HTTPS是新一代web服务的最佳组合。

2. 一次证书链不全引发的问题

  • 现象:公司切换新域名,在阿里云直接部署证书到SLB后个别客户反馈手机打开官网提示不受信任的证书。但是电脑端chrome打开又一切正常。
  • 问题解析:操作系统中通常都包含了受信任的CA,但是很多证书通常不是由CA直接签发的。而是使用中间层证书进行签名。这就会导致如果你部署的证书中没有包含中间层证书,就会因为证书链不全而不被信任,当然chrome和火狐两大主流的浏览器中已经包含了大多的中间层证书所以访问时没有问题,但是像安卓的各种发行版里面的浏览器,未必包含二级证书,如果web服务端没有包含中间证书且使用的浏览器中也没有包含中间证书,就会导致信任问题。

3. 证书安全

  • 在部署HTTPS后,很容易被忽视的一个问题是证书本身的安全保障。HTTPS的私钥泄露,会产生诸多的安全问题。上文中的内容可以获知,拿到了私钥,在满足一定条件的情况下,用户与服务端的交互基本处于裸奔的状态,用户账密,订单交易信息都可能会被拦截。因此,CA签发下来的SSL证书本身也有必要管理起来,不能随意复制,放置。

4. 证书监控

  • 通过curl命令可以获取HTTPS握手的整个过程,以及证书详情。通常我们通过获取expire date来监控证书过期时间。如果curl命令的错误输出不为0 排除网络问题,就可能是证书不被信任。当然也可以不用curl命令,通过openssl或者编写脚本来实现监控,原理一样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
yaml复制代码 wangxun@wangxun ~ curl https://zcygov.cn -vv
* Rebuilt URL to: https://zcygov.cn/
* Trying 101.37.44.108...
* TCP_NODELAY set
* Connected to zcygov.cn (101.37.44.108) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=CN; ST=\U6D59\U6C5F\U7701; L=\U676D\U5DDE\U5E02; O=\U653F\U91C7\U4E91\U6709\U9650\U516C\U53F8; OU=IT; CN=*.zcygov.cn
* start date: May 7 00:00:00 2019 GMT
* expire date: May 6 12:00:00 2021 GMT
* subjectAltName: host "zcygov.cn" matched cert's "zcygov.cn"
* issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=GeoTrust RSA CA 2018
* SSL certificate verify ok.
> GET / HTTP/1.1
> Host: zcygov.cn
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Date: Wed, 07 Aug 2019 07:16:47 GMT
< Content-Type: text/html
< Content-Length: 191
< Connection: keep-alive
< Set-Cookie: acw_tc=76b20feb15651622072312298e0526c6496462332328e0842a443f28ecb7be;path=/;HttpOnly;Max-Age=2678401
< Location: https://www.zcygov.cn/
<
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>openresty/1.13.6.1</center>
</body>
</html>
* Connection #0 to host zcygov.cn left intact

6. Nginx下的HTTP2及HTTPS配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ini复制代码server {
listen 4433 ssl;
listen [::]:4433 ssl;
server_name cai-inc.com;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains"; # 启用HSTS
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 服务端支持的TLS版本
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
# 服务端支持的和不支持的加密套件。
ssl_certificate "/etc/letsencrypt/certs/fullchain.pem";
ssl_certificate_key "/etc/letsencrypt/certs/privkey.pem";
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:8899;
}

}

总结

  • HTTP2.0和HTTPS是一组很好的搭配,HTTP2.0为HTTP1.1提效, HTTPS来保障链接的安全性。在应对页面大量资源加载的情况下使用HTTP2.0可以明显的提升页面加载效率。
  • HTTPS证书的体系的核心是信任的CA机构,同样的域名的HTTPS证书的私钥泄露也将导致安全风险,因为部署在用户侧,域名证书私钥需要妥善保管。
  • 证书部署时需要部署完整的证书链, 证书链不全会导致在一些老旧的设备上存在证书校验无法通过的问题。
  • 伪造根证书,又被操作系统所信任是很可怕的,这意味着你的所有请求在根证书私钥的拥有者眼中是裸奔的。
  • 证书不可信可以导致很大的稳定性事故, 所以证书的监控很有必要

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

PHP 设计模式八 注册树模式

发表于 2021-11-26

「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」

课程背景

  • 最近完成一个项目 对于代码分层有了一丢丢了解 但是架构设计合理性上存在问题
  • 万物看本质基本功硬 底层知识扎实才能写出更优质的代码 才能走得更远
  • 作为努力奔跑的程序员 又一次的去温习 思考 设计模式等基础的php知识
  • 因为掘金粑粑有奖励,所以把学习的笔记整理记录并分享了出来。

正文开始

前面我们介绍了单例模式 工厂模式。

  • 类先实现单例模式保证每次请求只会初始化一次,节约系统高性能
  • 工厂模式中定义统一的方法获取单例中的实例。
    这里继续编写下一层,注册树模式。

介绍

如果每一个地方都直接调用工厂创建对象,那每次使用的时候都得去调用工厂模式创建。我们可以把最基本的,常用的类。放在一颗树上,并在初始方法中完成所有基本类的初始化。这样当我们需要使用某一个类的时候,直接从树上拿下一个直接使用即可。

开始编写

Register.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
php复制代码<?php
namespace Liiy;

class Register{

protected static $objects; //保存所有的数组

//设置变量
public static function set($alias, $object){
if(!self::$objects[$alias]){
self::$objects[$alias] = $object;
}
}

//获取变量
public static function get($alias){
return self::$objects[$alias];
}

//卸载类
public static function _unset($alias){
unset(self::$objects[$alias]);
}
}

注册树定义以后,只是定义了入口出口。具体树上要放些啥内容。还是要看开发者如何去定义的。
此时我们假设在创建db的时候把它添加到注册数中。

1
2
3
4
5
6
7
8
9
10
11
12
php复制代码<?php
namespace Liiy;

class Factory{

public static function createDb(){
$db = Db::getInstance();
Register::set('db1',$db); //添加到注册树中

return $db;
}
}

那样在入口文件中,我们就可以直接通过别名,拿到想要的实例对象了。

1
2
3
php复制代码Factory::createDb();  //具体的初始化业务而定
$db = Register::get('db1');
var_dump($db);

image.png

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

PHP 设计模式七 单例模式

发表于 2021-11-26

「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」

课程背景

  • 最近完成一个项目 对于代码分层有了一丢丢了解 但是架构设计合理性上存在问题
  • 万物看本质基本功硬 底层知识扎实才能写出更优质的代码 才能走得更远
  • 作为努力奔跑的程序员 又一次的去温习 思考 设计模式等基础的php知识
  • 因为掘金粑粑有奖励,所以把学习的笔记整理记录并分享了出来。

正文开始

上面介绍了一些php的基础知识,这里开始正式介绍一些设计模式。

单例模式

比如说数据库链接的操作,链接数据库很是消耗资源。如果我们不处理创建数据库的操作,每次都用new创建。那一个请求 响应会花费更长的时间。这就直接影响到了整个站点体验。

1 私有化构造方法,就保证当前类没有办法用new创建。

image.png
此时当使用new创建对象的时候会报错。

2 提供出口给别的方法调用

1
2
3
php复制代码    static function getInstance(){
return new self;
}

3 私有变量保存对象
image.png

此时当我们访问对象时候,访问的是同一个对象。

image.png

image.png
4 完成单例以后,结合之前编写的工厂模式。工厂模式中也不可以使用new关键字创建了。也需要调用指定的实例化方法的方式。

1
2
3
4
5
6
7
8
9
php复制代码<?php
namespace Liiy;

class Factory{

public static function createDb(){
return Db::getInstance();
}
}

总结这样通过工厂类方法创建出来的对象都是单例对象,提高了执行的效率。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

HarmonyOS(鸿蒙)——Text(文本)组件介绍

发表于 2021-11-26

「这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战」

一、简介

Text是用来显示字符串的组件,在界面上显示为一块文本区域。Text作为一个基本组件,有很多扩展,常见的有按钮组件Button,文本编辑组件TextField。

Text组件继承自Componet。

华为官方学习地址:

developer.harmonyos.com/cn/docs/doc…

二、属性

Text组件支持的属性比较多,但是由于其非常重要,因此每个属性我会根据官网文档一一实现。

2.1 text

属性名称 中文描述 取值 取值说明 使用案例
text 显示文本 string类型 可以直接设置文本字串,也可以引用string资源(推荐使用)。 ohos:text=”熄屏时间”ohos:text=”$string:test_str”

代码演示:

1
2
3
4
5
ini复制代码 <Text
ohos:id="$+id:text"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="李子捌"/>

显示 效果:

2.2 text_size

属性名称 中文描述 取值 取值说明 使用案例
text_size 文本大小 float类型 表示字体大小的float类型。可以是浮点数值,其默认单位为px;也可以是带px/vp/fp单位的浮点数值;也可以引用float资源。 ohos:text_size=”30”ohos:text_size=”16fp”ohos:text_size=”$float:size_value”

代码演示:

1
2
3
4
5
6
ini复制代码 <Text
ohos:id="$+id:text"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="150px"
ohos:text_size="150px"/>

显示 效果:

2.3 text_color

属性名称 中文描述 取值 取值说明 使用案例
text_color 文本颜色 color类型 可以直接设置色值,也可以引用color资源。 ohos:text_color=”#A8FFFFFF”ohos:text_color=”$color:black”

代码演示:

1
2
3
4
5
6
7
8
ini复制代码<Text
ohos:id="$+id:text"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="blue"
ohos:text_size="100px"
ohos:text_color="blue"
/>

演示效果:

使用HEX效果:

2.4 text_font

属性名称 中文描述 取值 取值说明 使用案例
text_font 字体 sans-serif 可以设置的字体如表中所列。 ohos:text_font=”HwChinese-medium”
sans-serif-medium
HwChinese-medium
sans-serif-condensed
sans-serif-condensed-medium
monospace

代码演示:

1
2
3
4
5
6
7
8
9
ini复制代码<Text
ohos:id="$+id:text"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="李子捌"
ohos:text_size="100px"
ohos:text_color="#000000"
ohos:text_font="HwChinese-medium"
/>

演示效果:

2.5 italic

属性名称 中文描述 取值 取值说明 使用案例
italic 文本是否斜体字体 boolean类型 可以直接设置true/false,也可以引用boolean资源。 ohos:italic=”true”ohos:italic=”$boolean:true”

代码演示:

1
2
3
4
5
6
7
8
9
10
ini复制代码<Text
ohos:id="$+id:text"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="李子捌"
ohos:text_size="100px"
ohos:text_color="#000000"
ohos:text_font="sans-serif"
ohos:italic="true"
/>

演示效果:

2.6 text_weight

属性名称 中文描述 取值 取值说明 使用案例
text_weight 字重 integer类型 表示字体大小的integer类型。也可以引用integer资源。 ohos:text_weight=”100”ohos:text_weight=”$integer:100”

代码演示:

1
2
3
4
5
6
7
8
9
10
11
ini复制代码    <Text
ohos:id="$+id:text"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="Liziba"
ohos:text_size="100px"
ohos:text_color="#0000FF"
ohos:text_font="serif"
ohos:italic="true"
ohos:text_weight="100"
/>

演示效果:

text_weight = 100

text_weight = 600

2.7 background_element

常用的背景如常见的文本背景、按钮背景,可以采用XML格式放置在graphic目录下。

在Project窗口,打开“entry > src > main > resources > base”,右键点击“graphic”文件夹,选择“New > File”,命名为“background_text.xml”,在background_text.xml中定义文本的背景。


代码演示:

1
2
3
4
5
6
7
8
9
10
xml复制代码<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">

<!--设置圆角弧度-->
<corners ohos:radius="20px"/>
<!--设置背景色-->
<solid ohos:color="#878787"/>

</shape>

演示效果:

2.8 text_alignment

属性名称 中文描述 取值 取值说明 使用案例
text_alignment 文本对齐方式 left 表示文本靠左对齐。 可以设置取值项如表中所列,也可以使用“
top 表示文本靠顶部对齐。
right 表示文本靠右对齐。
bottom 表示文本靠底部对齐。
horizontal_center 表示文本水平居中对齐。
vertical_center 表示文本垂直居中对齐。
center 表示文本居中对齐。
start 表示文本靠起始端对齐。
end 表示文本靠结尾端对齐。

代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ini复制代码<!-- 结合width和height可以很方便的查看text_alignment的效果 -->
<Text
ohos:id="$+id:text"
ohos:width="300vp"
ohos:height="100vp"
ohos:text="Liziba"
ohos:text_size="100px"
ohos:text_color="#0000FF"
ohos:text_font="serif"
ohos:italic="true"
ohos:text_weight="100"
ohos:left_margin="15vp"
ohos:bottom_margin="15vp"
ohos:right_padding="15vp"
ohos:left_padding="15vp"
ohos:text_alignment="bottom|horizontal_center"
ohos:background_element="$graphic:background_text"
/>

演示效果:

2.9 multiple_lines

属性名称 中文描述 取值 取值说明 使用案例
multiple_lines 多行模式设置 boolean类型 可以直接设置true/false,也可以引用boolean资源。 ohos:multiple_lines=”true”ohos:multiple_lines=”$boolean:true”

代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ini复制代码<!--multiple_lines换行-->
<Text
ohos:id="$+id:text"
ohos:width="300px"
ohos:height="match_content"
ohos:text="LizibaLiziba"
ohos:text_size="100px"
ohos:text_color="#0000FF"
ohos:text_font="serif"
ohos:italic="true"
ohos:text_weight="100"
ohos:max_text_lines="2"
ohos:multiple_lines="true"
ohos:background_element="$graphic:background_text"
/>

演示效果:

2.10 max_text_lines

属性名称 中文描述 取值 取值说明 使用案例
max_text_lines 文本最大行数 integer类型 可以直接设置整型数值,也可以引用integer资源。 ohos:max_text_lines=”2”ohos:max_text_lines=”$integer:two”

代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ini复制代码<!--
multiple_lines 换行
max_text_lines 最多展示行数
LizibaLizibaLiziba 三个只展示了2行
-->
<Text
ohos:id="$+id:text"
ohos:width="300px"
ohos:height="match_content"
ohos:text="LizibaLizibaLiziba"
ohos:text_size="100px"
ohos:text_color="#0000FF"
ohos:text_font="serif"
ohos:italic="true"
ohos:text_weight="100"
ohos:multiple_lines="true"
ohos:max_text_lines="2"
ohos:background_element="$graphic:background_text"
/>

演示效果:

2.11 auto_font_size

属性名称 中文描述 取值 取值说明 使用案例
auto_font_size 是否支持文本自动调整文本字体大小 boolean类型 可以直接设置true/false,也可以引用boolean资源。 ohos:auto_font_size=”true”ohos:auto_font_size=”$boolean:true”

代码演示:

方法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ini复制代码<!--
auto_font_size 自动调节文字大小
max_text_lines="1" 我这里设置行数为1,就非常容易展示效果
-->
<Text
ohos:id="$+id:text"
ohos:width="300px"
ohos:height="match_content"
ohos:text="LizibaLizibaLiziba"
ohos:text_size="100px"
ohos:text_color="#0000FF"
ohos:text_font="serif"
ohos:italic="true"
ohos:text_weight="100"
ohos:max_text_lines="1"
ohos:auto_font_size="true"
ohos:background_element="$graphic:background_text"
/>

演示效果:

方法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ini复制代码<!--
auto_font_size 自动调节文字大小
max_text_lines="1" 我这里设置行数为1,就非常容易展示效果
-->
<Text
ohos:id="$+id:text"
ohos:width="300px"
ohos:height="match_content"
ohos:text="L"
ohos:text_size="100px"
ohos:text_color="#0000FF"
ohos:text_font="serif"
ohos:italic="true"
ohos:text_weight="100"
ohos:max_text_lines="1"
ohos:auto_font_size="true"
ohos:background_element="$graphic:background_text"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
scala复制代码package com.liziba.demo.slice;

import com.liziba.demo.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.agp.components.Component;
import ohos.agp.components.Text;

public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);

// 每次点击修改内容,演示字体的自动调整
Text text = (Text) this.findComponentById(ResourceTable.Id_text);
text.setClickedListener(component -> {
text.setText(text.getText() + "L");
});
}
}

演示效果:

开始点击前

连续点击后

2.12 AutoScrolling

当文本过长时,可以设置跑马灯效果,实现文本滚动显示。前提是文本换行关闭且最大显示行数为1,默认情况下即可满足前提要求。

代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
ini复制代码<Text
ohos:id="$+id:text"
ohos:width="300px"
ohos:height="match_content"
ohos:text="LizibaLizibaLizibaLiziba"
ohos:text_size="100px"
ohos:text_color="#0000FF"
ohos:text_font="serif"
ohos:italic="true"
ohos:text_weight="100"
ohos:background_element="$graphic:background_text"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
java复制代码package com.liziba.demo.slice;

import com.liziba.demo.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.Text;

public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);

Text text = (Text) this.findComponentById(ResourceTable.Id_text);
// 跑马灯效果
text.setTruncationMode(Text.TruncationMode.AUTO_SCROLLING);
// 始终处于自动滚动状态
text.setAutoScrollingCount(Text.AUTO_SCROLLING_FOREVER);
//启动跑马灯效果
text.startAutoScrolling();
}

@Override
public void onActive() {
super.onActive();
}

@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}

}

演示效果:

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

HarmonyOS(鸿蒙)——单击事件

发表于 2021-11-26

「这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战」

一、简介

1.1 什么是组件

组件就是文本、按钮、图片等元素的统称

1.2 什么是事件

事件就是可以被组件识别的操作,常见的事件有单击、双击、长按和滑动等。

1.3 什么是单击事件

单击事件又称点击事件,单击事件是我们平时操作过程中触发的最多的事件。

1.4 实现步骤

实现HarmonyOS(鸿蒙)的单击事件主要分为四个步骤:

  1. 定义组件,给组件分配唯一ID,之后通过ID定位组件
  2. 给定义的组件绑定单击事件
  1. 实现ClickedListener接口并重写onClick方法
  2. 实现onClick方法中的具体逻辑,以此完成点击事件的相关业务操作

二、案例

2.1 创建项目

File -> New -> New Project

选择Empty Ability(Java),单击Next;

填写项目相关配置信息,点击Next;

项目创建完成后的效果如下

\

2.2 定义组件

这一步会定义一个按钮(按钮也是一个组件),并且给按钮组件分配唯一ID,之后通过ID定位按钮组件,在这里可能需要首先了解一下Ability相关技术,这样可以更好的了解Ability框架以及页面之间的包含关系,如果有完全不了解的可以查阅这篇文章,做个简单入门《HarmonyOS(鸿蒙)—— Ability与页面》。

找到MainAbilitySlice.java文件,然后按住ctrl键+点击ResourceTable.Layout_ability_main,进入ability_main.xml文件

也可以直接定位ability_main.xml文件

\

组件代码开发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ini复制代码<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">


<!-- ohos:id定义组件的id,注意格式固定$+id:xxxx -->
<!--match_content 表示包裹内容,按钮的大小与按钮内的文字大小一致-->
<Button
ohos:id="$+id:button"
ohos:width="match_content"
ohos:height="match_content"
ohos:text="点击我"
ohos:text_size="19fp"
ohos:text_color="#FFFFFF"
ohos:top_padding="8vp"
ohos:bottom_padding="8vp"
ohos:right_padding="70vp"
ohos:left_padding="70vp"
ohos:center_in_parent="true"
ohos:margin="10vp"
ohos:background_element="#007DFF"
/>

</DirectionalLayout>

2.3 定义的组件绑定单击事件

Component findComponentById(int resID)方法返回的是Component,Component是HarmonyOS中所有组件的父类。我们首先找到MainAbilitySlice.java文件,在onStart方法中进行事件的绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
java复制代码package com.liziba.demo.slice;

import com.liziba.demo.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;

public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);

//1. 找到组件
Button button = (Button) findComponentById(ResourceTable.Id_button);

//2. 绑定单击事件 -- 此时代码并未完成,需要传入Component.ClickedListener的实现类
button.setClickedListener();
}

@Override
public void onActive() {
super.onActive();
}

@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}

2.4 实现ClickedListener接口并重写onClick方法

这里可以直接使用MainAbilitySlice实现Component.ClickedListener接口,也可以通过定义内部类ButtonListener实现Component.ClickedListener接口,或者定义其他外部类实现Component.ClickedListener接口都可以,案例采用第二种情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
typescript复制代码package com.liziba.demo.slice;

import com.liziba.demo.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;

public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);

//1. 找到组件
Button button = (Button) findComponentById(ResourceTable.Id_button);

//2. 绑定单击事件
button.setClickedListener(new ButtonListener());
}

@Override
public void onActive() {
super.onActive();
}

@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}

/**
* 实现ClickedListener接口并重写onClick方法
*/
class ButtonListener implements Component.ClickedListener {

/**
* 点击事件触发的操作会调用的方法
* @param component 被点击的组件对象
*/
@Override
public void onClick(Component component) {
// 具体点击操作的逻辑处理
}
}

2.5 实现onClick方法中的具体逻辑,以此完成点击事件的相关业务操作

在onClick方法中,处理具体点击操作的逻辑,这里通过修改按钮中的文字来显示单击事件效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码/**
* 实现ClickedListener接口并重写onClick方法
*/
class ButtonListener implements Component.ClickedListener {

/**
* 点击事件触发的操作会调用的方法
* @param component 被点击的组件对象
*/
@Override
public void onClick(Component component) {
// 具体点击操作的逻辑处理
Button button = (Button) component;
button.setText("哦,我被点击了!");
}
}

三、测试

3.1 登录远程模拟器

点击Tools -> Device Manager

点击Login进行登录

登录华为账户,点击允许,如果没有注册账户的,请先注册一个华为账户

选择P40作为远程模拟器

启动成功后会看到如下调试手机界面

3.2 运行项目

点击右上角的三角形直接运行,或者点击甲壳虫进入调试模式

运行效果,点击前

点击后

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【Go实战 电商平台】(6) 用户信息修改 写在前面 1

发表于 2021-11-26

这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战

写在前面

由于用户的登录涉及到了身份这一方面的认证,所以我们就可以通过JWT进行用户的鉴权,以确认用户的身份。

  1. 路由注册

  • 编写用户更新的请求路由
1
go复制代码authed.PUT("user", api.UserUpdate)
  1. 函数编写

2.1 service层

  • 定义一个用户修改的结构体
    只能修改用户名或是昵称,修改密码后续再说。
1
2
3
4
5
go复制代码//用户修改信息的服务
type UserUpdateService struct {
NickName string `form:"nickname" json:"nickname" binding:"required,min=5,max=10"`
UserName string `form:"user_name" json:"user_name" binding:"required,min=5,max=15"`
}
  • 定义用户信息修改函数
1
go复制代码func (service UserUpdateService) Update(id uint) serializer.Response {...}

2.2 api层

  • 定义一个用户更新服务
1
go复制代码var userUpdateService service.UserUpdateService
  • 读取请求头中Authorization的值
1
go复制代码claims,_ := util.ParseToken(c.GetHeader("Authorization"))
  • 绑定这个服务
1
go复制代码c.ShouldBind(&userUpdateService)
  • 调用这个服务的update方法
1
go复制代码res := userUpdateService.Update(claims.ID)
  • 上下文返回
1
go复制代码c.JSON(200, res)
  • api层完整代码
1
2
3
4
5
6
7
8
9
10
11
go复制代码func UserUpdate(c *gin.Context) {
var userUpdateService service.UserUpdateService
claims,_ := util.ParseToken(c.GetHeader("Authorization"))
if err := c.ShouldBind(&userUpdateService); err == nil {
res := userUpdateService.Update(claims.ID)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
logging.Info(err)
}
}
  1. 服务编写

  • 根据Authorization解析出来的id找到该用户
1
2
3
4
5
6
7
8
9
10
11
12
13
go复制代码	var user model.User
code := e.SUCCESS
//找到用户
err := model.DB.First(&user, id).Error
if err != nil {
logging.Info(err)
code = e.ErrorDatabase
return serializer.Response{
Status: code,
Msg: e.GetMsg(code),
Error: err.Error(),
}
}
  • 根据传送来的信息对用户的原本信息进行修改
1
2
go复制代码	user.Nickname = service.NickName
user.UserName = service.UserName
  • 保存到数据库中
1
go复制代码err = model.DB.Save(&user).Error
  • 返回用户的信息
1
2
3
4
5
go复制代码	return serializer.Response{
Status: code,
Data: serializer.BuildUser(user),
Msg: e.GetMsg(code),
}
  • 服务层完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
go复制代码func (service UserUpdateService) Update(id uint) serializer.Response {
var user model.User
code := e.SUCCESS
//找到用户
err := model.DB.First(&user, id).Error
if err != nil {
logging.Info(err)
code = e.ErrorDatabase
return serializer.Response{
Status: code,
Msg: e.GetMsg(code),
Error: err.Error(),
}
}
user.Nickname = service.NickName
user.UserName = service.UserName
err = model.DB.Save(&user).Error
if err != nil {
logging.Info(err)
code = e.ErrorDatabase
return serializer.Response{
Status: code,
Msg: e.GetMsg(code),
Error: err.Error(),
}
}
return serializer.Response{
Status: code,
Data: serializer.BuildUser(user),
Msg: e.GetMsg(code),
}
}
  1. 验证

在这里插入图片描述

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

接上篇:Git Worktree 高级使用,这样清爽多了

发表于 2021-11-26

前言

上一篇文章 Git Worktree 大法真香 带大家了解了 git worktree 是如何帮助我同时在多个分支工作,并且互不影响的。但是创建 worktree 的目录位置不是在当前项目下,总感觉创建好的这些 worktree 不属于当前项目,这对于磁盘管理强迫症的我来说是十分难受的,今天就带大家了解一种高级用法来解决这个痛点

准备知识

在使用高级用法之前,你需要知道一点 bare repo 知识,我们先从你熟悉的命令开始

1
2
shell复制代码git init
git clone https://github.com/FraserYu/amend-crash-demo.git

这两个命令就会生成一个 non-bare repo,我们通常都在这样的 repo 中进行日常工作, 你可以在这里面 add/commit/pull/push

要想生成一个 bare repo 也很简单, 只需在上面两个命令的基础上加上 --bare 参数即可

1
2
shell复制代码git init --bare
git clone --bare https://github.com/FraserYu/amend-crash-demo.git

来执行这两个 clone 命令,并查看文件内容你就会看出差别了

  1. bare repo 仅仅包含 Git 相关信息,并不包含我们的实际项目文件(.java/.js/.py), 而 non-bare repo 却包含我们的全部信息
  2. bare repo 名称默认是带有 .git 后缀的,这也恰恰证明了第一点
  3. bare repo 中是不存在 .git 文件夹的,这也就导致它不能像 non-bare repo 那样 add/commit/pull/push

看到这,你可能感觉 bare repo 就是一个 Git 空壳文件夹,一无是处。其实不然,正因为 bare repo 的这些特性(不能对它进行更改),也就避免 repo 里面的内容被弄的一团糟,所以可以被用来做私有的中心化 repo,一张图解释,其实就是这样的:

如果你有兴趣,可以按照下面的命令在本地实验一下整个过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
shell复制代码user@server:$~  git init --bare name_to_repo.git

user@machine1:$~ git clone user@server:/path/to/repo/name_to_repo.git .
user@machine1:$~ cd name_to_repo
user@machine1:$~ touch README
user@machine1:$~ echo "Hello world" >> README
user@machine1:$~ git add README
user@machine1:$~ git commit -m "Adding a README"
user@machine1:$~ git push origin master

user@server:$~ ls /path/to/repo/name_to_repo.git/
branches/ config description HEAD hooks/ info/ objects/ refs/

user@machine2:$~ git clone user@server:/path/to/repo/name_to_repo.git .
user@machine2:$~ ls name_to_repo.git/
README
user@machine2:$~ cat README
Hello world

实验结果就是:无论在 machine1 下怎么 push 文件,bare repo 中都不会存在你 push 的文件,只有 Git 相关信息。但是 machine2 clone 最新的 repo 时,却能看到machine1 push 的文件,这正是 Git 的魔法所在

到这里,bare repo 就解释完了。接下来,接上一篇 Git Worktree 大法真香 内容,借助 bare repo 的特性,来优化同时在多个分支工作的做法吧

Git Worktree 高级用法

首先,在你选定的目录下为你的项目(比如这里叫 amend-crash-demo)单独创建一个文件夹, 并 cd 进去

1
2
shell复制代码mkdir amend-crash-demo
cd amend-crash-demo

接下来以 bare 的形式 clone 项目代码, 并将内容 clone 到 .bare 文件夹内:

1
shell复制代码git clone --bare git@github.com:FraserYu/amend-crash-demo.git .bare

我们还要在当前目录下创建一个 .git 文件,文件内容是以 gitdir 的形式指向我们的 .bare 文件夹 (如果不理解 gitdir 的含义,请回看 Git Worktree 大法真香 )

1
shell复制代码echo "gitdir: ./.bare" > .git

然后我们要编辑 .bare/config 文件,并修改 [remote "origin"]内容,和下面内容保持一致(也就是添加第 6 行内容),这确保我们创建 worktree 切换分支,可以显示正确的分支名称

1
2
3
4
5
6
shell复制代码vim .bare/config

# -----------------------------------------------
[remote "origin"]
url = git@github.com:FraserYu/amend-crash-demo.git
fetch = +refs/heads/*:refs/remotes/origin/*

接下来我们就可以创建 worktree 了,首先我们要为 main 分支创建 worktree,因为 main 分支 HEAD 的指向的 commit-ish 就是你创建其他 worktree 的 commit-ish

1
2
3
4
5
shell复制代码git worktree add main

# --------------------------------
Preparing worktree (checking out 'main')
HEAD is now at 82b8711 add main file

通常我们不会直接在 main 分支上直接工作,而是创建其它类型的分支,继续创建名为 feature/JIRA234-feature3 的 worktree

1
2
3
4
5
shell复制代码git worktree add -b "feature/JIRA234-feature3" feature3

# ------------------------------------------------
Preparing worktree (new branch 'feature/JIRA234-feature3')
HEAD is now at 82b8711 add main file

查看当前文件夹的内容,你会发现只有 main 和 feature3 两个文件夹(因为 .bare 和 .git 是隐藏文件夹/文件),这样是不是相当清爽呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
shell复制代码ls -l

# -------------------------------------------
total 0
drwxr-xr-x 10 rgyb staff 320 Nov 23 21:44 feature3
drwxr-xr-x 10 rgyb staff 320 Nov 23 21:36 main


ls -al
# -------------------------------------------
total 8
drwxr-xr-x 6 rgyb staff 192 Nov 23 21:44 .
drwxr-xr-x 3 rgyb staff 96 Nov 23 21:14 ..
drwxr-xr-x 12 rgyb staff 384 Nov 23 21:36 .bare
-rw-r--r-- 1 rgyb staff 16 Nov 23 21:29 .git
drwxr-xr-x 10 rgyb staff 320 Nov 23 21:44 feature3
drwxr-xr-x 10 rgyb staff 320 Nov 23 21:36 main

接下来就可以尽情的在我们的各种分支上,彼此互不影响的进行 add/commit/pull/push 操作了

1
2
3
4
5
6
7
8
9
10
shell复制代码echo "feature3 development" > feature3.yaml
git add feature3.yaml

git commit -m "feat: [JIRA234-feature3] feature3 development"
# ------------------------------------------------------------------
[feature/JIRA234-feature3 aeaac94] feat: [JIRA234-feature3] feature3 development
1 file changed, 1 insertion(+)
create mode 100644 feature3.yaml

git push --set-upstream origin feature/JIRA234-feature3

通过上一篇文章 worktree 的四个命令,多分支协同开发不再是问题:

1
2
3
4
5
6
7
8
9
shell复制代码git worktree add
git worktree list
# ------------------------------------------------------------------------------------------------
/Users/rgyb/Documents/projects/amend-crash-demo/.bare (bare)
/Users/rgyb/Documents/projects/amend-crash-demo/feature3 aeaac94 [feature/JIRA234-feature3]
/Users/rgyb/Documents/projects/amend-crash-demo/main 82b8711 [main]

git worktree remove
git worktree prune

总结

通过借助 bare repo 的特性,我们可以非常整洁的将所有 worktree 只管理在当前项目目录下,多分支协同开发,就像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
shell复制代码.
└── amend-crash-demo
├── feature3
│   ├── README.md
│   ├── config.yaml
│   ├── feat1.txt
│   ├── feature3.yaml
│   ├── file1.yaml
│   ├── hotfix.yaml
│   ├── main.properties
│   └── main.yaml
└── main
├── README.md
├── config.yaml
├── feat1.txt
├── file1.yaml
├── hotfix.yaml
├── main.properties
└── main.yaml

3 directories, 15 files

如果你有磁盘管理强迫症,这绝对是个好办法。

如果你想更好的理解整个过程,你需要在操作本文命令的同时,查看 Git 相关的文件信息

有什么问题,留言区交流

参考

  1. www.blopig.com/blog/2017/0…
  2. lists.mcs.anl.gov/pipermail/p…
  3. www.saintsjd.com/2011/01/wha…
  4. infrequently.org/2021/07/wor…
  5. morgan.cugerone.com/blog/how-to…
    个人博客:https://dayarch.top

日拱一兵 | 原创

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

dart系列之 时间你慢点走,我要在dart中抓住你 简介

发表于 2021-11-26

「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战」

简介

时间和日期是我们经常会在程序中使用到的对象。但是对时间和日期的处理因为有不同时区的原因,所以一直以来都不是很好用。就像在java中,为时间和日期修改和新增了多次API,那么作为新生的语言dart而言,会有什么不一样的地方吗?

dart中关于日期和时间的两个非常重要的类是DateTime和Duration.

其中DateTime表示的是时间,而Duration表示的是时间差。

DateTime

先看一下DateTime的使用。

DateTime表示的是一个时间点。因为世界时钟有UTC和本地时间两种。所以,在使用DataTime的时候,也可以使用这两种时钟。

最简单的就是获取当前的时间:

1
ini复制代码var now = DateTime.now();

如果要创建指定日期的时间,则可以将年月日传入DateTime的构造函数:

1
ini复制代码var now = DateTime(2021, 11, 20);

注意,上面创建的日期是本地日期。

如果要创建UTC time,则可以使用DateTime.utc方法:

1
ini复制代码var utc = DateTime.utc(2021, 11, 20);

还有一种表示时间的方法是unix time, Unix time指的是从1970年1月1日开始所经过的秒数.

DateTime有两种表示Unix time时间的方法,分别是:

1
2
ini复制代码  DateTime.fromMicrosecondsSinceEpoch(10000);
DateTime.fromMillisecondsSinceEpoch(10000);

他们的区别在于,一个表示的是微秒,一个表示的是毫秒。

DateTime还可以将字符串转换成为DateTime对象:

1
ini复制代码var time= DateTime.parse('2002-02-27T14:00:00-0500');

事实上,DateTime.parse可以接受多种字符类型,如下所示:

1
2
3
4
5
6
7
8
9
10
arduino复制代码 `"2012-02-27"`
`"2012-02-27 13:27:00"`
`"2012-02-27 13:27:00.123456789z"`
`"2012-02-27 13:27:00,123456789z"`
`"20120227 13:27:00"`
`"20120227T132700"`
`"20120227"`
`"+20120227"`
`"2012-02-27T14Z"`
`"2012-02-27T14+00:00"`

Duration

Duration表示的是两个时间之间的差值。

来看下Duration的构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
ini复制代码  const Duration(
{int days = 0,
int hours = 0,
int minutes = 0,
int seconds = 0,
int milliseconds = 0,
int microseconds = 0})
: this._microseconds(microsecondsPerDay * days +
microsecondsPerHour * hours +
microsecondsPerMinute * minutes +
microsecondsPerSecond * seconds +
microsecondsPerMillisecond * milliseconds +
microseconds);

可以看到Duration可以表示从天到microseconds的间隔,已经足够用了. 应该怎么使用呢?

1
2
3
4
5
ini复制代码var time = DateTime.now();

// 添加一年
var nextYear = time.add(const Duration(days: 365));
assert(nextYear.year == 2022);

同样的,我们可以对还可以减去Duration:

1
2
3
4
5
ini复制代码var time = DateTime.now();

//减少一年
var lastYear = time.subtract(const Duration(days: 365));
assert(lastYear.year == 2020);

当然还可以计算两个日期的差值:

1
2
ini复制代码var duration = nextYear.difference(time);
assert(duration.inDays == 365);

总结

以上就是dart中对时间和日期的支持。

本文已收录于 www.flydean.com/17-dart-dat…

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

OpenStack Victoria版——5控制节点-Pl

发表于 2021-11-26

这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战


5.控制节点-Placement服务组件

更多步骤:OpenStack Victoria版安装部署系列教程

OpenStack部署系列文章

OpenStack Victoria版 安装部署系列教程

OpenStack Ussuri版 离线安装部署系列教程(全)

OpenStack Train版 离线安装部署系列教程(全)

欢迎留言沟通,共同进步。


Placement服务的作用是跟踪资源(如计算节点,存储资源池,网络资源池等)的使用情况,提供自定义资源的能力,为分配资源提供服务。Placement在openstack的Stein版本之前是属于Nova组件的一部分。

在安装Nova之前需要先安装此组件。

@[toc]

创建placement相关数据库、凭据与API端点

1.创建placement数据库并授权

使用mysql数据库的root账户登录,密码为控制节点环境准备时初始化mysql数据设置的密码为root。

1
bash复制代码mysql -u root -proot

创建placement数据库并授权,设置密码为111111

1
2
3
4
5
6
7
8
mysql复制代码CREATE DATABASE placement;
GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'localhost' IDENTIFIED BY '111111';
GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'%' IDENTIFIED BY '111111';

flush privileges;
show databases;
select user,host from mysql.user;
exit

2.创建服务凭据

同样,创建并注册该项目的服务证书

域用户、项目、实体

先加载环境变量source /admin-openrc.sh

1
2
bash复制代码cd 
source admin-openrc.sh

创建一个plancement用户并设置密码为111111

1
bash复制代码openstack user create --domain default --password 111111 placement

使用admin角色将Placement用户添加到服务项目中

1
bash复制代码openstack role add --project service --user placement admin

创建Plancement服务

1
2
bash复制代码openstack service create --name placement --description "Placement API" placement
openstack service list

3.创建placement项目的endpoint(API端口)

1
2
3
4
bash复制代码openstack endpoint create --region RegionOne placement public http://controller:8778
openstack endpoint create --region RegionOne placement internal http://controller:8778
openstack endpoint create --region RegionOne placement admin http://controller:8778
openstack endpoint list

placement相关软件安装与配置

1.安装placement软件

1
bash复制代码dnf install openstack-placement-api -y

2.配置文件修改

(1)/etc/placement/placement.conf

修改前先备份
编辑vim /etc/placement/placement.conf文件,文件700行左右

1
bash复制代码cp /etc/placement/placement.conf /etc/placement/placement.conf.bak

命令行修改文件内容

1
2
3
4
5
6
7
8
9
10
11
12
bash复制代码crudini --set /etc/placement/placement.conf placement_database connection mysql+pymysql://placement:111111@controller/placement
crudini --set /etc/placement/placement.conf api auth_strategy keystone
crudini --set /etc/placement/placement.conf keystone_authtoken auth_url http://controller:5000/v3
crudini --set /etc/placement/placement.conf keystone_authtoken memcached_servers controller:11211
crudini --set /etc/placement/placement.conf keystone_authtoken auth_type password
crudini --set /etc/placement/placement.conf keystone_authtoken project_domain_name Default
crudini --set /etc/placement/placement.conf keystone_authtoken user_domain_name Default
crudini --set /etc/placement/placement.conf keystone_authtoken project_name service
crudini --set /etc/placement/placement.conf keystone_authtoken username placement
crudini --set /etc/placement/placement.conf keystone_authtoken password 111111
echo "Result of Configuration"
grep '^[a-z]' /etc/placement/placement.conf

(2)nova的虚拟主机配置文件

如果下面检查placement服务状态出现问题可以使用下面方法解决

编辑/etc/httpd/conf.d/00-placement-api.conf
V版本安装完成后会自动生成:/etc/httpd/conf.d/00-placement-api.conf文件

由于有个包的bug需要配置修改文件,需要修改nova虚拟主机配置文件,增加内容,完整的文件内容如下:

1
2
shell复制代码cp /etc/httpd/conf.d/00-placement-api.conf /etc/httpd/conf.d/00-placement-api.conf.bak
vim /etc/httpd/conf.d/00-placement-api.conf

#在#SSLCertificateKeyFile ...底下添加

1
2
3
4
5
6
7
8
9
10
shell复制代码#Placement API
<Directory /usr/bin>
<IfVersion >= 2.4>
Require all granted
</IfVersion>
<IfVersion < 2.4>
Order allow,deny
Allow from all
</IfVersion>
</Directory>

3.同步placement数据库

1
bash复制代码su -s /bin/sh -c "placement-manage db sync" placement
1
bash复制代码mysql -uplacement -p111111 -e "use placement;show tables;"

4.重启Apache服务(httpd)

如果重启失败,注意防火墙配置

1
2
bash复制代码systemctl restart httpd
systemctl status httpd

5.检查Placement服务状态

1
bash复制代码placement-status upgrade check

出现如下图所示,说明安装配置成功
在这里插入图片描述

placement服务安装完成

使用VMware虚拟机的话,现在可以关机做快照。

1
bash复制代码poweroff

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

springcloud config学习笔记

发表于 2021-11-26

这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

0 环境

  • 操作系统:win10
  • 编辑器:idea
  • springcloud版本:H版

1 简介

springcloud config –> 分布式系统解决方案 它包含server(配置文件)和client(获取server文件) 可以和eureka server配合使用

  • springcloud config功能:
    • 集中管理各个微服务/环境的配置文件(中央仓库 统一打包 发快递)
    • 支持多种开发语言和高并发查询
    • 提供server(服务端)和客户端(client)
    • 配置文件一经修改 可快速生效 但是client立刻生效需要中间件
    • 配置文件通过git/svn进行管理(支持版本回退)

2 git提交数据

在本地找一个位置 新建目录client1添加3个文件.properties结尾 上传到git上(最好每个文件开头名与目录全名一致) 一个文件夹代表一个微服务

1
2
3
4
5
shell复制代码git init
git add .
git commit -m "first commit"
git remote add origin xxx你的具体git地址
git push -u origin master

在这里插入图片描述

3 ConfigServer搭建

3.1 ConfigServer添加依赖

在这里插入图片描述

3.2 开启configserver

在启动类上添加注解

1
2
3
4
5
6
7
8
9
10
java复制代码@SpringBootApplication
// 启动ConfigServer
@EnableConfigServer
public class ConfigServerApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}

}

3.3 ConfigServer yml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
yaml复制代码spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/Abadbeginning/configRepo.git
search-paths: client1
ignoreLocalSshSettings: true #为了激活基于属性的SSH配置
# 找到本地id_rsa 将其文件cv一下
privateKey:
-----BEGIN OPENSSH PRIVATE KEY-----
xxxxx
-----END OPENSSH PRIVATE KEY-----
server:
port: 8088

3.4 启动configserver

在这里插入图片描述)在这里插入图片描述

3.5 访问

在这里插入图片描述

  • 修改git数据 server端生效了
    在这里插入图片描述
    在这里插入图片描述
  • 访问规则
1
2
3
4
5
shell复制代码/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

{application} 就是应用名称,对应到配置文件上来,就是配置文件的名称部分,例如我上面创建的配置文件。

{profile} 就是配置文件的版本,我们的项目有开发版本、测试环境版本、生产环境版本,对应到配置文件上来就是以 application-{profile}.yml 加以区分,例如application-dev.yml、application-sit.yml、application-prod.yml。

{label} 表示 git 分支,默认是 master 分支,如果项目是以分支做区分也是可以的,那就可以通过不同的 label 来控制访问不同的配置文件了。

4 ConfigClient搭建

4.1 创建springboot项目 添加configclient

在这里插入图片描述

4.2 bootstrap.yml配置

bootstrap在application配置之前 我们需要做点事情

1
2
3
4
5
6
7
8
9
10
11
yaml复制代码spring:
application:
name: client1
cloud:
config:
#http://localhost:8088/client1/prod/master
uri: http://localhost:8088
profile: dev
label: master
server:
port: 8090

4.3 configclient controller配置

1
2
3
4
5
6
7
8
9
10
11
12
java复制代码@RestController
public class HelloController {

@Value("${milk}")
String milk;

@GetMapping("/hello")
public String hello(){
return milk;
}

}

4.4 启动config-server和config-client

在这里插入图片描述

5 ConfigServer简单配置

  • 不使用git仓库的 2种本地使用方法(2种方法别同时使用)
    • 在config-server中classpath添加xxxx.yml/properties 和以前的使用一样
    • 指定本地磁盘的位置 存放xxx.yml/properties
1
2
3
4
5
6
7
8
9
10
11
12
13
yaml复制代码spring:
# 在config-server中的classpath下寻找配置(在项目server下resources里-->xxx.yml...) 不再去git上找
# profiles:
# active: native
application:
name: config-server
cloud:
config:
server:
# 在server中 指定本地磁盘的配置文件的位置
# 了解即可
native:
search-locations: file:/D:/properties/

6 常见加密方式简介

1
2
diff复制代码+ 不可逆加密
+ 可逆加密

6.1 不可逆加密

理论上无法根据已加密的密文推导出明文(常用算法 MD5消息摘要算法 SHA安全散列算法) –> 一般你喝了一瓶水 通过肠胃加密 不太可能再把水吐回瓶子里(再说了 肯定不是原来那个味了)

6.2 可逆加密

  • 通过加密后密文可推导出明文 可逆加密分为:
    • 对称加密
      • 加密和解密密钥是一样的(算法 aes 3des des)
    • 非对称加密
      • 加密的称为公钥 可以给任何人 解密的叫私钥(自己知) 常见算法RSA

7 对称加密

需要下载不定长的JCE
或在oracle中下载

7.1 解压

下载好JCE后 解压 并cv到下面这个目录 注意: 一开始我是在lib下创建security 后来才访问http://localhost:8088/encrypt/status时报错 搜索了才发现位置放的不对 需要在下面的目录下更改
在这里插入图片描述

7.2 在configserver中配置bootstrap.yml

1
2
3
yaml复制代码# 密钥
encrypt:
key: milk

7.3 启动configserver

首先查看加密状态 ok后 然后在用postman访问解密接口在对具体数据加密存放到git上 查看结果

在这里插入图片描述
在这里插入图片描述

7.4 在postman中加密

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这样被认为是字符串 需要加标识符{cipher}
例如milk={cipher}在这里将加密串cv过来

7.5 启动configserver和client并访问

config-server(文件变了 能及时感知到) client不行(后续需要用中间件等办法解决)
在这里插入图片描述

  • 需要重启client才会生效
    在这里插入图片描述

8 非对称加密

8.1 先生成密钥对

1
java复制代码keytool -genkeypair -alias config-server -keyalg RSA -keystore D:\git\config-server.keystore

在这里插入图片描述

8.2 在configserver中的bootstrap.yml里配置

1
2
3
4
5
6
7
8
9
yml复制代码# 密钥
#encrypt:
# key: milk
encrypt:
key-store:
location: config-server.keystore
password: 123456
secret: 123456
alias: config-server

在这里插入图片描述
在这里插入图片描述

8.3 防止xxx.keystore被过滤

需要在configserver pom文件build节点中配置(因为之后用一下jks 一并加上)

1
2
3
4
5
6
7
8
9
10
11
xml复制代码	  <resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.yml</include>
<include>**/*.jks</include>
<include>**/*.keystore</include>
</includes>
</resource>
</resources>

在这里插入图片描述

重启server
在这里插入图片描述

文件加密 并cv到git上 进行测试
在这里插入图片描述

8.4 重启/启动 config-server和config-client

在这里插入图片描述
在这里插入图片描述

9 安全管理

9.1 在config-server中添加springsecurity依赖

1
2
3
4
5
xml复制代码	<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>

9.2 配置yml

  • 在configserver中配置
1
2
3
4
5
yml复制代码spring:
security:
user:
name: admin
password: 123

在这里插入图片描述

  • 在configclient中这样配置会报错(很多时候会忘记了)
    在这里插入图片描述)在这里插入图片描述
  • 注意:config-client springsecurity yml配置(注意是cloud config)
1
2
3
4
5
yaml复制代码spring:
cloud:
config:
username: admin
password: 123

9.3 启动configserver和client

  • 访问
    在这里插入图片描述
    在这里插入图片描述

10 服务化

10.1 分别在config-server和config-client添加eureka依赖

1
2
3
4
java复制代码   <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

10.2 在config-server bootstrap.yml配置

配置eureka连接

1
2
3
4
5
yaml复制代码eureka:
client:
service-url:
# defaultZone: http://localhost:1234/eureka
defaultZone: http://localhost:8761/eureka

10.3 在config-client bootstrap.yml配置

  • 在config-server bootstrap.yml中开启配置eureka连接以及服务等(config-server地址别写死 更改全)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
yaml复制代码spring:
application:
name: client1
cloud:
config:
#http://localhost:8088/client1/prod/master
# uri: http://localhost:8088
profile: dev
label: master

username: admin
password: 123

# 开启通过eureka获取config-server的功能
discovery:
enabled: true

# 配置config-server服务名称
service-id: config-server

eureka:
client:
service-url:
# defaultZone: http://localhost:1234/eureka
defaultZone: http://localhost:8761/eureka
server:
port: 8090

10.4 启动报错

在这里插入图片描述

需要更换一种方式

1
java复制代码keytool -genkeypair -alias new-config-server -keyalg RSA -keypass 123456 -keystore D:\git\config-server.jks -storepass 123456
  • 在config server pom中添加
1
2
3
4
5
6
7
8
9
10
11
xml复制代码		<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.yml</include>
<include>**/*.jks</include>
<!-- <include>**/*.keystore</include>-->
</includes>
</resource>
</resources>
  • jks(自行探索 和之前的类似)
1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码keytool -genkeypair -alias config-server -keyalg RSA \ 
-dname "CN=zhangmutian, OU=company, O=organization, L=city, ST=province, C=china" \
-keypass 222222 \
-keystore config-server.jks \
-storepass 111111 \
-validity 365 \
genkeypair 参数即产生一对public key和private key。
alias 指定key的别名,用于区分同一keystore中不同的key。
keyalg 指定生成key的算法,这里使用默认的RSA
dname 指定common name,即CN,用以验证key的身份。其中各项皆为自定义参数,OU为单位名称,O为组织名称,L为城市,S为省份/州,C为国家
keypass 为key的密码
keystore 为keystore的文件名
storepass 访问keystore的密码
  • 为了得到效果 非对称加密那部分就不用了

config-client profile: test未使用加密方式 可以快速演示效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
yml复制代码spring:
application:
name: client1
cloud:
config:
#http://localhost:8088/client1/prod/master
# uri: http://localhost:8088
profile: test
label: master

username: admin
password: 123

# 开启通过eureka获取config-server的功能
discovery:
enabled: true

# 配置config-server服务名称
service-id: config-server


eureka:
client:
service-url:
# defaultZone: http://localhost:1234/eureka
defaultZone: http://localhost:8761/eureka

server:
port: 8090

10.5 重启

在这里插入图片描述

10.6 实现configclient感知数据更新

  • 配置文件更改
    1 config-client添加监控
1
2
3
4
xml复制代码    <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2 修改git仓库

profile test –> 值改为test123
在这里插入图片描述
3 config-client bootstrap.yml配置
监控刷新

1
2
3
4
5
yaml复制代码management:
endpoints:
web:
exposure:
include: refresh

4 post访问监控

127.0.0.1:8090/actuator/refresh 再次访问config-client 消息确实刷新了 但是还是很繁琐 需要单独一个请求
在这里插入图片描述
在这里插入图片描述

11 请求重试

请求时 可能会失败 我们可以配置一个请求重试的功能 在请求config-server失败重试 而不是在请求hello时报错

1 添加依赖

1
2
3
4
5
6
7
8
xml复制代码		<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>

2 config-client bootstrap.yml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
yaml复制代码spring:
application:
name: client1
cloud:
config:
#http://localhost:8088/client1/prod/master
# uri: http://localhost:8088
profile: test
label: master

# 关闭 为了请求不到config-server
# username: admin
# password: 123

# 开启通过eureka获取config-server的功能
discovery:
enabled: true

# 配置config-server服务名称
service-id: config-server
# 开启失败快速响应
fail-fast: true
retry:
# 请求重试的初始间隔时间
initial-interval: 1000
# 最大重试次数
max-attempts: 6
# 重试时间间隔阶乘
multiplier: 1.1
# 最大间隔
max-interval: 2000

eureka:
client:
service-url:
# defaultZone: http://localhost:1234/eureka
defaultZone: http://localhost:8761/eureka

server:
port: 8090

management:
endpoints:
web:
exposure:
include: refresh

在这里插入图片描述

12 小结

首先创建git项目 创建springboot项目添加configserver的依赖 添加启动注解 配置仓库基本信息 测试是否连接成功(server okle)
创建configclient项目添加configclient依赖 配置连接server具体的哪个文件的(通过一些的标识标记 写死的) 配置一个访问接口 访问server是否能显示数据
需要通过占位符对client改造 灵活访问server
2种加密方式的配置 很类似 需要配置相对应encrypt….属性 都要生成密钥对 密钥对文件不被读取 需要在build节点中配置 server没有被保护 谁都能访问 很危险不是
需要配合springsecurity 并且添加依赖配置springsecurity 注意了server --> springsecurity xxx | client --> cloud xxx
为了server和client更加方便 引入进来eureka config-client不会自动刷新 简单实现一下 之后的章节会用消息中间件完成 这里先用监控刷新 针对单个 一但接口多了肯定不合适的
在configclient调用configserver调用失败 不能失败了 还访问某个接口 需要配置重试功能

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…181182183…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%