背景
最近在浏览 Android 11 源代码的时候,发现在ART虚拟机头文件 art_method.h 中,存在大量的类似REQUIRES_SHARED(Locks::mutator_lock_);
的代码。
最近在浏览 Android 11 源代码的时候,发现在ART虚拟机头文件 art_method.h 中,存在大量的类似REQUIRES_SHARED(Locks::mutator_lock_);
的代码。
Brotli是一种全新的数据格式,可以提供比Zopfli高20-26%的压缩比。据谷歌研究,Brotli压缩速度同zlib的Deflate实现大致相同,而在Canterbury语料库上的压缩密度比LZMA和bzip2略大。
链接:Google开源Brotli压缩算法 。
微软使用了一种基于谷歌提供的C代码的实现,向.NET Core 2.1添加了Brotli压缩支持。由于Brotli得到了许多Web浏览器和Web服务器的广泛支持,所以.NET Core提供对这项技术的支持是非常有用的。
什么是 Brotli 压缩算法
Brotli最初发布于2015年,用于网络字体的离线压缩。Google软件工程师在2015年9月发布了包含通用无损数据压缩的Brotli增强版本,特别侧重于HTTP压缩。其中的编码器被部分改写以提高压缩比,编码器和解码器都提高了速度,流式API已被改进,增加更多压缩质量级别。新版本还展现了跨平台的性能改进,以及减少解码所需的内存。
与常见的通用压缩算法不同,Brotli使用一个预定义的120千字节字典。该字典包含超过13000个常用单词、短语和其他子字符串,这些来自一个文本和HTML文档的大型语料库。预定义的算法可以提升较小文件的压缩密度。
使用brotli替换deflate来对文本文件压缩通常可以增加20%的压缩密度,而压缩与解压缩速度则大致不变。使用Brotli进行流压缩的内容编码类型已被提议使用“br”。
Brotli 的实际压缩效果,需要根据需要来具体分析,目前并不是每个网站都有较好的加速效果,另外,目前仅仅被限制在HTTPS上,HTTP是不支持的。
在 ubuntu 20.04 系统上自带的 apache 2.4.41 上启用 brotli 压缩算法的方式如下:
1 2 3 |
$ sudo apt-get install brotli $ sudo a2enmod brotli |
在 Apache 2 的配置文件增加 brolti 的配置信息,如下:
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 |
<VirtualHost 1.2.3.4:443> DocumentRoot /var/www/ ServerName example.com SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem Include /etc/letsencrypt/options-ssl-apache.conf CustomLog /var/log/apache2/example.com.access.log common ErrorLog /var/log/apache2/example.com.error.log <IfModule mod_brotli.c> SetOutputFilter BROTLI_COMPRESS;DEFLATE BrotliCompressionQuality 6 BrotliCompressionWindow 18 AddOutputFilterByType BROTLI_COMPRESS text/html AddOutputFilterByType BROTLI_COMPRESS text/plain AddOutputFilterByType BROTLI_COMPRESS text/xml AddOutputFilterByType BROTLI_COMPRESS text/css AddOutputFilterByType BROTLI_COMPRESS text/javascript AddOutputFilterByType BROTLI_COMPRESS application/x-javascript AddOutputFilterByType BROTLI_COMPRESS application/javascript AddOutputFilterByType BROTLI_COMPRESS application/json AddOutputFilterByType BROTLI_COMPRESS application/x-font-ttf AddOutputFilterByType BROTLI_COMPRESS application/vnd.ms-fontobject AddOutputFilterByType BROTLI_COMPRESS image/x-icon </IfModule> </VirtualHost> |
重启服务,如下:
1 |
$ sudo systemctl restart apache2.service |
测试结果
1 2 3 4 5 6 7 8 9 10 11 12 |
$ curl -I -H 'Accept-Encoding: br' https://www.mobibrw.com HTTP/1.1 200 OK Date: Wed, 10 Feb 2021 01:17:49 GMT Server: Apache Vary: Accept-Encoding,Cookie Cache-Control: max-age=3, must-revalidate Upgrade: h2 Connection: Upgrade Last-Modified: Wed, 10 Feb 2021 01:16:24 GMT Content-Encoding: br Content-Length: 1 Content-Type: text/html; charset=UTF-8 |
我们认识到在移动端开发中安全性设置非常重要,尤其是目前非常流程H5混合式开发APP,在Android开发中,我们可以通过证书锁定的方式来增加客户端与服务端的安全保障《证书锁定SSL Pinning简介及用途》,本文主要介绍 SSL数字证书在Android开发中的证书锁定(SSL/TLS Pinning)
通常由CA权威机构签发的证书,其根证书都内置在最新的Android操作系统中,因此默认情况下可不进行SSL证书锁定,开发APP时也就变得非常简单,以infinisign.com为例,摘自App security best practices
1 2 3 4 |
URL url = new URL("https://infinisign.com"); HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); urlConnection.connect(); InputStream in = urlConnection.getInputStream(); |
1 2 3 4 5 6 |
val url = URL("https://infinisign.com") val urlConnection = url.openConnection() as HttpsURLConnection urlConnection.connect() urlConnection.inputStream.use { ... } |
本方案是官方提供,但需要依赖Android N(Android 7.0 API 24)及以后版本,可在APP开发阶段在APP中内置安全性设置,以达到防止中间人攻击(MITM)的目的,此方法只限制在Android 7.0 API 24以后版本,因此该版本之前的安全性设置仍然需要使用证书锁定方法,本文以infinisign.com为例。
创建文件res/xml/network_security_config.xml
,需要注意的是,使用证书锁定,需要配置一个备份密钥,假如证书到期或更换了CA品牌后,不至于重新发行APP,这个备份密码可以是中级证书或根证书。
通俗的说,如果系统检测到签发证书过期了,则自动使用其中级或才根级证书作为验证,因为通常中级机构、根机构的证书到期时间非常长。
但实际情况是,infinisign.com所售的CA签发证书有效期都是一年,而现在发行APP或更新APP通常在一年都会有更新重新上架操作。
1 2 3 4 5 6 7 8 9 |
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">infinisign.com</domain> <pin digest="SHA-256">wLgBEAGmLltnXbK6pzpvPMeOCTKZ0QwrWGem6DkNf6o</pin> <!-- 备份密钥,比如infinisign.com的中级机构是geotrust --> <pin digest="SHA-256">wLgBEAGmLltnXbK6pzpvPMeOCTKZ0QwrWGem6DkNf6o</pin> </domain-config> </network-security-config> |
在Androidmanifest.xml
引入配置文件android:networkSecurityConfig="@xml/network_security_config"
1 2 3 4 5 6 7 |
<?xml version="1.0" encoding="utf-8"?> <manifest ... > <application android:networkSecurityConfig="@xml/network_security_config"> <!-- application 其它子元素 --> </application> </manifest> |
okHttp是一个用于Android处理网络请求的开源项目,是安卓端最流行的轻量级的网络框架,其主要用来替代HttpUrlConnection处理方式
1 2 3 4 5 |
client = new OkHttpClient.Builder() .certificatePinner(new CertificatePinner.Builder() .add("infinisign.com", "sha256/S8Ff3JCaO4V...") .build()) .build(); |
不过需要注意的是,okHttp锁定证书方式不适用于公钥锁定方式,必须以证书锁定方式内置SSL数字证书。
TrustManager是一个比较老的证书锁定方法,主要用于早期的Android版本或者用于一些CA根机构在Android系统中缺失根证书的情形下,当然也适用于自签名证书的锁定,通常我们不建议这样做,因为TrustManager的缺点是中间人仍然可以使用成熟的绕过方案来实现截持。
在SSL普及的今天,let's encrypt的开源免费解决方案,和一些入门的便宜DV域名型SSL证书(见PositiveSSL ¥39/年)足以媲美自签名方案。
详细请参考javax.net.ssl.TrustManager接口实现,简单步骤如下
在APP源码中内置证书
1 |
/res/raw |
使用KeyStore加载证书
1 2 3 4 |
val resourceStream = resources.openRawResource(R.raw.infinisign_cert) val keyStoreType = KeyStore.getDefaultType() val keyStore = KeyStore.getInstance(keyStoreType) keyStore.load(resourceStream, null) |
TrustManagerFactory实例化证书
1 2 3 |
val trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm() val trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm) trustManagerFactory.init(keyStore) |
创建SSLContext实例,与TrustManager进行绑定。
1 2 3 4 5 |
val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, trustManagerFactory.trustManagers, null) val url = URL("http://infinisign.com/") val urlConnection = url.openConnection() as HttpsURLConnection urlConnection.sslSocketFactory = sslContext.socketFactory |
在多数移动操作系统中有大大小小几十个CA机构内置的根证书,但也不排除已经不被信任的CA机构存在的旧根证书,还有一些例如国内的一些基于Android老版本的操作系统仍然面临着安全风险,所以使用SSL数字证书锁定(SSL/TLS Pinning)的目标是缩小可信CA的范围,让APP客户端传送数据更安全,更切实地保障用户数据。
本次涉及漏洞
1.漏洞名称:SSL 3.0 POODLE攻击信息泄露漏洞(CVE-2014-3566)【原理扫描】
2.SSL/TLS 受诫礼(BAR-MITZVAH)攻击漏洞(CVE-2015-2808)【原理扫描】
知识普及1:SSL协议要点
SSL(Secure Sockets Layer 安全套接层)是一种基于Web应用的安全通信协议,最早由Netscape(网景)公司提出。SSL介于TCP协议和应用层协议之间,主要作用就是将HTTP、FTP等应用层的数据进行加密然后依托可靠的TCP协议在互联网上传输到目的地,其中最典型的应用就是https。
SSL提供3个基本的安全服务:
1)身份合法性:数据发送方和接收方要确认彼此身份,要确保各自的身份不会被冒充。
2)数据机密性:所有传输的数据都进行加密,并且要确保即使数据被截获也无法破解。
3)数据完整性:确保收到的数据与发送方发出的数据一致,没有被篡改。
SSL协议主要采用的数据加密算法:
1)非对称加密算法:数据加密和解密使用不同的密钥,如RSA公钥加密算法。优点是安全级别高,很难被破解;缺点是加密解密的速度慢,因此只适用于小量数据的加密。SSL协议采用非对称加密算法实现数字签名,验证数据发送方(或接收方)的身份,同时也用非对称加密算法交换密钥(用于数据加密的对称加密算法的密钥,以及用于数据完整性验证的MAC算法)。
2)对称加密算法:数据加密和解密使用同一个密钥,如DES、3DES、RC4等都是对称加密算法。优点是加解密速度快,适用于大数据量的加密,但安全性较差。SSL协议采用对称加密算法对传输的数据进行加密。
3)MAC算法:Message Authentication Codes,即消息认证码算法,MAC含有密钥散列函数算法,兼容了MD和SHA算法的特性,并在此基础上加入了密钥。SSL协议采用MAC算法来检验消息的完整性。
知识普及2:SSL协议的版本
目前在用的SSL协议主要有5个版本,分别是SSL2.0、SSL3.0、TLS1.0、TLS1.1和TLS1.2,这里的TLS(Transport Layer Security,传输层安全)协议是SSL协议的升级版。
在SSL协议曝出Poodle漏洞后,微信公众平台将取消对SSLv2、SSLv3两个版本的支持,浏览器及其他采用SSL协议的平台也会逐渐取消对SSLv2、SSLv3的支持,目前只建议使用TLSv1.0、TLSv1.1和TLSv1.2三个版本。
1.漏洞名称:SSL 3.0 POODLE攻击信息泄露漏洞(CVE-2014-3566)【原理扫描】
涉及设备漏洞服务
此次设备受此漏洞波及影响主要是因为上述系统部署了Tomcat 的https服务,而Tomcat https服务默认情况下支持SSL 3.0协议(目前在用的SSL协议主要有5个版本,分别是SSL2.0、SSL3.0、TLS1.0、TLS1.1和TLS1.2.SSL3.0是已过时且不安全的协议,目前已被TLS 1.0,TLS 1.1,TLS 1.2替代)
漏洞描述和利用
SSL3.0是已过时且不安全的协议,目前已被TLS 1.0,TLS 1.1,TLS 1.2替代,因为兼容性原因,大多数的TLS实现依然兼容SSL3.0。
为了通用性的考虑,目前多数浏览器版本都支持SSL3.0,TLS协议的握手阶段包含了版本协商步骤,一般来说,客户端和服务器端的最新的协议版本将会被使用。其在与服务器端的握手阶段进行版本协商的时候,首先提供其所支持协议的最新版本,若该握手失败,则尝试以较旧的协议版本协商。能够实施中间人攻击的攻击者通过使受影响版本浏览器与服务器端使用较新协议的协商的连接失败,可以成功实现降级攻击,从而使得客户端与服务器端使用不安全的SSL3.0进行通信,此时,由于SSL 3.0使用的CBC块加密的实现存在漏洞,攻击者可以成功破解SSL连接的加密信息,比如获取用户cookie数据。这种攻击被称为POODLE攻击(Padding Oracle On Downgraded Legacy Encryption)。此漏洞影响绝大多数SSL服务器和客户端,影响范围广泛。但攻击者如要利用成功,需要能够控制客户端和服务器之间的数据(执行中间人攻击)。
一. tPacketcapture工具
1.什么是Packetcapture?
tPacketCapture数据包捕获,而无需使用任何root权限,原理是构建了一个本地VPN服务,引导网络请求流经VPN服务,从而实现抓包。 tPacketCapture使用了Android OS VpnService提供。 捕获的数据保存在外部存储为PCAP文件格式。
2.操作步骤
1)安装tPacketCapture.apk
2)打开该APP,点击"Capture"按钮
3)触发需要抓包的操作
4)用以下命令,adb pull /mnt/shell/emulated/0/Android/data/jp.co.taosoftware.android.packetcapture/files/,把文件拷贝出来了
5).pcap会保存在adb的相同路径下
6)将.pcap取出后,用wireshark打开就能看到了相关信息
哇,一看标题怎么这么长啊,其实意思很简单,哥讨厌用HTTP Client做POST与GET提交觉得那个毕竟是别人写得API库,所以我就自己实现了一个简单的HTTP客户端,支持POST方式提交数据,GET方式查询数据,是测试Restful API比较方便点,然后支持form与JSON两种方式提交数据,当然也支持返回数据为JSON格式.当然这些东西都是基于JAVA Socket直接完成的,不借助任何第三方的库,主要是JDK的API其实已经够用啦. 当然我也没有用URLConnect这个东西,毕竟它在Socket基础上又包装了一下,有违我写这篇文章的目的.
好啦,讲完理由,下面就说说要怎么样才能实现啊,光说不练假把式啊!大致分了几个步骤
一:当然是要知道HTTP协议,知道常用的HTTP请求头,比如Host, Accept, Content-Type 知道HTTP协议支持的方法,常用有GET/POST/PUT/DELETE等如果不知道,也不用担心,我保证你读完这篇文章,你就知道一些啦,当然有个最好的参考文档就是HTTP相关的RFC文档,认真读一下肯定解决你自己心中的HTTP那些疑惑
二: 知道发送HTTP GET与POST格式很重要, 固定的格式如下:
[REQUEST]<SP><URL><SP>[HTTP VERSION]<CLRF>
[REQUEST HEADER: ]<SP>[VALUE]<CLRF>
可以有多个请求头
最后<CLRF>
发送完HTTP请求头部以后, 针对不同请求如POST要发送内容部分,发送完成以后同样,以<CLRF>结尾.
解释: <SP>表示空格, <CLRF>表示回车换行JAVA中表示为”\r\n”REQUEST表示HTTP请求命令,可以为POST, GET, PUT, DELETE等之一,HTTP VERSION的常见可能值为HTTP/1.1或者HTTP/1.0
三: 如果1与2的知识你都具备了,下面就来介绍一下JAVA Socket的相关知识,如何创建一个JAVA客户端套接字Socket s = new Socket()如此即可,简单吧!如何连接到远程的主机与端口, 当提供URL字符串时候,可以这么做
1 2 3 4 5 6 7 8 9 |
URL url = new URL(“http://blog.csdn.net/jia20003”); String host = url.getHost; int port = url.getDefaultPort(); SocketAddress dest = new InetSocketAddress(this.host, this.port); s.connect(dest); |
即可连接到远程主机,下面就可以请求文章内容了, 也很容易的
1 2 3 4 5 6 7 8 9 |
String path = "/jia20003/article/details/16908661"; SocketAddress dest = new InetSocketAddress(this.host, this.port); socket.connect(dest); OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream()); bufferedWriter = new BufferedWriter(streamWriter); bufferedWriter.write("GET " + path + " HTTP/1.1\r\n"); bufferedWriter.write("Host: " + this.host + "\r\n"); bufferedWriter.write("\r\n"); bufferedWriter.flush(); |
最后也是最重要的一点,字符编码,尽量都用同一种字符编码来发 送请求数据,推荐使用utf-8测试程序, 我写两个简单的PHP文件放 在wamp server上,完成对全部代码的测试。
四:关于HTTP与HTTPS
HTTP协议是位于第四层协议TCP之上完成的应用层协议, 端到端都是明文传送,别人一旦网络抓包以后都可以看到你的提交与请求数据,这个好像不太安全. HTTP协议的默认端口是80这个是RFC文档声明的,属于官方标准,没什么道理可以讲.HTTPS是基于SSL加密传输的,这样别人截获你的数据包破解的概率要小一点,比HTTP安全一点,其默认端口是443, 好像QQ邮箱与谷歌的WEB Mail邮箱都是基于HTTPS. 但是HTTPS通信方式只是传输数据加密,都客户端来说是透明的,它还是一样要遵守HTTP协议规范来发送POST与GET请求等.完整的测试程序如下:
http协议实现:
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
package com.gloomyfish.http.client; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.URLEncoder; public class TestDemo { private int port; private String host; private Socket socket; private BufferedReader bufferedReader; private BufferedWriter bufferedWriter; public TestDemo(String host, int port) { socket = new Socket(); this.host = host; this.port = port; } public void sendGet() throws IOException { String path = "/zhigang/getDemo.php"; SocketAddress dest = new InetSocketAddress(this.host, this.port); socket.connect(dest); OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream()); bufferedWriter = new BufferedWriter(streamWriter); bufferedWriter.write("GET " + path + " HTTP/1.1\r\n"); bufferedWriter.write("Host: " + this.host + "\r\n"); bufferedWriter.write("\r\n"); bufferedWriter.flush(); BufferedInputStream streamReader = new BufferedInputStream(socket.getInputStream()); bufferedReader = new BufferedReader(new InputStreamReader(streamReader, "utf-8")); String line = null; while((line = bufferedReader.readLine())!= null) { System.out.println(line); } bufferedReader.close(); bufferedWriter.close(); socket.close(); } public void sendPost() throws IOException { String path = "/zhigang/postDemo.php"; String data = URLEncoder.encode("name", "utf-8") + "=" + URLEncoder.encode("gloomyfish", "utf-8") + "&" + URLEncoder.encode("age", "utf-8") + "=" + URLEncoder.encode("32", "utf-8"); // String data = "name=zhigang_jia"; SocketAddress dest = new InetSocketAddress(this.host, this.port); socket.connect(dest); OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream(), "utf-8"); bufferedWriter = new BufferedWriter(streamWriter); bufferedWriter.write("POST " + path + " HTTP/1.1\r\n"); bufferedWriter.write("Host: " + this.host + "\r\n"); bufferedWriter.write("Content-Length: " + data.length() + "\r\n"); bufferedWriter.write("Content-Type: application/x-www-form-urlencoded\r\n"); bufferedWriter.write("\r\n"); bufferedWriter.write(data); bufferedWriter.write("\r\n"); bufferedWriter.flush(); BufferedInputStream streamReader = new BufferedInputStream(socket.getInputStream()); bufferedReader = new BufferedReader(new InputStreamReader(streamReader, "utf-8")); String line = null; while((line = bufferedReader.readLine())!= null) { System.out.println(line); } bufferedReader.close(); bufferedWriter.close(); socket.close(); } public static void main(String[] args) { TestDemo td = new TestDemo("127.0.0.1",8099); try { // td.sendGet(); //send HTTP GET Request td.sendPost(); // send HTTP POST Request } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } |
想要成为支持HTTPS客户端, 只要在创建Socket的时候如下:
1 |
socket = (SSLSocket)((SSLSocketFactory)SSLSocketFactory.getDefault()).createSocket(this.host, port); |
https协议实现:
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
package zmx.socket.http; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import java.net.URLEncoder; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; public class SocketForHttpTest { private int port; private String host; private Socket socket; private BufferedReader bufferedReader; private BufferedWriter bufferedWriter; public SocketForHttpTest(String host,int port) throws Exception { this.host = host; this.port = port; /** * http协议 */ //socket = new Socket(this.host, this.port); /** * https协议 */ socket = (SSLSocket)((SSLSocketFactory)SSLSocketFactory.getDefault()).createSocket(this.host, this.port); } public void sendGet() throws IOException { //String requestUrlPath = "/z69183787/article/details/17580325"; String requestUrlPath = "/"; OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream()); bufferedWriter = new BufferedWriter(streamWriter); bufferedWriter.write("GET " + requestUrlPath + " HTTP/1.1\r\n"); bufferedWriter.write("Host: " + this.host + "\r\n"); bufferedWriter.write("\r\n"); bufferedWriter.flush(); BufferedInputStream streamReader = new BufferedInputStream(socket.getInputStream()); bufferedReader = new BufferedReader(new InputStreamReader(streamReader, "utf-8")); String line = null; while((line = bufferedReader.readLine())!= null){ System.out.println(line); } bufferedReader.close(); bufferedWriter.close(); socket.close(); } public void sendPost() throws IOException { String path = "/"; String data = URLEncoder.encode("name", "utf-8") + "=" + URLEncoder.encode("张三", "utf-8") + "&" + URLEncoder.encode("age", "utf-8") + "=" + URLEncoder.encode("32", "utf-8"); // String data = "name=zhigang_jia"; System.out.println(">>>>>>>>>>>>>>>>>>>>>"+data); OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream(), "utf-8"); bufferedWriter = new BufferedWriter(streamWriter); bufferedWriter.write("POST " + path + " HTTP/1.1\r\n"); bufferedWriter.write("Host: " + this.host + "\r\n"); bufferedWriter.write("Content-Length: " + data.length() + "\r\n"); bufferedWriter.write("Content-Type: application/x-www-form-urlencoded\r\n"); bufferedWriter.write("\r\n"); bufferedWriter.write(data); bufferedWriter.write("\r\n"); bufferedWriter.flush(); BufferedInputStream streamReader = new BufferedInputStream(socket.getInputStream()); bufferedReader = new BufferedReader(new InputStreamReader(streamReader, "utf-8")); String line = null; while((line = bufferedReader.readLine())!= null) { System.out.println(line); } bufferedReader.close(); bufferedWriter.close(); socket.close(); } public static void main(String[] args) throws Exception { /** * http协议测试 */ //SocketForHttpTest forHttpTest = new SocketForHttpTest("www.baidu.com", 80); /** * https协议测试 */ SocketForHttpTest forHttpTest = new SocketForHttpTest("www.baidu.com", 443); try { //forHttpTest.sendGet(); forHttpTest.sendPost(); } catch (IOException e) { e.printStackTrace(); } } } |
所以有时候离开apache的HTTP Client等第三方Jar,程序员也是一样活!
缓存的主要作用是暂时在内存中保存业务系统的数据处理结果,并且等待下次访问使用。在日长开发有很多场合,有一些数据量不是很大,不会经常改动,并且访问非常频繁。但是由于受限于硬盘IO的性能或者远程网络等原因获取可能非常的费时。会导致我们的程序非常缓慢,这在某些业务上是不能忍的!而缓存正是解决这类问题的神器!
1 2 3 4 5 6 7 |
$ sudo apt-get install libcanberra-gtk-module $ sudo apt install avahi-daemon $ sudo apt-get remove --purge samba $ sudo apt-get install samba |
编辑配置文件
1 |
$ sudo vim /etc/samba/smb.conf |
文件尾部增加
1 2 3 4 5 6 7 |
[Movie] comment = Movie path = /mnt/data/Movie/ # 需要共享的目录位置 browseable = yes # 是否可见 read only = yes # 是否只读 guest ok = yes # 登陆用户是否可访问 valid users = user # 可访问的用户 |
重启服务
1 |
$ sudo service smbd restart |
这里就不再赘述关于SVN与Git的区别以及为什么要迁移源码到Git了,毕竟Git是当前的主流DVCS了,而且已经公认地非常好用,如果你还在使用SVN的话该考虑换了,是时候迁移那些遗留代码了,有兴趣可以参阅 Why Git 和 Perforce to Git 了解更多。
通常来说,在项目开发过程中,难免会遇到一些老项目代码正被SVN管理着,但基于当下诸多原因,或是扩展开发,或是战略转移,或是为了更好地开发体验,需要将这些在维护的遗留项目源码迁移为Git管理。
那如何有效地迁移源码?并且如何保留提交记录、分支记录以及开发成员等信息呢?笔者前一段时间就经历了这样的迁移工作,还是有必要分享一下,也算是一种总结了。
由于app要实现登录缓存功能,但惊讶的发现不经过设置okHttp是不会自动管理header的.
官网的文档也是醉了,找了半天没看懂怎么搞.
其实实现自动管理cookie很简单很简单,在OkHttp的builder中加上个.cookiejar()就能实现自动缓存,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
OkHttpClient.Builder b = new OkHttpClient.Builder(); b.cookieJar(new CookieJar() { //这里一定一定一定是HashMap<String, List<Cookie>>,是String,不是url. private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>(); @Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { cookieStore.put(url.host(), cookies); } @Override public List<Cookie> loadForRequest(HttpUrl url) { List<Cookie> cookies = cookieStore.get(url.host()); return cookies != null ? cookies : new ArrayList<Cookie>(); } }); |
!!!!!注意注意注意!!!!!!
HashMap的key是String!!!!直接传进去url,是没有效果的!!!!!