正确操作Java集合
java 对于collection操作
删除
切记使用迭代器
正确的代码:1
2
3
4
5
6for(Iterator<String> it = famous.iterator(); it.hasNext();){
String s = it.next();
if(s.equals("madehua")){
it.remove();
}
}
错误的代码:1
2
3
4
5
6
7
8
9
10
11
12
13for(Iterator<String> it = famous.iterator(); it.hasNext();){
String s = it.next();
if(s.equals("madehua")){
famous.remove(s); // 使用了错误的remove方法
}
}
for (String s : famous) {
if (s.equals("madehua")) {
famous.remove(s);
}
}
Tomcat系统架构和设计模式
Tomcat 的总体结构
从上图中可以看出 Tomcat 的心脏是两个组件:Connector 和 Container,关于这两个组件将在后面详细介绍。Connector 组件是可以被替换,这样可以提供给服务器设计者更多的选择,因为这个组件是如此重要,不仅跟服务器的设计的本身,而且和不同的应用场景也十分相关,所以一个 Container 可以选择对应多个 Connector。多个 Connector 和一个 Container 就形成了一个 Service,Service 的概念大家都很熟悉了,有了 Service 就可以对外提供服务了,但是 Service 还要一个生存的环境,必须要有人能够给她生命、掌握其生死大权,那就非 Server 莫属了。所以整个 Tomcat 的生命周期由 Server 控制。
参考资料
SpringMVC记录
springmvc的一些常用最佳实践
内容概要:
- SpringMVC的源代码执行流程
- 使用WebDataBinder进行参数的个性化绑定和验证
- 使用高级的HandlerMethodArgumentResolver来实现参数的个性化解析
- 使用aspectj拦截器进行全局参数校验
- 全局错误处理,隐藏后端异常以及友好提示
- 使用Spring自带拦截器HandlerInterceptor进行权限控制
数据验证
Filter实现
Spring MVC 中 HandlerInterceptorAdapter的使用
参数提取
自定义WebArgumentResolver/HandlerMethodArgumentResolver从session中提取参数
https是如何确保安全的
https是如何确保安全的
什么是Https
HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL
Https的作用
内容加密 建立一个信息安全通道,来保证数据传输的安全;
身份认证 确认网站的真实性
数据完整性 防止内容被第三方冒充或者篡改
Https的劣势
对数据进行加解密决定了它比http慢
需要进行非对称的加解密,且需要三次握手。首次连接比较慢点,当然现在也有很多的优化。
出于安全考虑,浏览器不会在本地保存HTTPS缓存。实际上,只要在HTTP头中使用特定命令,HTTPS是可以缓存的。Firefox默认只在内存中缓存HTTPS。但是,只要头命令中有Cache-Control: Public,缓存就会被写到硬盘上。 IE只要http头允许就可以缓存https内容,缓存策略与是否使用HTTPS协议无关。
HTTPS和HTTP的区别
https协议需要到CA申请证书。
http是超文本传输协议,信息是明文传输;https 则是具有安全性的ssl加密传输协议。
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
数字证书
- 为什么要有数字证书?
对于请求方来说,它怎么能确定它所得到的公钥一定是从目标主机那里发布的,而且没有被篡改过呢?亦或者请求的目标主机本本身就从事窃取用户信息的不正当行为呢?这时候,我们需要有一个权威的值得信赖的第三方机构(一般是由政府审核并授权的机构)来统一对外发放主机机构的公钥,只要请求方这种机构获取公钥,就避免了上述问题的发生。
SSL、TLS的握手过程
SSL与TLS握手整个过程如下图所示,下面会详细介绍每一步的具体内容:
现在的https基本都使用TLS了,因为更加安全.
SSL协议在握手阶段使用的是非对称加密,在传输阶段使用的是对称加密,也就是说在SSL上传送的数据是使用对称密钥加密的!因为非对称加密的速度缓慢,耗费资源。其实当客户端和主机使用非对称加密方式建立连接后,客户端和主机已经决定好了在传输过程使用的对称加密算法和关键的对称加密密钥,由于这个过程本身是安全可靠的,也即对称加密密钥是不可能被窃取盗用的,因此,保证了在传输过程中对数据进行对称加密也是安全可靠的,因为除了客户端和主机之外,不可能有第三方窃取并解密出对称加密密钥!如果有人窃听通信,他可以知道双方选择的加密方法,以及三个随机数中的两个。整个通话的安全,只取决于第三个随机数(Premaster secret)能不能被破解。
总结
https实际就是在TCP层与http层之间加入了SSL/TLS来为上层的安全保驾护航,主要用到对称加密、非对称加密、证书,等技术进行客户端与服务器的数据加密传输,最终达到保证整个通信的安全性。
流程:
前提:CA证书
握手阶段: 生成随机数 + 数据摘要 + 数字签名 + 非对称加密算法 ==> 对称机密的密钥
数据传输阶段: 基于对称加密密钥的交流
如何定位消耗CPU最多的线程
#如何定位消耗CPU最多的线程
1 | public class Test{ |
这个例子里新创建了11个线程,其中10个线程没干什么事,主要是sleep,另外有一个线程在循环里一直跑着,可以想象这个线程是这个进程里最耗cpu的线程了,那怎么把这个线程给抓出来呢?
首先我们可以通过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``` shell
$ top -Hp 18207
top - 19:11:43 up 573 days, 2:43, 2 users, load average: 3.03, 3.03, 3.02
Tasks: 44 total, 1 running, 43 sleeping, 0 stopped, 0 zombie
Cpu(s): 18.8%us, 0.0%sy, 0.0%ni, 81.1%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 99191752k total, 98683576k used, 508176k free, 128248k buffers
Swap: 1999864k total, 191064k used, 1808800k free, 17413760k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18250 admin 20 0 26.1g 28m 10m R 99.9 0.0 0:19.50 java Test
18207 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18208 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.09 java Test
18209 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18210 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18211 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18212 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18213 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18214 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18215 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18216 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18217 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18218 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18219 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18220 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18221 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18222 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18223 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18224 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18225 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18226 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
18227 admin 20 0 26.1g 28m 10m S 0.0 0.0 0:00.00 java Test
拿到这个结果之后,我们可以看到cpu最高的线程是pid为18250的线程,占了99.8%:1
2PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18250 admin 20 0 26.1g 28m 10m R 99.9 0.0 0:19.50 java Test
接着我们可以通过1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35``` shell
$ jstack 18207
2016-03-30 19:12:23
Full thread dump OpenJDK 64-Bit Server VM (25.66-b60 mixed mode):
"Attach Listener" #30 daemon prio=9 os_prio=0 tid=0x00007fb90be13000 nid=0x47d7 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" #29 prio=5 os_prio=0 tid=0x00007fb96245b800 nid=0x4720 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Busiest Thread" #28 prio=5 os_prio=0 tid=0x00007fb91498d000 nid=0x474a runnable [0x00007fb9065fe000]
java.lang.Thread.State: RUNNABLE
at Test$2.run(Test.java:18)
"Thread-9" #27 prio=5 os_prio=0 tid=0x00007fb91498c800 nid=0x4749 waiting on condition [0x00007fb906bfe000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test$1.run(Test.java:9)
"Thread-8" #26 prio=5 os_prio=0 tid=0x00007fb91498b800 nid=0x4748 waiting on condition [0x00007fb906ffe000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test$1.run(Test.java:9)
"Thread-7" #25 prio=5 os_prio=0 tid=0x00007fb91498b000 nid=0x4747 waiting on condition [0x00007fb9073fe000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test$1.run(Test.java:9)
"Thread-6" #24 prio=5 os_prio=0 tid=0x00007fb91498a000 nid=0x4746 waiting on condition [0x00007fb9077fe000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test$1.run(Test.java:9)
...
上面的线程栈我们注意到nid的值其实就是线程ID,它是十六进制的,我们将消耗cpu最高的线程18250,转成十六进制0X47A,然后从上面的线程栈里找到nid=0X47A的线程,其栈为:1
2
3"Busiest Thread" #28 prio=5 os_prio=0 tid=0x00007fb91498d000 nid=0x474a runnable [0x00007fb9065fe000]
java.lang.Thread.State: RUNNABLE
at Test$2.run(Test.java:18)
即将最耗cpu的线程找出来了,是Businest Thread
Session原理、安全以及最基本的Express和Redis实现
Session原理、安全以及最基本的Express和Redis实现
Session基础概念
Session管理是Web Application的基础也是一个老生常谈的话题。为了方便后文的展开,更重要的是确认自己清晰的理解了整个Session管理的概念,我在此还是决定赘述的整个流程。如果你已经对于Session概念非常清晰的话,可以跳过本节不影响对于后文的理解。
HTTP协议在设计的时候是无状态的。这是一个很关键的概念,意味着服务器在处理请求的时候,并不关注这个请求是谁发来的。这对于以提供内容为核心的Web1.0,例如门户网站,非常适合。然而对于以应用服务为核心的Web 2.0而言,服务器端必须有能力从请求中提取出请求者的身份信息,以在请求者不用反复输入身份的情况下,提供连续的服务。
实现请求身份验证的方式很多,其中一种广泛接受的方式是使用服务器端产生的Session ID结合浏览器的Cookie实现对Session的管理,一般来说包括以下4个步骤:
- 服务器端的产生Session ID
- 服务器端和客户端存储Session ID
- 从HTTP Header中提取Session ID
- 根据Session ID从服务器端的Hash中获取请求者身份信息
上图是一个使用Redis Cluster来实现对Session管理的流程,但本质上除了redisMatrix.get和redisMatrix.set以外,和一般的Session管理流程是一致的。
简单来说,一个请求到达的时候,服务器会先判断是否带有Session信息。如果有,则根据Session ID去数据库中查找是否具有对应的用户身份信息。此处可能会出现Session失效、非法的Session信息等可能性,那么服务器视同无Ssession信息的情况,重新的产生一个随机的字符串,并且在Http返回头中写入新的Session ID信息。另一者,如果服务器成功获取了用户的身份信息则以该身份为请求者提供服务。
使用Express和Redis对Session管理的实现
Redis是一个非常适合用于Session管理的数据库。第一,它的结构简单,key-value的形式非常符合SessionID-UserID的存储;第二,读写速度非常快;第三,自身支持数据自动过期和清除;第四,语法、部署非常简单。基于以上原因,很多Session管理都是基于Redis实现的。
Session的安全问题
SessionId就如同请求者的身份证,一旦被攻击者恶意获得,攻击者便可以伪装成请求者对服务器发起请求,也就是我们经常听到的会话劫持(Session/Cookie Hijack)
关于会话劫持的原理推荐大家去看这篇文章
基于上述实现方法的Session管理,我认为基本上可以排除
- 暴力破解SessionId
- 恶意植入固定SessionId
两种可能,因为uid的库基本上可以保证SessionId的随机性;而传递SessionId则依赖HTTP请求头中的Cookie信息而非URL,同时在用户登录立刻更换SessionId。
唯一的可能性来源于Session的监听,而对于这种攻击有效的两种防止办法是:
- Https
很多网站仅仅在Login的阶段使用Https防止用户的用户名、密码信息被监听者获取,但是随后的SessionId同样有可能被监听者获取而伪造登录者的身份信息。因此更加推荐的方式是所有的信息传递全部使用Https实现,这样即使监听着截获了信息也无法破解其中的内容。关于如何使用NodeJS建立一个HTTPS的server可以参考《HTTPS的原理和NodeJS的实现》 这篇文章 - httpOnly
Express在options中提供了httpOnly的属性,此属性默认值为true,这个属性保证了Cookie的信息不能够通过JavaScript脚本获取。
参考: