<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.7.3">Jekyll</generator><link href="https://abekthink.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://abekthink.github.io/" rel="alternate" type="text/html" /><updated>2018-07-12T18:28:53+08:00</updated><id>https://abekthink.github.io/</id><title type="html">abekthink’s blog</title><subtitle>An amazing website.</subtitle><author><name>Lei Geng</name><email>abekdev@163.com</email><uri>https://abekthink.github.io</uri></author><entry><title type="html">RSA Encryption with Python and Java and Swift</title><link href="https://abekthink.github.io/backend/rsa-encryption-with-python-java-swift/" rel="alternate" type="text/html" title="RSA Encryption with Python and Java and Swift" /><published>2018-07-01T19:28:50+08:00</published><updated>2018-07-01T19:28:50+08:00</updated><id>https://abekthink.github.io/backend/rsa-encryption-with-python-java-swift</id><content type="html" xml:base="https://abekthink.github.io/backend/rsa-encryption-with-python-java-swift/">&lt;p&gt;我们经常会遇到这样的需求：需要各端（Server/Android/iOS/Web等）通信时进行加密，防止请求被拦截或者篡改。&lt;/p&gt;

&lt;p&gt;本文以实际项目中的真实加密场景说明下，如何对请求进行加密签名，防止数据被篡改。&lt;/p&gt;

&lt;h2 id=&quot;需求&quot;&gt;需求&lt;/h2&gt;
&lt;p&gt;客户端（Android/iOS/Web等）向服务端发送请求，需要对请求中的参数进行加密签名。&lt;/p&gt;

&lt;h2 id=&quot;设计&quot;&gt;设计&lt;/h2&gt;
&lt;p&gt;这里采用了通用的RSA加密签名验证算法，整个流程主要包括如下两部分：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;客户端：参数处理，生成签名（sign），向服务端发送带sign的请求。&lt;/li&gt;
  &lt;li&gt;服务端：参数处理，用请求中的sign进行签名验证，如果不通过则返回参数验证失败；否则，进行后续处理，并将结果返回。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这里为了方便说明，这里举一个简单例子来说明客户端和服务端的处理流程：&lt;/p&gt;

&lt;h3 id=&quot;客户端&quot;&gt;客户端&lt;/h3&gt;
&lt;p&gt;假设，客户端请求中包含name、age和career三个参数，为了防止数据被篡改，我们将三个参数进行标准化的字符串排序。
比如请求中的参数为：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    &quot;namge&quot;: &quot;张三&quot;,
    &quot;age&quot;: 25,
    &quot;career&quot;: &quot;后端研发工程师&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;则将该结构体按照key排序后，生成相应字符串format_str，比如：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;age=25&amp;amp;career=后端研发工程师&amp;amp;namge=张三
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，在将以上字符串format_str进行RSA签名，生成相应的sign，比如：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mtttq1BIW2ENAc16sZspwGOdfh+Qu7idr8...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最终客户端，向后端请求的参数是：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    &quot;namge&quot;: &quot;张三&quot;,
    &quot;age&quot;: 25,
    &quot;career&quot;: &quot;后端研发工程师&quot;,
    &quot;sign&quot;: &quot;mtttq1BIW2ENAc16sZspwGOdfh+Qu7idr8...&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;服务端&quot;&gt;服务端&lt;/h3&gt;
&lt;p&gt;服务端在接到请求后，如客户端一样生成相应的format_str（需要先将sign提取出来）：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;age=25&amp;amp;career=后端研发工程师&amp;amp;namge=张三
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后，通过RSA验证算法，将format_str和请求中的sign参数进行验证，如果失败则是参数或者签名有误，服务端以此来判断参数是否有被篡改。&lt;/p&gt;

&lt;h2 id=&quot;各语言加密方式&quot;&gt;各语言加密方式&lt;/h2&gt;
&lt;p&gt;下面分别从Python、Java、Swift等主流语言，来分别说明各方是怎么加密和解密的。&lt;/p&gt;

&lt;h3 id=&quot;说明&quot;&gt;说明&lt;/h3&gt;
&lt;p&gt;下面各语言代码示例都会用到公钥和私钥，这里简单声明下：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public_key = '''-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3...
-----END PUBLIC KEY-----
'''

private_key = '''-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDu...
-----END RSA PRIVATE KEY-----
'''
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;备注：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;基于刚刚假设的场景，客户端需要保存私钥，以便进行签名；服务端需要保存公钥，以便进行验证。&lt;/li&gt;
  &lt;li&gt;这里使用了PKCS1 PSS作为signature算法，SHA256为hash算法。&lt;/li&gt;
  &lt;li&gt;另外，这里采用了加盐方式，咱设salt_len为11。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;python&quot;&gt;Python&lt;/h3&gt;
&lt;p&gt;首先需要安装加密包依赖，使用&lt;code class=&quot;highlighter-rouge&quot;&gt;pip install pycrypto&lt;/code&gt;命令即可。&lt;/p&gt;

&lt;p&gt;导入依赖：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;from Crypto.Signature import PKCS1_PSS
from Crypto.Hash import SHA256
from base64 import b64decode, b64encode
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;加密函数，用于生成sign：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def sign_with_pkcs1_pss(format_str):
    signer = PKCS1_PSS.new(private_key, saltLen=11)
    h = SHA256.new()
    h.update(format_str.encode())
    sign = b64encode(signer.sign(h)).decode(&quot;utf-8&quot;, &quot;strict&quot;)
    reutrn sign
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;验证函数，用于验证参数是否正确：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def verify_with_pkcs1_pss(format_str, sign)
    verifier = PKCS1_PSS.new(public_key, saltLen=11)

    h = SHA256.new()
    h.update(format_str.encode())
    signature = b64decode(sign)
    if verifier.verify(h, signature):
        return True
    else:
        return False
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;备注：这里以python3环境举例。&lt;/p&gt;

&lt;h3 id=&quot;javaandroid&quot;&gt;Java(Android)&lt;/h3&gt;
&lt;p&gt;Java代码，用下面这段代码即可：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;github&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abekthink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rsa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;android&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Base64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputStreamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ByteArrayInputStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AlgorithmParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeyFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrivateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PublicKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MGF1ParameterSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PKCS8EncodedKeySpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PSSParameterSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X509EncodedKeySpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bouncycastle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PemObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bouncycastle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PemReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bouncycastle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PemWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bouncycastle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BouncyCastleProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;codec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;binary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Base64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RSAAlgorithm&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BouncyCastleProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PrivateKey&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getPrivateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pemFormatKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;KeyFactory&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KeyFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RSA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;BC&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;PemReader&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pemReader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PemReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputStreamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ByteArrayInputStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pemFormatKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getBytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pemObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pemReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readPemObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pemObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PKCS8EncodedKeySpec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privKeySpec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PKCS8EncodedKeySpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatePrivate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;privKeySpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signPSS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plainText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PrivateKey&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Signature&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateSignature&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SHA256withRSA/PSS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AlgorithmParameters&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pss&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PSSParameterSpec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PSSParameterSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SHA-256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;MGF1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MGF1ParameterSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SHA-256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;privateSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;privateSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initSign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;privateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;privateSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plainText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getBytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF_8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Base64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Base64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DEFAULT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Base64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encodeBase64String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generateSign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formatStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prikey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-----BEGIN RSA PRIVATE KEY-----&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-----END RSA PRIVATE KEY-----&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PrivateKey&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getPrivateKeyFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prikey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signPSS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;formatStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PublicKey&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getPublicKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pemFormatKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;KeyFactory&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KeyFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RSA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;BC&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;PemReader&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pemReader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PemReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputStreamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ByteArrayInputStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;public_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getBytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pemObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pemReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readPemObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pemObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;X509EncodedKeySpec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pubKeySpec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X509EncodedKeySpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatePublic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pubKeySpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verifyPSS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plainText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pomPubKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PublicKey&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publicKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getPublicKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pomPubKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Signature&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publicSignature&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SHA256withRSA/PSS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AlgorithmParameters&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pss&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publicSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PSSParameterSpec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PSSParameterSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SHA-256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;MGF1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MGF1ParameterSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SHA-256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;publicSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;publicSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initVerify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publicKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;publicSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plainText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getBytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF_8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signatureBytes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Base64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decodeBase64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publicSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;signatureBytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verifySign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formatStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pubkey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-----BEGIN PUBLIC KEY-----&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;...&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-----END PUBLIC KEY-----&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RSAAlgorithm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verifyPSS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;formatStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pubkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Android端：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;需要在gradle配置dependencies项中，增加如下依赖：
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;implementation('org.bouncycastle:bcprov-jdk15on:1.54')
implementation('commons-codec:commons-codec:1.2')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;同时，将上述代码中做出如下调整：
 将
     &lt;code class=&quot;highlighter-rouge&quot;&gt;KeyFactory factory = KeyFactory.getInstance(&quot;RSA&quot;, &quot;BC&quot;);&lt;/code&gt;
 修改为：
     &lt;code class=&quot;highlighter-rouge&quot;&gt;KeyFactory factory = KeyFactory.getInstance(&quot;RSA&quot;, new BouncyCastleProvider());&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;swift&quot;&gt;Swift&lt;/h3&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public struct RSA {

    public static func publicKey() -&amp;gt; Data {
        let publicKeyPEM = &quot;&quot;&quot;
        -----BEGIN PUBLIC KEY-----
        ...
        -----END PUBLIC KEY-----
        &quot;&quot;&quot;

        let publicKeyDER = try! SwKeyConvert.PublicKey.pemToPKCS1DER(publicKeyPEM)
        return publicKeyDER
    }

    public static func privateKey() -&amp;gt; Data {
        let privateKeyPEM = &quot;&quot;&quot;
        -----BEGIN RSA PRIVATE KEY-----
        ...
        -----END RSA PRIVATE KEY-----
        &quot;&quot;&quot;

        let privateKeyDER = try! SwKeyConvert.PrivateKey.pemToPKCS1DER(privateKeyPEM)
        return privateKeyDER
    }

    public static func sign(_ message: String) -&amp;gt; String? {
        guard let data = message.data(using: .utf8) else { return nil }
        let sign = try? CC.RSA.sign(data, derKey: privateKey(), padding: .pss,
                                    digest: .sha256, saltLen: 11)
        return sign?.base64EncodedString()
    }

//    public static func sign(_ parameters: [String: Any]) -&amp;gt; String? {
//        let message = parameters
//            .sorted(by: { $0.key &amp;lt; $1.key })
//            .map({ $0 + &quot;=&quot; + &quot;\($1)&quot; })
//            .joined(separator: &quot;&amp;amp;&quot;)
//        guard let data = message.data(using: .utf8) else { return nil }
//        let sign = try? CC.RSA.sign(data, derKey: privateKey(), padding: .pss,
//                                    digest: .sha256, saltLen: 11)
//        return sign?.base64EncodedString()
//    }

    public static func verify(_ message: String, sign: Data) -&amp;gt; String? {
        guard let data = message.data(using: .utf8) else { return nil }
        let verified = try? CC.RSA.verify(testMessage, derKey: pubKey, padding: padding,
                                          digest: .sha256, saltLen: 11, signedData: sign!)

        return verified
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;参考文档&quot;&gt;参考文档&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dlitz/pycrypto&quot;&gt;Python RSA 代码库&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.java2s.com/Tutorial/Java/0490__Security/RSASignatureGeneration.htm&quot;&gt;Java RSA 参考代码&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/soyersoyer/SwCrypt&quot;&gt;Swift RSA 代码库&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Lei Geng</name><email>abekdev@163.com</email><uri>https://abekthink.github.io</uri></author><category term="rsa" /><category term="python" /><category term="java" /><category term="android" /><category term="swift" /><summary type="html">我们经常会遇到这样的需求：需要各端（Server/Android/iOS/Web等）通信时进行加密，防止请求被拦截或者篡改。</summary></entry><entry><title type="html">Robot Framework教程 —— 整合Jenkins</title><link href="https://abekthink.github.io/test/robot-framework-tutorial-integration-jenkins/" rel="alternate" type="text/html" title="Robot Framework教程 —— 整合Jenkins" /><published>2018-01-19T17:25:10+08:00</published><updated>2018-01-19T17:25:10+08:00</updated><id>https://abekthink.github.io/test/robot-framework-tutorial-integration-jenkins</id><content type="html" xml:base="https://abekthink.github.io/test/robot-framework-tutorial-integration-jenkins/">&lt;p&gt;本文主要讲下如何使用Robot Framework搭建自己的自动化测试框架，包括如下几部分：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/test/robot-framework-tutorial-installation&quot; title=&quot;安装&quot;&gt;安装&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/test/robot-framework-tutorial-keywords&quot; title=&quot;关键字&quot;&gt;关键字&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/test/robot-framework-tutorial-integration-jenkins&quot; title=&quot;整合Jenkins&quot;&gt;整合Jenkins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;安装&quot;&gt;安装&lt;/h2&gt;
&lt;p&gt;网上安装Jenkins的文章很多，这里不详细介绍了，具体可以查看如下文档：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://jenkins.io/doc/pipeline/tour/getting-started/&quot;&gt;Jenkins官方文档&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jianshu.com/p/ab3302cd68eb&quot;&gt;Mac OS X 安装Jenkins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;创建任务&quot;&gt;创建任务&lt;/h2&gt;
&lt;p&gt;在Jenkins首页左边导航栏点击新建任务，创建一个freestyle类型的任务。如下图：&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/jenkins-new-job.jpg&quot; alt=&quot;Jenkins New Job&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;上一篇文章我们已经写了一批&lt;a href=&quot;https://github.com/abekthink/robot-framework-demo&quot;&gt;测试示例&lt;/a&gt;，所以这里我们只要引用就可以了。&lt;/p&gt;

&lt;p&gt;为了能够及时的获取最新代码进行测试，我们需要对Jenkins里GitHub相关的配置进行下设置：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;在&lt;code class=&quot;highlighter-rouge&quot;&gt;General&lt;/code&gt;部分，填写下&lt;code class=&quot;highlighter-rouge&quot;&gt;GitHub project&lt;/code&gt;字段：&lt;code class=&quot;highlighter-rouge&quot;&gt;git@github.com:abekthink/robot-framework-demo.git&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;在&lt;code class=&quot;highlighter-rouge&quot;&gt;源码管理&lt;/code&gt;部分，选择&lt;code class=&quot;highlighter-rouge&quot;&gt;Git&lt;/code&gt;选项，然后填写&lt;code class=&quot;highlighter-rouge&quot;&gt;Repository URL&lt;/code&gt;字段：&lt;code class=&quot;highlighter-rouge&quot;&gt;git@github.com:abekthink/robot-framework-demo.git&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/jenkins-job-general.jpg&quot; alt=&quot;Jenkins Job General&quot; /&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/jenkins-job-scm.jpg&quot; alt=&quot;Jenkins Job SCM&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;然后我们在&lt;code class=&quot;highlighter-rouge&quot;&gt;构建&lt;/code&gt;部分，选择&lt;code class=&quot;highlighter-rouge&quot;&gt;Execute shell&lt;/code&gt;配置一个shell脚本：&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mkdir &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; reports/robot-framework-demo
/usr/local/bin/robot &lt;span class=&quot;nt&quot;&gt;--outputdir&lt;/span&gt; reports/robot-framework-demo test3.robot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/jenkins-job-build.jpg&quot; alt=&quot;Jenkins Job Build&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;这样就完成了整个任务的配置，不过这时候产出的文件我们并不能直接看到测试报告。
所以，下一节我们会讲下使用&lt;code class=&quot;highlighter-rouge&quot;&gt;Robot Framework plugin&lt;/code&gt;来更加直观的查看测试结果。&lt;/p&gt;

&lt;h2 id=&quot;配置测试插件&quot;&gt;配置测试插件&lt;/h2&gt;
&lt;p&gt;按照&lt;code class=&quot;highlighter-rouge&quot;&gt;首页&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;系统管理&lt;/code&gt; -&amp;gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;管理插件&lt;/code&gt;打开插件管理的页面。在页面选择&lt;code class=&quot;highlighter-rouge&quot;&gt;可选插件&lt;/code&gt;，然后搜索&lt;code class=&quot;highlighter-rouge&quot;&gt;robot&lt;/code&gt;，结果中有个&lt;code class=&quot;highlighter-rouge&quot;&gt;Robot Framework plugin&lt;/code&gt;，这个就是我们想要的，我们安装即可。&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/jenkins-plugin-management.jpg&quot; alt=&quot;Jenkins Plugin Management&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;插件安装完成后，我们就可以直接应用在我们刚才的任务里了。打开刚才任务的配置页面，在&lt;code class=&quot;highlighter-rouge&quot;&gt;构建后操作&lt;/code&gt;部分：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;选择&lt;code class=&quot;highlighter-rouge&quot;&gt;Publish Robot Framework test results&lt;/code&gt;进行编辑。&lt;/li&gt;
  &lt;li&gt;在&lt;code class=&quot;highlighter-rouge&quot;&gt;Directory of Robot output&lt;/code&gt;字段里填写刚才报表的产出目录&lt;code class=&quot;highlighter-rouge&quot;&gt;reports/robot-framework-demo&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;在&lt;code class=&quot;highlighter-rouge&quot;&gt;Thresholds for build result	&lt;/code&gt;部分可以配置项目在什么情况下显示蓝灯和黄灯，这里配置了当测试用例20%失败时则显示黄灯。&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/jenkins-job-config-robot.jpg&quot; alt=&quot;Jenkins Job Config Robot&quot; /&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/jenkins-job-config-robot-1.jpg&quot; alt=&quot;Jenkins Job Config Robot&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;配置完成后，可以去任务里&lt;code class=&quot;highlighter-rouge&quot;&gt;立即构建&lt;/code&gt;，查看刚刚配置的结果。&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/jenkins-job-result.jpg&quot; alt=&quot;Jenkins Job Result&quot; /&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;无法查看html&quot;&gt;无法查看HTML&lt;/h2&gt;
&lt;p&gt;为了查看具体的测试结果，我们去打开&lt;code class=&quot;highlighter-rouge&quot;&gt;Robot Framework plugin&lt;/code&gt;产生的&lt;code class=&quot;highlighter-rouge&quot;&gt;report.html&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;log.html&lt;/code&gt;，最后发现无法打开，报出如下错误：&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/jenkins-html-error.jpg&quot; alt=&quot;Jenkins HTML Error&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;在Google上查询了相关问题，分析得来主要是因为在Jenkins 1.641引入了CSP头保护本地文件。
所以要解决这个问题有两个版本：一个是直接修改Java启动时CSP有关的参数；另一个是增加groovy文件，在Jenkins启动时生效。
这里我们采用了第一种，比较简单直接，直接修改tomcat中的&lt;code class=&quot;highlighter-rouge&quot;&gt;catalina.sh&lt;/code&gt;，在文件最上面增加如下几行：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;JENKINS_CSP_OPTS=&quot;sandbox allow-scripts; default-src 'none'; img-src 'self' data: ; style-src 'self' 'unsafe-inline' data: ; script-src 'self' 'unsafe-inline' 'unsafe-eval' ;&quot;
JENKINS_OPTS=&quot;-Dhudson.model.DirectoryBrowserSupport.CSP=\&quot;$JENKINS_CSP_OPTS\&quot;&quot;
CATALINA_OPTS=&quot;$JENKINS_OPTS $CATALINA_OPTS&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;将Tomcat关闭然后重新启动，就能正常查看相关报表了。&lt;/p&gt;

&lt;h2 id=&quot;配置自动化&quot;&gt;配置自动化&lt;/h2&gt;
&lt;p&gt;我们希望当代码提交时就能进行立即触发Jenkins任务的构建和测试，这样整个自动化才能完全实现，所以，这里提一下如何进行配置。&lt;/p&gt;

&lt;p&gt;这里依然拿&lt;code class=&quot;highlighter-rouge&quot;&gt;robot-framework-demo&lt;/code&gt;来举例：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;首先在Jenkins上该任务的设置页面的&lt;code class=&quot;highlighter-rouge&quot;&gt;构建触发器&lt;/code&gt;板块，将&lt;code class=&quot;highlighter-rouge&quot;&gt;GitHub hook trigger for GITScm polling&lt;/code&gt;选项打勾。&lt;/li&gt;
  &lt;li&gt;然后，去GitHub上&lt;code class=&quot;highlighter-rouge&quot;&gt;robot-framework-demo&lt;/code&gt;项目设置里配置&lt;code class=&quot;highlighter-rouge&quot;&gt;Webhooks&lt;/code&gt;，创建一个新的webhook。
    &lt;ul&gt;
      &lt;li&gt;URL的格式是&lt;code class=&quot;highlighter-rouge&quot;&gt;$JENKINS_BASE_URL/github-webhook/&lt;/code&gt;，这里的JENKINS_BASE_URL就是你Jenkins服务的链接地址。&lt;/li&gt;
      &lt;li&gt;Trigger类型的话可以只选择Push事件。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/jenkins-job-github-trigger.jpg&quot; alt=&quot;Jenkins Job GitHub Trigger&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;配置完成后，当项目的master有代码push时，即可触发webhook；然后触发Jenkins上的任务进行构建，这样就是实时查看最新的测试报告了。&lt;/p&gt;

&lt;h2 id=&quot;参考文档&quot;&gt;参考文档&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://jenkins.io/doc/&quot;&gt;Jenkins官方文档&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.jenkins.io/display/JENKINS/GitHub+Plugin&quot;&gt;GitHub Plugin for Jenkins&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.jenkins.io/display/JENKINS/Configuring+Content+Security+Policy&quot;&gt;Configuring Content Security Policy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://issues.jenkins-ci.org/browse/JENKINS-32118&quot;&gt;Robot Framework log/report file can not be opened&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cyotek.com/blog/adjusting-the-jenkins-content-security-policy&quot;&gt;Adjusting the Jenkins Content Security Policy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Lei Geng</name><email>abekdev@163.com</email><uri>https://abekthink.github.io</uri></author><category term="robot" /><category term="robotframework" /><category term="autotest" /><category term="test" /><category term="agile" /><category term="jenkins" /><category term="自动化测试" /><summary type="html">本文主要讲下如何使用Robot Framework搭建自己的自动化测试框架，包括如下几部分： 安装 关键字 整合Jenkins</summary></entry><entry><title type="html">Robot Framework教程 —— 关键字</title><link href="https://abekthink.github.io/test/robot-framework-tutorial-keywords/" rel="alternate" type="text/html" title="Robot Framework教程 —— 关键字" /><published>2018-01-18T23:58:56+08:00</published><updated>2018-01-18T23:58:56+08:00</updated><id>https://abekthink.github.io/test/robot-framework-tutorial-keywords</id><content type="html" xml:base="https://abekthink.github.io/test/robot-framework-tutorial-keywords/">&lt;p&gt;本文主要讲下如何使用Robot Framework搭建自己的自动化测试框架，包括如下几部分：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/test/robot-framework-tutorial-installation&quot; title=&quot;安装&quot;&gt;安装&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/test/robot-framework-tutorial-keywords&quot; title=&quot;关键字&quot;&gt;关键字&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/test/robot-framework-tutorial-integration-jenkins&quot; title=&quot;整合Jenkins&quot;&gt;整合Jenkins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;声明&quot;&gt;声明&lt;/h2&gt;
&lt;p&gt;框架支持多种方式撰写测试数据和用例，包括HTML、TSV、纯文本、reStructuredText等。本文主要讲解纯文本方式，如果想看其他几种方式，请查看&lt;a href=&quot;http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#test-data-syntax&quot;&gt;测试数据格式&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;一个简单的示例&quot;&gt;一个简单的示例&lt;/h2&gt;
&lt;p&gt;让我们直接看一个简单的示例，从而初步理解框架是如何进行测试的。&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*** Settings ***

*** Test Cases ***
Test Robot Framework Logging
    Log    &quot;Test Logging&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Settings&lt;/code&gt;部分是空的，因为我们并没有引用标准测试库或者外部测试类。现在我们可以忽略它。
&lt;code class=&quot;highlighter-rouge&quot;&gt;Test Cases&lt;/code&gt;部分，我们定义了一个测试用例。它执行了一个关键字&lt;code class=&quot;highlighter-rouge&quot;&gt;Log&lt;/code&gt;，我们传递一个&lt;code class=&quot;highlighter-rouge&quot;&gt;Test Loging&lt;/code&gt;的字符串参数给它。这里有几点需要注意下，关键字与参数之间要至少有2个空格以上。另外，在每一个测试用例下的每一行，都要有至少2个空格以上的缩进。在这个例子中，很明显的看到我们都保留了4个空格，但往往这点容易被人忽略。&lt;/p&gt;

&lt;p&gt;以上测试用例，我们可以使用robot或者java命令来去运行测试。如下：&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pybot &lt;span class=&quot;nt&quot;&gt;--outputdir&lt;/span&gt; ./reports test0.robot
java &lt;span class=&quot;nt&quot;&gt;-jar&lt;/span&gt; /usr/local/opt/robotframework/robotframework-3.0.2.jar &lt;span class=&quot;nt&quot;&gt;--outputdir&lt;/span&gt; ./report test0.robot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;命令行能够支持多种选项，在这上面这个例子中，我们只使用了&lt;code class=&quot;highlighter-rouge&quot;&gt;--outputdir&lt;/code&gt;选项来指定测试日志和报告存储的目录。&lt;/p&gt;

&lt;h2 id=&quot;关键字--编程语言&quot;&gt;关键字 —— 编程语言&lt;/h2&gt;
&lt;p&gt;使用关键字就像学习了一门新的编程语音。关键字语法包括内置、标注和外部测试库。内置库可以直接使用，无需显式声明import。标准库也是框架的一部分，但是需要在Settings块内显式地声明import。外部库必须单独安装，然后按照标准库的方式导入。
下面举一个例子：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*** Settings ***
Library     String


*** Test Cases ***
Test Robot Framework Logging
    Log    Test Logging
    Log Many    First Entry   Second Entry   Third Entry
    Log To Console    Display to console while Robot is running

Test For Loop
    : FOR    ${INDEX}    IN RANGE    1    3
    \    Log    ${INDEX}
    \    ${RANDOM_STRING}=    Generate Random String    ${INDEX}
    \    Log    ${RANDOM_STRING}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;首先，我们扩展了第一个测试用例，增加了几行调用不同的关键字。尽管这三行都是打日志，但在测试用例定义下执行多个关键字是我们最常用的测试方式。
第二个测试用例实现了一个循环，以及如何执行一个关键字将结果赋值到一个变量中。
因为关键字&lt;code class=&quot;highlighter-rouge&quot;&gt;Generate Random String&lt;/code&gt;来自于&lt;code class=&quot;highlighter-rouge&quot;&gt;String Library&lt;/code&gt;，所以我需要在&lt;code class=&quot;highlighter-rouge&quot;&gt;Settings&lt;/code&gt;块下import它。&lt;/p&gt;

&lt;h2 id=&quot;写自己的关键字&quot;&gt;写自己的关键字&lt;/h2&gt;
&lt;p&gt;到现在为止，已经学习了怎么是怎么使用保留的关键字。下面我们写自己的第一个关键字：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*** Settings ***

*** Test Cases ***
Test Robot Framework Logging
    Log    Test Logging

Test My Robot Framework Logging
    My Logging    My Message    WARN

*** Keywords ***
My Logging
    [Arguments]    ${msg}    ${level}
    Log    ${msg}    ${level}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;自己的关键字需要写在&lt;code class=&quot;highlighter-rouge&quot;&gt;Keywords&lt;/code&gt;块下。语法和写测试用例一样，最大的不同是可以再自己的关键字里传递参数。&lt;/p&gt;

&lt;h2 id=&quot;管理关键字--资源文件&quot;&gt;管理关键字 —— 资源文件&lt;/h2&gt;
&lt;p&gt;当然，随着关键字定义越来越多，测试用例文件会越来越复杂，不容易管理。为了解决这个，可以考虑在所谓的资源文件里定义新的关键字，然后这些资源文件可以与测试库一样被导入进测试用例文件。&lt;/p&gt;

&lt;p&gt;资源文件如下：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*** Keywords ***
My Logging
    [Arguments]    @{arg}
    Log Many    @{arg}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;测试用例文件如下：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*** Settings ***
Resource        resource-0.txt

*** Variables ***
${MESSAGE}    &quot;Test My Logging 3&quot;

*** Test Cases ***
Test Robot Framework Logging
    Log    &quot;Test Logging&quot;

Test My Logging
    My Logging    &quot;Test My Logging 1&quot;    &quot;Test My Logging 2&quot;    ${MESSAGE}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在上面这个例子中，测试用例文件和资源文件必须在同一个目录下；当然，也可以把资源文件放在子目录下，在import时也需要使用那个相对路径。
另外，上面例子中还使用了&lt;code class=&quot;highlighter-rouge&quot;&gt;Variables&lt;/code&gt;块来定义变量。&lt;/p&gt;

&lt;h2 id=&quot;高级功能&quot;&gt;高级功能&lt;/h2&gt;
&lt;p&gt;现在已经学会了写自己关键字、资源文件和测试用例文件的基本知识。当然还会有一些更高级的功能。下面列了两个你可能会使用到的功能。&lt;/p&gt;

&lt;h3 id=&quot;设置和拆卸&quot;&gt;设置和拆卸&lt;/h3&gt;
&lt;p&gt;下面这个列子显示了在执行关键字前，需要做的准备工作以及测试完成后做的清理工作。&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*** Settings ***
Suite Setup       Setup Actions
Suite Teardown    Teardown Actions

*** Test Cases ***
Test Robot Framework Logging
    Log    Test Logging

*** Keywords ***
Setup Actions
    Log    Setup Actions done here

Teardown Actions
    Log    Teardown Actions done here
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;打标签&quot;&gt;打标签&lt;/h3&gt;
&lt;p&gt;打标签和关键字并没有关系，但是这是一个很好的方式，在测试完成后产生的测试报表中有利于更好地理解报表。&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*** Settings ***
Suite Setup       Setup Actions
Suite Teardown    Teardown Actions

*** Test Cases ***
Test Robot Framework Logging
    [Tags]    sample   logging
    Log    Test Logging

*** Keywords ***
Setup Actions
    Log    Setup Actions done here

Teardown Actions
    Log    Teardown Actions done here
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;这些标签在报表中会进行汇总，你可以查看有多少测试用例与这些标签有关。&lt;/p&gt;

&lt;h2 id=&quot;报告与日志文件&quot;&gt;报告与日志文件&lt;/h2&gt;
&lt;p&gt;Robot Framework框架产生的报告和日志文件功能是非常强大的。&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/keywords-report.jpg&quot; alt=&quot;Report File&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;正如上面的截图，显示了测试结果不同维度的统计。你可以看到有多少测试用例打上了同一个标签。另外，你还可以按照测试套件查看统计，如下图。&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/keywords-report-suites.jpg&quot; alt=&quot;Report File Details&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;值得高兴的是你还可以继续深挖，点击某一个具体用例查看其具体的结果。这样会打开一个新的日志页面。这个页面可以查看每一个关键字被执行的结果。如果一个用例执行失败了，可以非常方便地查看到具体是什么原因导致的。&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/keywords-log.jpg&quot; alt=&quot;Log File&quot; /&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;参考文档&quot;&gt;参考文档&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://robotframework.org/robotframework/&quot;&gt;Robot Framework文档&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html&quot;&gt;Robot Framework用户说明文档&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://robotframework.org/robotframework/#standard-libraries&quot;&gt;Robot Framework标准库&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/ThomasJaspers/robot-keyword-tutorial&quot;&gt;Keywords语法简明教程&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/abekthink/robot-framework-demo&quot;&gt;本文代码示例&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/ThomasJaspers/robot-keyword-tutorial&quot;&gt;robot-keyword-tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Lei Geng</name><email>abekdev@163.com</email><uri>https://abekthink.github.io</uri></author><category term="robot" /><category term="robotframework" /><category term="autotest" /><category term="test" /><category term="agile" /><category term="keywords" /><category term="自动化测试" /><summary type="html">本文主要讲下如何使用Robot Framework搭建自己的自动化测试框架，包括如下几部分： 安装 关键字 整合Jenkins</summary></entry><entry><title type="html">Robot Framework教程 —— 安装</title><link href="https://abekthink.github.io/test/robot-framework-tutorial-installation/" rel="alternate" type="text/html" title="Robot Framework教程 —— 安装" /><published>2018-01-17T20:54:16+08:00</published><updated>2018-01-17T20:54:16+08:00</updated><id>https://abekthink.github.io/test/robot-framework-tutorial-installation</id><content type="html" xml:base="https://abekthink.github.io/test/robot-framework-tutorial-installation/">&lt;p&gt;本文主要讲下如何使用Robot Framework搭建自己的自动化测试框架，包括如下几部分：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/test/robot-framework-tutorial-installation&quot; title=&quot;安装&quot;&gt;安装&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/test/robot-framework-tutorial-keywords&quot; title=&quot;关键字&quot;&gt;关键字&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/test/robot-framework-tutorial-integration-jenkins&quot; title=&quot;整合Jenkins&quot;&gt;整合Jenkins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;介绍&quot;&gt;介绍&lt;/h2&gt;

&lt;h3 id=&quot;主要特性&quot;&gt;主要特性&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;它是一款基于Python的，可扩展的关键字驱动（keyword-driven）的自动化测试框架。&lt;/li&gt;
  &lt;li&gt;采用简单易用的表格式语法统一创建测试用例。&lt;/li&gt;
  &lt;li&gt;高复用性：提供了关键字语法，可以使用现有的关键字创建高级的关键字。&lt;/li&gt;
  &lt;li&gt;提供HTML格式的测试结果报表，简单易读。&lt;/li&gt;
  &lt;li&gt;跨平台和应用，不依赖相关环境。&lt;/li&gt;
  &lt;li&gt;提供简单API库创建自定义测试库，可以在本地使用Python或者Java运行。&lt;/li&gt;
  &lt;li&gt;提供命令行CLI界面和基于XML的输出文件。&lt;/li&gt;
  &lt;li&gt;支持Selemium页面测试，Java GUI测试，运行进程，Telnet，SSH等。&lt;/li&gt;
  &lt;li&gt;支持创建数据驱动（data-driven）的测试用例。&lt;/li&gt;
  &lt;li&gt;内置支持变量，尤其适用于在不同环境下进行测试。&lt;/li&gt;
  &lt;li&gt;提供标签分类和可以指定测试用例执行。&lt;/li&gt;
  &lt;li&gt;易于与源码控制整合，测试套件可以是文件或者字典，可以按照源码版本进行管理。&lt;/li&gt;
  &lt;li&gt;提供测试用例和测试套件级别的设置（setup）和拆卸（teardown）&lt;/li&gt;
  &lt;li&gt;模块化架构：支持为多种不同类型接口的应用提供创建针对性测试用例&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;架构设计&quot;&gt;架构设计&lt;/h3&gt;
&lt;p&gt;Robot Framework是一个通用的，独立于应用和技术的框架。它有一个高度模块化的体系结构，如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/robot-framework/architecture.jpg &quot; alt=&quot;Robot Framework architecture&quot; width=&quot;50%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里进行几点说明：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;测试数据（&lt;code class=&quot;highlighter-rouge&quot;&gt;Test Data&lt;/code&gt;）可以采用简单易编辑的表格形式进行撰写。&lt;/li&gt;
  &lt;li&gt;框架启动时，它可以获取测试数据，执行测试用例，并生成日志和报表。&lt;/li&gt;
  &lt;li&gt;核心的框架并不了解测试以下的系统，一般都是通过测试库（&lt;code class=&quot;highlighter-rouge&quot;&gt;Test Libraries&lt;/code&gt;）进行交互。&lt;/li&gt;
  &lt;li&gt;库既可以直接使用应用接口，也可以使用低级别的测试工具（&lt;code class=&quot;highlighter-rouge&quot;&gt;Test Tools&lt;/code&gt;）作为驱动。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;相关截图&quot;&gt;相关截图&lt;/h3&gt;
&lt;p&gt;下面是测试数据和测试报告的一些截图示例：&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/test-case-data.jpg&quot; alt=&quot;Test Case Data&quot; /&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;/assets/images/robot-framework/test-case-report.jpg&quot; alt=&quot;Test Case Report&quot; /&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;安装&quot;&gt;安装&lt;/h2&gt;
&lt;p&gt;安装一般有两种方式：Python环境下直接进行安装；或者Java环境下直接使用一个独立Jar包。&lt;/p&gt;

&lt;h3 id=&quot;python&quot;&gt;Python&lt;/h3&gt;
&lt;p&gt;最简单直接的方式，直接使用pip命令进行安装。&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install the latest version&lt;/span&gt;
pip install robotframework

&lt;span class=&quot;c&quot;&gt;# Upgrade to the latest version&lt;/span&gt;
pip install &lt;span class=&quot;nt&quot;&gt;--upgrade&lt;/span&gt; robotframework
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，也可以将&lt;a href=&quot;https://github.com/robotframework/robotframework&quot;&gt;Github源码&lt;/a&gt;下载下来，进行手动安装：&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python setup.py install
jython setup.py install
ipy setup.py install
pypy setup.py install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;备注：不同的python环境请使用对应的解释器。&lt;/p&gt;

&lt;h3 id=&quot;java&quot;&gt;Java&lt;/h3&gt;
&lt;p&gt;Java相对简单些，直接去&lt;a href=&quot;http://search.maven.org/#search%7Cga%7C1%7Ca%3Arobotframework&quot;&gt;Maven中心仓库&lt;/a&gt;下载最新的Jar包即可，包的命名是&lt;code class=&quot;highlighter-rouge&quot;&gt;robotframework-&amp;lt;version&amp;gt;.jar&lt;/code&gt;。
使用时直接执行如下类似指令即可：&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;java &lt;span class=&quot;nt&quot;&gt;-jar&lt;/span&gt; robotframework-3.0.2.jar mytests.robot
java &lt;span class=&quot;nt&quot;&gt;-jar&lt;/span&gt; robotframework-3.0.2.jar &lt;span class=&quot;nt&quot;&gt;--variable&lt;/span&gt; name:value mytests.robot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;参考文档&quot;&gt;参考文档&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/robotframework/robotframework&quot;&gt;Robot Framework源码&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://robotframework.org/&quot;&gt;Robot Framework主站&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://robotframework.org/robotframework/&quot;&gt;Robot Framework文档&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html&quot;&gt;Robot Framework用户说明文档&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Lei Geng</name><email>abekdev@163.com</email><uri>https://abekthink.github.io</uri></author><category term="robot" /><category term="robotframework" /><category term="autotest" /><category term="test" /><category term="agile" /><category term="自动化测试" /><summary type="html">本文主要讲下如何使用Robot Framework搭建自己的自动化测试框架，包括如下几部分： 安装 关键字 整合Jenkins</summary></entry><entry><title type="html">使用JWT保证服务间通信的安全</title><link href="https://abekthink.github.io/backend/json-web-token/" rel="alternate" type="text/html" title="使用JWT保证服务间通信的安全" /><published>2017-12-19T21:08:10+08:00</published><updated>2017-12-19T21:08:10+08:00</updated><id>https://abekthink.github.io/backend/json-web-token</id><content type="html" xml:base="https://abekthink.github.io/backend/json-web-token/">&lt;p&gt;本文主要讲下JWT(JSON Web Token)的基本原理，以及为什么使用它，如何使用它。&lt;/p&gt;

&lt;h2 id=&quot;什么是jwt&quot;&gt;什么是JWT&lt;/h2&gt;
&lt;p&gt;先看看JWT的定义：&lt;/p&gt;

&lt;p class=&quot;notice--info&quot;&gt;A JSON Web Token (JWT) is a &lt;a href=&quot;https://www.w3schools.com/js/js_json_objects.asp&quot;&gt;JSON object&lt;/a&gt; that is defined in &lt;a href=&quot;https://tools.ietf.org/html/rfc7519&quot;&gt;RFC 7519&lt;/a&gt; as a safe way to represent a set of information between two parties. The token is composed of a header, a payload, and a signature.&lt;/p&gt;

&lt;p&gt;翻译过来就是说：JWT是一种基于RFC 7519标准的JSON对象，主要是为了双方通信的安全而制定的。它包含头部（header），载荷（payload）和签名（signature）三部分。&lt;/p&gt;

&lt;h2 id=&quot;为什么使用jwt&quot;&gt;为什么使用JWT&lt;/h2&gt;
&lt;p&gt;说到这里，就要说下传统的基于session的用户认证方式以及基于token的区别了。&lt;/p&gt;

&lt;h3 id=&quot;基于session的用户认证方式&quot;&gt;基于session的用户认证方式&lt;/h3&gt;
&lt;p&gt;大家都知道，http是一种无状态的协议，用户登录成功后如果下次再次访问还需要再次认证，或者下次访问时携带相关的认证信息也是可以的。
session认证就是后面这种实现方式：当用户登录成功后，由服务端为用户生成相应的认证信息存储在服务端，并在请求响应返回前，将认证信息写入响应的cookie内；这样，用户下一次请求时携带之前cookie的认证信息即可，服务端在收到请求后，对cookie里的信息与服务端的session进行比对认证，验证通过后即可进行后续处理。&lt;/p&gt;

&lt;p&gt;这种方式的缺点是：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;如果客户端不能支持cookie功能，接入会非常困难。&lt;/li&gt;
  &lt;li&gt;服务端存储了用户的session信息，这些信息随着用户量的增大，服务端的空间开销都会不断增加。&lt;/li&gt;
  &lt;li&gt;服务器的session信息是需要独立存储的，如果数据分散存放在各个服务器，那么还要考虑用户是在哪台服务器登录了，难于扩展和维护。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;基于token的用户认证方式&quot;&gt;基于token的用户认证方式&lt;/h3&gt;
&lt;p&gt;基于token的用户认证是一种服务端无状态的认证方式，服务端无需存储用户的认证信息或者会话信息，并且任何服务器拿到token都能进行用户认证。&lt;/p&gt;

&lt;p&gt;主要流程大致如下：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;用户使用密码登录或者第三方登录。&lt;/li&gt;
  &lt;li&gt;服务器收到用户的登录信息进行身份认证；通过身份验证后，服务器生成token，并将其返回。&lt;/li&gt;
  &lt;li&gt;客户端收到token后存储在本地，并在后续的请求中均携带该token。&lt;/li&gt;
  &lt;li&gt;服务器收到后续的用户请求时，用请求的token进行用户认证，验证通过后即可进行后续处理。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这种认证方式相对简单，而且可扩展性很强，无需考虑服务器的单点问题等。我这里选用了JWT作为token的生成策略。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/jwt/jwt-auth.png&quot; alt=&quot;How an application uses JWT to verify the authenticity of a user.&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;jwt生成与验证&quot;&gt;JWT生成与验证&lt;/h2&gt;

&lt;h3 id=&quot;jwt格式&quot;&gt;JWT格式&lt;/h3&gt;

&lt;p&gt;刚才已经提到，JWT包含三个组件：头部，载荷和签名。其格式大致如下：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;header.payload.signature
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里举一个比较真实的例子，如下：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIzMmNkOGI4M2RjZTk0MDBjOGNjOWRiNmVkNjdhZjBkOSIsImlzcyI6ImFiZWt0aGluay5naXRodWIuaW8iLCJleHAiOjE1MTM3NjA1MjksImlhdCI6MTUxMzI2MDUyOSwiYXVkIjoic29tZW9uZSIsInN1YiI6ImFiZWt0aGluay5naXRodWIuaW8ifQ.O0laKQkICjLO5V4gY_LWqADdEtjCgqM_deFHduqBMTk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;header&quot;&gt;header&lt;/h3&gt;
&lt;p&gt;header主要包含着token是如何加密生成的信息，是一个类似如下格式的JSON对象：&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;typ&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;JWT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;alg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;HS256&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;这里，typ指定了这个对象是一个JWT对象，alg指明了用于生成JWT签名组件用到的算法。在这个例子中，我们用到了&lt;code class=&quot;highlighter-rouge&quot;&gt;HMAC-SHA256&lt;/code&gt;算法（只要提供一个secret即可进行加密的算法）。&lt;/p&gt;

&lt;h3 id=&quot;payload&quot;&gt;payload&lt;/h3&gt;
&lt;p&gt;payload也是一个JSON对象，它主要包含了一些用户的有效信息，例如：&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;uid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;32cd8b83dce9400c8cc9db6ed67af0d9&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;iss&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;abekthink.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;exp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1513760529&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;iat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1513260529&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;aud&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;someone&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;sub&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;abekthink.github.io&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;标准中的保留字如下：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;变量名&lt;/th&gt;
      &lt;th&gt;英文全写&lt;/th&gt;
      &lt;th&gt;备注&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;iss&lt;/td&gt;
      &lt;td&gt;Issuer&lt;/td&gt;
      &lt;td&gt;该JWT的发布者&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;sub&lt;/td&gt;
      &lt;td&gt;Subject&lt;/td&gt;
      &lt;td&gt;该JWT面向的主体或者用户&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;aud&lt;/td&gt;
      &lt;td&gt;Audience&lt;/td&gt;
      &lt;td&gt;接收该JWT的用户&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;exp&lt;/td&gt;
      &lt;td&gt;Expiration Time&lt;/td&gt;
      &lt;td&gt;过期时间（单位为秒）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;nbf&lt;/td&gt;
      &lt;td&gt;Not Before&lt;/td&gt;
      &lt;td&gt;开始时间（单位为秒），在该时间之前无效&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;iat&lt;/td&gt;
      &lt;td&gt;Issued At&lt;/td&gt;
      &lt;td&gt;发布时间（单位为秒）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;jti&lt;/td&gt;
      &lt;td&gt;JWT ID&lt;/td&gt;
      &lt;td&gt;JWT唯一标识，区分不同发布者的统一的标识&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;字段的具体说明可以参考&lt;a href=&quot;https://tools.ietf.org/html/rfc7519#section-4.1.1&quot;&gt;RFC 7519&lt;/a&gt;。&lt;/p&gt;

&lt;p class=&quot;notice--warning&quot;&gt;&lt;strong&gt;备注：&lt;/strong&gt; header和payload部分，均采用base64加密。所以，任何人均可以解密出来，建议不要放用户敏感信息。&lt;/p&gt;

&lt;h3 id=&quot;signature&quot;&gt;signature&lt;/h3&gt;
&lt;p&gt;最后一部分是签名信息，它由header、payload以及secret三部分生成而来。&lt;/p&gt;

&lt;p&gt;首先，将header和payload分别用base64url加密，然后两段加密后的字符串用&lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt;连接起来；之后，将拼接后的字符串，用secret进行hash加密得到最终签名的部分，加密的算法就是之前header里alg指定的算法。这里用一段伪代码来表示签名的生成过程：&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// signature algorithm&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;base64urlEncode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;base64urlEncode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;signature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;jwt生成&quot;&gt;JWT生成&lt;/h3&gt;
&lt;p&gt;将加密后的header、payload和signature三部分用&lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt;连接起来，即最后的JWT。&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// header(after base64url)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// payload(after base64url)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;eyJ1aWQiOiIzMmNkOGI4M2RjZTk0MDBjOGNjOWRiNmVkNjdhZjBkOSIsImlzcyI6ImFiZWt0aGluay5naXRodWIuaW8iLCJleHAiOjE1MTM3NjA1MjksImlhdCI6MTUxMzI2MDUyOSwiYXVkIjoic29tZW9uZSIsInN1YiI6ImFiZWt0aGluay5naXRodWIuaW8ifQ&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// signature&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;O0laKQkICjLO5V4gY_LWqADdEtjCgqM_deFHduqBMTk&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// final jwt&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;eyJ1aWQiOiIzMmNkOGI4M2RjZTk0MDBjOGNjOWRiNmVkNjdhZjBkOSIsImlzcyI6ImFiZWt0aGluay5naXRodWIuaW8iLCJleHAiOjE1MTM3NjA1MjksImlhdCI6MTUxMzI2MDUyOSwiYXVkIjoic29tZW9uZSIsInN1YiI6ImFiZWt0aGluay5naXRodWIuaW8ifQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;O0laKQkICjLO5V4gY_LWqADdEtjCgqM_deFHduqBMTk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;notice--warning&quot;&gt;&lt;strong&gt;备注：&lt;/strong&gt; 这里hash算法用的是HS256（即&lt;code class=&quot;highlighter-rouge&quot;&gt;HMAC-SHA256&lt;/code&gt;），所以它只需要一个secret即可以完成JWT的生成和验证，该secret仅保存在服务端，必须保密；如果想要安全系数更高，建议采用使用公钥私钥的RS256（即&lt;code class=&quot;highlighter-rouge&quot;&gt;RSA-SHA256&lt;/code&gt;）算法。&lt;/p&gt;

&lt;h3 id=&quot;jwt验证&quot;&gt;JWT验证&lt;/h3&gt;
&lt;p&gt;前面已经详细地介绍了JWT是如何生成的，这里主要说下，当用户发来一个JWT，服务端如何进行验证。&lt;/p&gt;

&lt;p&gt;服务端收到JWT，主要的验证流程如下：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;先验证签名是否一致，即通过header和payload，再加上服务器的secret再次生成签名，看是否与JWT的第三部分一致。如果不一致说明token被篡改，应该拒绝该请求。&lt;/li&gt;
  &lt;li&gt;验证iss、sub、aud是否与之前生成token的相应配置一致。&lt;/li&gt;
  &lt;li&gt;验证nbf和exp是否在合理的有效期内。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;第一步服务端必须进行验证；后面的两部分可依据自己的应用场景进行选择性处理。&lt;/p&gt;

&lt;h2 id=&quot;注意事项&quot;&gt;注意事项&lt;/h2&gt;
&lt;p&gt;这里再次提醒下大家要注意的几点：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;JWT生成过程中不要放用户的敏感信息，因为很容易泄露。&lt;/li&gt;
  &lt;li&gt;出于安全考虑，建议对iss、sub、aud、nbf和exp字段均进行验证。&lt;/li&gt;
  &lt;li&gt;请尽量使用https协议。&lt;/li&gt;
  &lt;li&gt;别重复造轮子，现在有很多现成的&lt;a href=&quot;https://jwt.io/&quot;&gt;JWT开源库&lt;/a&gt;，包括c、python、java、nodejs、javascript、ruby、go等各种主流语言的版本。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;参考文档&quot;&gt;参考文档&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc7519&quot;&gt;RFC 7519标准&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/vandium-software/5-easy-steps-to-understanding-json-web-tokens-jwt-1164c0adfcec&quot;&gt;5步轻松理解JWT&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://jwt.io/&quot;&gt;JWT开源库&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Lei Geng</name><email>abekdev@163.com</email><uri>https://abekthink.github.io</uri></author><category term="JWT" /><category term="JSON Web Token" /><category term="Authentication Server" /><category term="认证" /><summary type="html">本文主要讲下JWT(JSON Web Token)的基本原理，以及为什么使用它，如何使用它。</summary></entry><entry><title type="html">一小时内使用Jekyll搭建自己的GitHub博客</title><link href="https://abekthink.github.io/website/write-blogs-using-github-and-jekyll/" rel="alternate" type="text/html" title="一小时内使用Jekyll搭建自己的GitHub博客" /><published>2017-12-17T20:18:00+08:00</published><updated>2017-12-17T20:18:00+08:00</updated><id>https://abekthink.github.io/website/write-blogs-using-github-and-jekyll</id><content type="html" xml:base="https://abekthink.github.io/website/write-blogs-using-github-and-jekyll/">&lt;p&gt;这里主要讲下自己是怎么一步一步从零开始搭建自己的博客的。&lt;/p&gt;

&lt;h2 id=&quot;安装ruby&quot;&gt;安装ruby&lt;/h2&gt;
&lt;p&gt;如果你的本机是macOSx，则执行如下命令即可完成ruby的安装：&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew install ruby
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果机器是其他操作系统，这里就不详述了，可以去参考下&lt;a href=&quot;https://www.ruby-lang.org/en/documentation/installation/&quot;&gt;Ruby官方安装文档&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;另外，如果gem没有安装成功，可以参考&lt;a href=&quot;https://rubygems.org/pages/download&quot;&gt;RubyGems&lt;/a&gt;去安装gem。&lt;/p&gt;

&lt;h2 id=&quot;安装jekyll&quot;&gt;安装Jekyll&lt;/h2&gt;
&lt;p&gt;在命令行里执行如下命令，即可完成jekyll的安装：&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem install jekyll bundler
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;notice--warning&quot;&gt;&lt;strong&gt;备注：&lt;/strong&gt; bundler类似于python的pip工具，用于管理项目中的包依赖等，非常好用；另外，bundler需要项目根目录有一个Gemfile文件，类似python项目的的requirements.txt文件。&lt;/p&gt;

&lt;h2 id=&quot;创建自己的github-pages&quot;&gt;创建自己的GitHub Pages&lt;/h2&gt;
&lt;p&gt;请进入&lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;，按照步骤创建自己的站点。&lt;/p&gt;

&lt;p&gt;如果需要本地配置，请参考&lt;a href=&quot;https://help.github.com/articles/setting-up-your-github-pages-site-locally-with-jekyll/&quot;&gt;GitHub Pages本地设置&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;初始化自己的站点&quot;&gt;初始化自己的站点&lt;/h2&gt;
&lt;p&gt;在命令行里执行如下命令，即可完成jekyll站点的初始化：&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jekyll new myblog
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;进入项目，即可进行本地测试：&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;myblog/
bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;jekyll serve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;添加jekyll主题&quot;&gt;添加Jekyll主题&lt;/h2&gt;
&lt;p&gt;我自己选择了&lt;a href=&quot;https://github.com/mmistakes/minimal-mistakes&quot;&gt;Minimal Mistakes&lt;/a&gt;主题。&lt;/p&gt;

&lt;p&gt;创建或者编辑根目录下的Gemfile，增改或者增加如下代码：&lt;/p&gt;
&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://rubygems.org&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;github-pages&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;group: :jekyll_plugins&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jekyll-remote-theme&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;执行如下命令，确保包依赖安装完毕：&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bundle update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;修改根目录下的_config.yml文件：&lt;/p&gt;

&lt;p&gt;增加&lt;code class=&quot;highlighter-rouge&quot;&gt;remote_theme: &quot;mmistakes/minimal-mistakes&quot;&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;然后将&lt;a href=&quot;https://github.com/benbalter/jekyll-remote-theme&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;jekyll-remote-theme&lt;/code&gt;&lt;/a&gt;添加至自己&lt;code class=&quot;highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt;文件中&lt;code class=&quot;highlighter-rouge&quot;&gt;plugins&lt;/code&gt;数组中，例如:&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;plugins:
  - jekyll-remote-theme
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最后，你可以使用&lt;code class=&quot;highlighter-rouge&quot;&gt;bundle exec jekyll serve&lt;/code&gt;测试主题是否添加成功了。&lt;/p&gt;

&lt;h2 id=&quot;撰写博文&quot;&gt;撰写博文&lt;/h2&gt;
&lt;p&gt;撰写博文时，需要将文件放在&lt;code class=&quot;highlighter-rouge&quot;&gt;_post&lt;/code&gt;目录下，文件名的格式一般为&lt;code class=&quot;highlighter-rouge&quot;&gt;YEAR-MONTH-DAY-title.MARKUP&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;当&lt;code class=&quot;highlighter-rouge&quot;&gt;YEAR&lt;/code&gt;是一个四位数时，&lt;code class=&quot;highlighter-rouge&quot;&gt;MONTH&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;DAY&lt;/code&gt;必须都是两位数，&lt;code class=&quot;highlighter-rouge&quot;&gt;MARKUP&lt;/code&gt;是文件名后缀，下面两个是正确的例子：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2016-06-04-hello-world.md
2017-08-18-writing-blogs.markdown
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如何撰写不熟悉markdown文件，请参考：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://guides.github.com/features/mastering-markdown/&quot;&gt;Mastering Markdown&lt;/a&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://xianbai.me/learn-md/index.html&quot;&gt;Learning-Markdown&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;配置评论&quot;&gt;配置评论&lt;/h2&gt;
&lt;p&gt;在配置staticman V2时，需要关注以下几点：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;reCaptcha的密钥等设置，需要在&lt;code class=&quot;highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;staticman.yml&lt;/code&gt;两个文件中都添加。&lt;/li&gt;
  &lt;li&gt;reCaptcha的secret需要进行&lt;a href=&quot;https://staticman.net/docs/encryption&quot;&gt;staticman密钥加密&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;本地无法启动问题&quot;&gt;本地无法启动问题&lt;/h2&gt;
&lt;p&gt;在本地启动时如果出现如下报错，基本上是因为国内网络下载或者unzip解压有问题导致。&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jekyll 3.6.2 | Error:  Zip end of central directory signature not found
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;出现这种问题，可以考虑采取以下两种方式：&lt;/p&gt;

&lt;p&gt;第一种：将依赖的包解压换成&lt;code class=&quot;highlighter-rouge&quot;&gt;rubyzip&lt;/code&gt;，例如：&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem 'jekyll-remote-theme', github: 'benbalter/jekyll-remote-theme', branch: 'rubyzip'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第二种：如果是remote_theme无法下载，可以考虑将依赖提前下载安装，theme直接使用，例如：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;首先，将&lt;code class=&quot;highlighter-rouge&quot;&gt;gem &quot;minimal-mistakes-jekyll&quot;&lt;/code&gt;加入到&lt;code class=&quot;highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;中&lt;/li&gt;
  &lt;li&gt;然后，执行&lt;code class=&quot;highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;更新依赖&lt;/li&gt;
  &lt;li&gt;最后，在&lt;code class=&quot;highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt;文件中设置&lt;code class=&quot;highlighter-rouge&quot;&gt;theme: minimal-mistakes-jekyll&lt;/code&gt;，需要将原来的&lt;code class=&quot;highlighter-rouge&quot;&gt;remote_theme&lt;/code&gt;设置移除&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;参考文档&quot;&gt;参考文档&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://mmistakes.github.io/minimal-mistakes/docs/configuration/&quot;&gt;Mistakes Theme Configuration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://staticman.net/docs/&quot;&gt;Staticman Docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.google.com/recaptcha&quot;&gt;reCaptcha&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/benbalter/jekyll-remote-theme/issues/5&quot;&gt;Error: No such file or directory - unzip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Lei Geng</name><email>abekdev@163.com</email><uri>https://abekthink.github.io</uri></author><category term="GitHub Pages" /><category term="Jekyll" /><summary type="html">这里主要讲下自己是怎么一步一步从零开始搭建自己的博客的。</summary></entry></feed>