• 主页
  • 随笔
所有文章 友链 关于我

  • 主页
  • 随笔

网络安全-数字证书与PKI

2019-11-17

1. 公钥基础设施简介

PKI(Public Key Infrustructure)又称为公钥基础设施,是一种遵循既定标准的密钥管理平台,它能够为所有网络应用提供加密和数字签名等密码服务及所必需的密钥和证书管理体系。

完整的PKI系统必须具有权威认证机关(CA)、注册中心(RA)数字证书存储库LDAP、密钥备份及恢复系统(KMC)、证书作废系统(CRL)、应用接口(RADS)等基本构成部分,构建PKI也将围绕着这五大系统来着手构建。

PKI是提供公钥加密和数字签名服务的系统或平台,目的是为了管理密钥和证书。一个机构通过采用PKI框架管理密钥和证书可以建立一个安全的网络环境。PKI主要包括四个部分:

(1)X.509格式的证书(X.509V3)和证书废止列表CRL(X.509 V2);

(2)CA/RA操作协议;

(3)CA管理协议;

(4)CA政策制定。

一个典型、完整、有效的PKI应用系统至少应具有以下部分:

(1)CA认证中心CA是PKI的核心,CA负责管理PKI结构下的所有用户(包括各种应用程序)的证书,把用户的公钥和用户的其它信息捆绑在一起,在网上验证用户的身份,CA还要负责用户证书的黑名单登记和黑名单发布,下面有CA的详细描述。

(2)X.500目录服务器X.500目录服务器用于发布用户的证书和黑名单信息,用户可通过标准的LDAP协议查询自己或其他人的证书和下载黑名单信息。

(3)具有高强度密码算法(SSL)的安全WWW服务器出口到中国的WWW服务器,如微软的IIS、Netscape的WWW服务

PKI是利用公钥技术实现电子商务安全的一种体系,是一种基础设施,网络通讯、网上交易是利用它来保证安全的。从某种意义上讲,PKI包含了安全认证系统,即安全认证系统一CA/RA系统是PKI不可缺的组成部分。

认证中心(CA),是电子商务体系中的核心环节,是电子交易中信赖的基础。它通过自身的注册审核体系,检查核实进行证书申请的用户身份和各项相关信息,使网上交易的用户属性客观真实性与证书的真实性一致。认证中心作为权威的、可信赖的、公正的第三方机构,专门负责发放并管理所有参与网上交易的实体所需的数字证书。

RA (Registration Authority),数字证书注册审批机构。RA系统是CA的证书发放、管理的延伸。它负责证书申请者的信息录入、审核以及证书发放等工作;同时,对发放的证书完成相应的管理功能。发放的数字证书可以存放于IC卡、硬盘或软盘等介质中。RA系统是整个CA中心得以正常运营不可缺少的一部分

密钥管理系统是与电子身份认证中心相对应的密钥管理及服务机构。密钥管理中心KMC的目的在于既保证通信的保密性与私有性,又使得有关国家机构在授权条件下可接入特定的通信过程并破译密文,以保障国家的安全与有关法律的实施。密钥管理中心本身并不知道用于通信信息加密的密钥,但它提供了一种手段,可以恢复该密钥。 密钥管理的目标是安全地实施和运用这些密钥管理服务功能。

2. 数字证书

数字证书,是经过权威认证机构CA签名用于为公钥信息作身份认证声明的电子文件。类似于日常生活中的身份证,它是以数字签名的形式声明,由数字证书认证机构来进行颁发,颁发流程一般为:

(1)用户到RA中心申请证书。

(2)USBKEY生成签名密钥对,产生CSR,将CSR上传到RA。

(3)RA向CA提交用户信息及CSR。

(4)CA向KMC请求加密密钥,同时提交用户的签名公钥。

(5)KMC生成加密密钥对,对加密私钥用签名公钥加密。

(6)KMC将经过加密的加密私钥和加密公钥发给CA。

(7)CA对用户信息及签名和加密公钥签名,生成证书。

(8)CA发布证书。

(9)RA下载证书和经过签名公钥加密的私钥。

(10)用户下载和安装证书和经过签名公钥加密的私钥。

证书主要的文件类型和协议有: PEM、DER、PFX、JKS、KDB、CER、KEY、CSR、CRT、CRL 、OCSP、SCEP等。它是以X.509标准格式将证书信息规范的存储到一系列可解析的字段当中,证书包含的内容包括版本、序列号、签名算法、颁发者、有效期、主题、公钥、私钥用法、CRL分发点、基本限制等等,见下表。

数字证书包含的信息:

名称 释义
版本号 标识证书的版本(V1 V2 V3)
序列号 证书的唯一标识符
签名 签名算法标识符
颁发者 证书颁发者的可识别名
有效期 证书有效期的时间段
主体 证书拥有者的可识别名
主体公钥信息 主体的公钥
颁发者唯一标识符 证书颁发者的唯一标识符 V2 V3
主体唯一标识符 证书拥有者的唯一标识符 V2 V3
扩展 可选的标准和专用的扩展 V2 V3

数字证书的有效性验证主要从三个方面:

1,数字证书有效期验证

就是说证书的使用时间要在起始时间和结束时间之内。通过解析证书很容易得到证书的有效期

2,根证书验证

先来理解一下什么是根证书?

普通的证书一般包括三部分:用户信息,用户公钥,以及CA签名

那么我们要验证这张证书就需要验证CA签名的真伪。那么就需要CA公钥。而CA公钥存在于另外一张证书(称这张证书是对普通证书签名的证书)中。因此我们又需要验证这另外一张证书的真伪。因此又需要验证另另外证书(称这张证书是对另外一张证书签名的证书)的真伪。依次往下回溯,就得到一条证书链。那么这张证书链从哪里结束呢?就是在根证书结束(即验证到根证书结束)。根证书是个很特别的证书,它是CA中心自己给自己签名的证书(即这张证书是用CA公钥对这张证书进行签名)。信任这张证书,就代表信任这张证书下的证书链。

所有用户在使用自己的证书之前必须先下载根证书。

所谓根证书验证就是:用根证书公钥来验证该证书的颁发者签名。所以首先必须要有根证书,并且根证书必须在受信任的证书列表(即信任域)。

3,CRL验证

CRL是经过CA签名的证书作废列表,用于证书冻结和撤销。一般来说证书中有CRL地址,供HTTP或者LDAP方式访问,通过解析可得到CRL地址,然后下载CRL进行验证。

并且证书中有CRL生效日期以及下次更新的日期,因此CRL是自动更新的,因此会有延迟性。

于是呢,还有另外一种方式OSCP证书状态在线查询,可以即时的查询证书状态。

两种证书状态查询方式的比较:

展开全文 >>

网络安全-哈希算法和数字签名

2019-11-17

1. 单向散列算法

  • 单向散列算法也称为哈希算法,它是一种不可逆的单向数学函数,把哈希算法应用于任意长度的一块数据,可以将它映射为一段数据唯一的、不可逆的、定长的、极其紧凑的字符串,这个字符串便称为散列值、哈希值或消息摘要。
  • 常见的单向散列算法有由Ron Rivest设计的可以产生128位散列值的MD5和由NSA设计的可以产生160位的散列值的SHA-1,还有SHA-224、 SHA-256、SHA-384 和SHA-512等。

常见 HASH 算法:

SM3 SHA MD5
Hash值 256bit 160bit 128bit
分组处理长 512bit 512bit 512bit
基本字长 32bit 32bit 32bit
步数 64(3*16) 80(4*20) 64(4*16)
消息长 <2^64bit 2^64bit 不限

MD5: MD5 是由 Ron Rivest 设计的可产生一个 128 位的散列值的散列算法。MD5 设计经过优化以用于 Intel 处理器。这种算法的基本原理已经泄露,这就是为什么它不太受欢迎的原因。

SHA-1:与 DSA 公钥算法相似,安全散列算法 1(SHA-1)也是由 NSA 设计的,并由 NIST 将其收录到 FIPS 中,作为散列数据的标准。它可产生一个 160 位的散列值。SHA-1 是流行的用于创建数字签名的单向散列算法。

HASH 算法主要应用:

**1)文件校验   **
我们比较熟悉的校验算法有奇偶校验和CRC校验,这2种校验并没有抗数据篡改的能力,它们一定程度上能检测并纠正数据传输中的信道误码,但却不能防止对数据的恶意破坏。   
MD5 Hash算法的”数字指纹”特性,使它成为目前应用最广泛的一种文件完整性校验和(Checksum)算法,不少Unix系统有提供计算md5 checksum的命令。   
**2)数字签名  ** 
Hash 算法也是现代密码体系中的一个重要组成部分。由于非对称算法的运算速度较慢,所以在数字签名协议中,单向散列函数扮演了一个重要的角色。对 Hash 值,又称”数字摘要”进行数字签名,在统计上可以认为与对文件本身进行数字签名是等效的。而且这样的协议还有其他的优点。   
**3)鉴权协议   **
如下的鉴权协议又被称作”挑战–认证模式:在传输信道是可被侦听,但不可被篡改的情况下,这是一种简单而安全的方法。

2. 数字签名

  • 数字签名,顾名思义,就像在办公中用笔对纸质文件进行签名,它是在计算机上对数据文件进行签名,用于鉴别数字信息,验证数据发送方的身份,保证数据的完整性和不可否认性。
  • 它结合使用了非对称加密技术与哈希算法来实现,它包含两种互补的运算:一个用于数字签名的签署,另一个用于数字签名的验证。签名和验签的过程一般由启用 PKI 的应用程序完成,首先由数据的发送方用哈希算法生成数据的哈希值,然后用自己的私钥将这个哈希值加密形成数字签名,将它与数据一起加密发送给接收方;接收方进行解密获得数据和数字签名后,用发送方的公钥对数字签名进行解密,如果成功解密,即证明了发送方的身份;对数字签名解密得到数据的哈希值之后,数据的接收方对数据用相同的哈希算法生成另一个哈希值,然后将这两个哈希值进行比较,如果两者相同,则证明这块数据极有可能在传输期间没有被篡改。.

数字签名签署和验证数据的步骤如图所示:

PKCS1 和 PKCS7 标准格式的签名:

1. PKCS1签名:即裸签名,签名值中只有签名信息。

2. PKCS7签名:签名中可以带有其他的附加信息,例如签名证书信息、签名原文信息、时间戳信息等。

PKCS7 的 attached 和 detached 方式的数字签名:

1. attached 方式是将签名内容和原文放在一起,按 PKCS7 的格式打包。PKCS7的结构中有一段可以放明文,但明文必需进行ASN.1编码。在进行数字签名验证的同时,提取明文。这里的明文实际上是真正内容的摘要。

2. detached 方式打包的 PKCS7格式包中不包含明文信息。因此在验证的时候,还需要传递明文才能验证成功。同理,这里的明文实际上是真正内容的摘要。

展开全文 >>

网络安全-非对称加密

2019-11-17

公开密钥密码算法也叫双密钥密码算法或非对称密钥密码算法,它的加密密钥与解密密钥不同。这种算法需要形成一个在数学上是相关联的密钥对(一个公钥和一个私钥),使用其中一个密钥加密数据,用另一个密钥来解密。公钥可以公开传递,不需要安全信道,但与之对应的私钥必须保密。在图1中,发件人用收件人的公钥加密了一段数据,只有收件人用自己的私钥才能解密。

图1 公钥加密要求使用一个公钥和一个私钥

与对称密钥密码体制一样,非对称密钥密码体制也有很多种加密算法。常用的非对称加密算法包括RSA、Elgamal(离散对数)、ECC(椭圆曲线)等。它没有了对称加密算法密钥分配之类的问题,不必再保持密钥传递信道的保密性,但它的加密速度相对较低。实际上在通信时,通常使用对称加密算法来加密大量数据,仅在传递对称密钥的时刻才使用非对称加密算法。这样将非对称加密算法与对称加密算法结合使用,可以优势互补、优化性能。同时,非对称密钥算法还可以用来签名和防抵赖等。

RSA公钥加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的。RSA取名来自开发他们三者的名字。RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

RSA 加解密算法详解:

(1)选择一对不同的、足够大的素数p,q。 p=3 q=11

(2)计算n=pq。 n=33

(3)计算∮(n)=(p-1)(q-1),对p, q保密。 ∮(n)=20   

(4)找一个与∮(n)互质的数e,且1<e< ∮(n)。 e=3

(5)计算d,使得de≡1 mod ∮(n)。 d=7

(6)公钥KU=(e,n),私钥KR=(d,n)。公钥{3,33} 私钥{7,33}

(7)加密时,先将明文变换成0至n-1的一个整数M。若明文较长,可先分割成适当的组,然后再进行交换。设密文为C,则加密过程为:C ≡ M ͤ (mod n)。        

(8)解密过程为: M ≡ C ͩ(mod n)。

Ø 在理论上,rsa 的安全性取决于模n分解的困难性,但数学上至今还未证明分解模就是攻击rsa的最佳办法。

Ø 人们完全可以设想有另外的途径破译rsa,如求解密指数d或找到(p-1)(q-1)等。但这些途径都不比分解n来的容易。

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
/* 
* 用jdk实现RSA:
* */
public static void jdkRSA(String src){

//1.初始化秘钥
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(512);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rSAPublicKey = (RSAPublicKey)keyPair.getPublic();
RSAPrivateKey private1 = (RSAPrivateKey)keyPair.getPrivate();
System.out.println("puk :"+Base64.encodeBase64String(rSAPublicKey.getEncoded()));
System.out.println("pvk :"+Base64.encodeBase64String(private1.getEncoded()));

//2.私钥,公钥--加密
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(private1.getEncoded());
KeyFactory instance = KeyFactory.getInstance("RSA");
KeyFactory keyFactory = instance;
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(src.getBytes());
System.out.println("私钥加密,公钥解密--加密:"+Base64.encodeBase64String(result));


//3.私钥加密,公钥解密---解密
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rSAPublicKey.getEncoded());
keyFactory=KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
result=cipher.doFinal(result);
System.out.println("私钥加密,公钥解密---解密"+new String(result));

//4.公钥加密,私密解密--加密
x509EncodedKeySpec =new X509EncodedKeySpec(rSAPublicKey.getEncoded());
keyFactory=KeyFactory.getInstance("RSA");
publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
result=cipher.doFinal(src.getBytes());
System.out.println("公钥加密,私密解密--加密"+Base64.encodeBase64String(result));

//5公钥加密,私钥解密--解密
pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(private1.getEncoded());
keyFactory=KeyFactory.getInstance("RSA");
privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
result=cipher.doFinal(result);
System.out.println("公钥加密,私钥解密--解密"+ new String(result));

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

ECC(Ellipse Curve Cryptography)椭圆曲线加密算法是1985年由基于椭圆曲线理论的公钥加密技术开发的。与传统的基于大质数因子分解困难性的加密方法不同,ECC通过椭圆曲线方程式的性质产生密钥。ECC 160bit算法等级,相当于RSA 1024bit密钥提供的保密强度,210bit与RSA2048算法强度相当,计算量则更小,处理速度更快,存储空间和传输带宽占用较少。

ECC 加解密算法详解:

1 ) ECDSA 签名

• 1、选择一条椭圆曲线Ep(a,b),和基点G;

• 2、选择私有密钥k(k<n,n为G的阶),利用基点G计算公开密钥K=kG;

• 3、产生一个随机整数r(r<n),计算点R=rG;

• 4、将原数据和点R的坐标值x,y作为参数,计算SHA1做为hash,即Hash=SHA1(原数据,x,y);

• 5、计算s≡r - Hash * k (mod n)

• 6、r和s做为签名值,如果r和s其中一个为0,重新从第3步开始执行

2) ECDSA 验证

• 1、接受方在收到消息(m)和签名值(r,s)后,进行以下运算

• 2、计算:sG+H(m)P=(x1,y1), r1≡ x1 mod p。

• 3、验证等式:r1 ≡ r mod p。

• 4、如果等式成立,接受签名,否则签名无效。

展开全文 >>

网络安全-对称加密

2019-11-17

对称密钥密码算法,也叫做单钥密码算法或私钥密码算法,发送方和接收方共同拥有相同的密钥,发送方使用这个密钥将明文数据加密成密文,然后发送给接收方,接收方收到密文后使用这个密钥将密文解密成明文读取。因为这个密钥既用来进行加密数据,也用来进行解密数据,所以叫做对称密钥,它是一种加密大量数据的加密方法。

对称密钥密码体制有很多种加密算法,常用的对称加密算法包括DES、3DES、AES、RC4、SM1(国产)、SM4(国产)等。这类算法的长处是加密速度快,便于硬件实现和大规模生产,但由于对称密钥算法的加密密钥和解密密钥时是相同的单个密钥,以是这类加密算法需要保障密钥安全。所以,在使用对称密钥算法加密通信前,必要有安全信道来传递密钥。

DES是一种分组数据加密技术(先将数据分成固定长度的小数据块,之后进行加密),速度较快,适用于大量数据加密,DES是应用最广泛的对称密码算法(由于计算能力的快速进展,DES已不在被认为是安全的);

DES 加解密算法详解:

1 ) In Action

• 有明文M(64位) = 0123456789ABCDEF,即 M(64位) = 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

• L(32位) = 0000 0001 0010 0011 0100 0101 0110 0111
R(32位) = 1000 1001 1010 1011 1100 1101 1110 1111

• 有密钥K(64位) = 133457799BBCDFF1,即 K(64位) = 00010011 00110100 01010111 01111001 10011011 10111100 11011111 11110001

• 其中末尾红色标注为奇偶校验位,即实际密钥为56位。

2 )第一步:生成 16 个子钥 (48 位 )

• 对 K 使用 PC-1(8×7)
57 49 41 33 25 17 9
 1 58 50 42 34 26 18
 10 2 59 51 43 35 27
 19 11 3 60 52 44 36
 63 55 47 39 31 23 15
 7 62 54 46 38 30 22
 14 6 61 53 45 37 29
 21 13 5 28 20 12 4

• 从而,由 K(64 位 ) = 00010011 00110100 01010111 01111001 10011011 10111100 11011111 11110001

• 得到 K+(56位) = 1111000 0110011 0010101 0101111 0101010 1011001 1001111 0001111

• 进而, C0(28位) = 1111000 0110011 0010101 0101111
D0(28位) = 0101010 1011001 1001111 0001111

• C1和D1分别为C0和D0左移1位。… C3和D3分别为C2和D2左移2位 …

• 从而得到** ** **C1D1 ~ C16D16 :

• C1 = 1110000110011001010101011111
**D1** = 1010101011001100111100011110

• C2 = 1100001100110010101010111111
**D2** = 0101010110011001111000111101

• C3 = 0000110011001010101011111111
**D3** = 0101011001100111100011110101

• C4 = 0011001100101010101111111100
**D4** = 0101100110011110001111010101

• …
…

• C15 = 1111100001100110010101010111
**D15** = 1010101010110011001111000111

• C16 = 1111000011001100101010101111
**D16** = 0101010101100110011110001111

• Kn(48 位 ) = PC-2( CnDn(56 位 ) )

• PC-2(8×6)

• 14 17 11 24 1 5

• 3 28 15 6 21 10

•  23 19 12 4 26 8

•  16 7 27 20 13 2

•  41 52 31 37 47 55

•  30 40 51 45 33 48

•  44 49 39 56 34 53

•  46 42 50 36 29 32

• 最终得到所有16个子钥,每个48位:
*K1*** = 000110 110000 001011 101111 111111 000111 000001 110010
*
K2*** = 011110 011010 111011 011001 110110 111100 100111 100101
*K3*** = 010101 011111 110010 001010 010000 101100 111110 011001
*
K4*** = 011100 101010 110111 010110 110110 110011 010100 011101
*K5*** = 011111 001110 110000 000111 111010 110101 001110 101000
*
K6*** = 011000 111010 010100 111110 010100 000111 101100 101111
*K7*** = 111011 001000 010010 110111 111101 100001 100010 111100
*
K8*** = 111101 111000 101000 111010 110000 010011 101111 111011
*K9*** = 111000 001101 101111 101011 111011 011110 011110 000001
*
K10*** = 101100 011111 001101 000111 101110 100100 011001 001111
*K11*** = 001000 010101 111111 010011 110111 101101 001110 000110
*
K12*** = 011101 010111 000111 110101 100101 000110 011111 101001
*K13*** = 100101 111100 010111 010001 111110 101011 101001 000001
*
K14*** = 010111 110100 001110 110111 111100 101110 011100 111010
*K15*** = 101111 111001 000110 001101 001111 010011 111100 001010
*
K16*** = 110010 110011 110110 001011 000011 100001 011111 110101

3 )第二步:用子钥对 64 位数据加密

• 对明文 M 使用 IP(8×8)

• 58 50 42 34 26 18 10 2

•  60 52 44 36 28 20 12 4

•  62 54 46 38 30 22 14 6

•  64 56 48 40 32 24 16 8

•  57 49 41 33 25 17 9 1

•  59 51 43 35 27 19 11 3

•  61 53 45 37 29 21 13 5

•  63 55 47 39 31 23 15 7

• 由于M(64位) =0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

• 对M运用IP,故有 IP(64位) = 1100 1100 0000 0000 1100 1100 1111 1111 1111 0000 1010 1010 1111 0000 1010 1010

• IP(64位) = L0(32位) + R0(32位)

• 故

• L0 (32位) = 1100 1100 0000 0000 1100 1100 1111 1111
**R0** (32位) = 1111 0000 1010 1010 1111 0000 1010 1010

• 从L0和R0开始,循环16次,得出L1R1到L16R16,依据递推公式:

• Ln = R(n-1)
Rn = L(n-1) + f (R(n-1),Kn)

L1=R0

R1=L0异或F(R0,K1)

• 其中除了Kn为48位,其他变量及函数均为32位。

• 其中+号表示异或XOR运算,函数f 从一个32位的数据块R(n-1)和一个48位子钥Kn得到一个新的32位数据块。

• 先将32位R(n-1)按照下表扩展到48位,在进行异或运算

•     32,   1,   2,   3,  4,   5,   
  4,   5,   6,   7,   8,  9,
  8,   9, 10, 11, 12, 13, 
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25, 
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32,  1,

• 得到48位数,将48位数顺序分成8份,6位一份,作为输入,通过第6位中的1和6作为行数2-5作为列数在S盒中进行置换最后形成32位的*f*** (*R(n-1),Kn***)

• 101000 行10 列0100à4 à1101 à 13

• S[1]:
  14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,  
0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,  
4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,  
15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13,

• S[2]:
    15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10, 
    3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5, 
    0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15, 
    13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9,

• S[3]

• ….

• S[8]

• 到此为止,我们得到了16对32位的数据块,即 L1R1, L2R2, L3R3, …, L16R16

• 最后一对L16R16就是我们需要的。

• 继续对R16L16(64位)运用一次重排列: IP-1(8×8)

• 40 8 48 16 56 24 64 32

• 39 7 47 15 55 23 63 31

•  38 6 46 14 54 22 62 30

•  37 5 45 13 53 21 61 29

•  36 4 44 12 52 20 60 28

•  35 3 43 11 51 19 59 27

•  34 2 42 10 50 18 58 26

•  33 1 41 9 49 17 57 25

• 即在 L16(32位) = 0100 0011 0100 0010 0011 0010 0011 0100
R16(32位) = 0000 1010 0100 1100 1101 1001 1001 0101
R16L16(64位) = 00001010 01001100 11011001 10010101 01000011 01000010 00110010 00110100

• 时,对R16L16运用IP-1,得 IP-1(64位) = 10000101 11101000 00010011 01010100 00001111 00001010 10110100 00000101 = 85E813540F0AB405

• 从而,经过以上步骤,最终从明文 M = 0123456789ABCDEF 得到密文 C = IP-1 = 85E813540F0AB405

• 以上为加密过程,要解密,依次反向计算即可。

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
/*
* 用jdk实现DES:
* */
public static void jdkDES(String src){
try{
// 生成KEY
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
keyGenerator.init(56);
// 产生密钥
SecretKey secretKey = keyGenerator.generateKey();
// 获取密钥
byte[] bytesKey = secretKey.getEncoded();


// KEY转换
DESKeySpec desKeySpec = new DESKeySpec(bytesKey);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
Key convertSecretKey = factory.generateSecret(desKeySpec);


// 加密(加解密方式:..工作模式/填充方式)
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey);
byte[] result = cipher.doFinal(src.getBytes());
System.out.println("jdk des encrypt:" + Hex.encodeHexString(result));

// 解密
cipher.init(Cipher.DECRYPT_MODE, convertSecretKey);
result = cipher.doFinal(result);
System.out.println("jdk des decrypt:" + new String(result));

} catch (Exception e) {
e.printStackTrace();
}
}

3DES是一种基于DES的加密算法,使用3个不同密匙对同一个分组数据块进行3次加密,如此以使得密文强度更高。

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
/*
* 用jdk实现3DES:
* */
public static void jdk3DES(String src) {
try {
// 生成KEY
KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
// 必须长度是:112或168
// keyGenerator.init(168);
keyGenerator.init(new SecureRandom());
// 产生密钥
SecretKey secretKey = keyGenerator.generateKey();
// 获取密钥
byte[] bytesKey = secretKey.getEncoded();


// KEY转换
DESedeKeySpec desKeySpec = new DESedeKeySpec(bytesKey);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
Key convertSecretKey = factory.generateSecret(desKeySpec);


// 加密
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey);
byte[] result = cipher.doFinal(src.getBytes());
System.out.println("jdk 3des encrypt:" + Hex.encodeHexString(result));

// 解密
cipher.init(Cipher.DECRYPT_MODE, convertSecretKey);
result = cipher.doFinal(result);
System.out.println("jdk 3des decrypt:" + new String(result));

} catch (Exception e) {
e.printStackTrace();
}
}

相较于DES和3DES算法而言,AES算法有着更高的速度和资源使用效率,安全级别也较之更高了,被称为下一代加密标准。AES将是未来最主要,最常用的对称密码算法;

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
/*
* 用jdk实现AES:
* */
public static void jdkAES(String src){
try{
// 生成KEY
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// 产生密钥
SecretKey secretKey = keyGenerator.generateKey();
// 获取密钥
byte[] keyBytes = secretKey.getEncoded();


// KEY转换
Key key = new SecretKeySpec(keyBytes, "AES");


// 加密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(src.getBytes());
System.out.println("jdk aes encrypt:" + Hex.encodeHexString(result));

// 解密
cipher.init(Cipher.DECRYPT_MODE, key);
result = cipher.doFinal(result);
System.out.println("jdk aes decrypt:" + new String(result));

} catch (Exception e) {
e.printStackTrace();
}
}

展开全文 >>

Linux网络管理

2019-11-17

1、网络基础

1.1、iso/osi七层模型

名词释义

  • ISO:国际标准化组织

  • OSI:开放系统互联模型

  • 比特:比特位010101..一个0/1一个单位

  • 帧:计算机硬件地址,主要是网卡MAC地址(MAC地址负责局域网通信)

  • 报文:ip地址(IP地址负责外网通信)

  • TPDU:传输协议数据单元

  • SPDU:会话协议数据单元

  • PPDU:表示层协议数据单元

  • APDU:应用协议数据单元

1.2、TCP/IP四层模型

  • TCP:可靠的、面向连接的协议

  • UDP: 不可靠的、面向无连接的协议

1.3、IP地址

  • A类IP:

    1.0.0.0代表网络本身,不能分配
    1.255.255.255代表当前网络的广播地址
    第一个数代表不同的网段,后三个数代表一个网段内不同的主机

  • B类IP:

    前两个数代表不同的网段(191.224和191.254不是同一个网络),后两个数代表一个网段内不同的主机

  • C类IP:

    前三个数代表不同网段,最后一个数代表一个网段内不同的主机

子网掩码的使用:

子网掩码和IP不能单独查看,必须同时查看

标准子网掩码:

  • A类:255.0.0.0

  • B类:255.255.0.0

  • C类:255.255.255.0

1.4、端口作用

netstat命令Windows和Linux都可以使用

1.5、DNS作用

DNS(Domain Name System)是域名系统的缩写,也称作名称解析

Hosts文件的优先级是高于DNS解析的

Hosts是做静态IP和域名对应

DNS服务:

  • 层次性

  • 分布式

域名用“.”进行分割便于分级管理

“.”表示根域名,根域名的服务器只有13台

顶级域:

  • 由域名分配组织ISO决定

组织域:

国家或地区域:

二级域:

  • 企业或个人申请

主机名:

  • 三级域名是申请完二级域名后自己规定的

  • 三级域+二级域+顶级域组成完整域名空间,并且域名全球唯一

域名空间意义:

1) 互联网中的域名是有结构有规划的

2) 域名进行了分级在进行域名和IP地址解析时才能更容易找到

1.6、网关作用

1) 网关在所有内网计算机访问的不是本网段的数据报时使用。

2) 网关负责将内网IP转换为公网IP,公网IP转换为内网IP。

2、Linux网络配置

2.1、Linux配置IP地址

1) ifconfig命令临时配置IP地址

2) setup工具永久配置IP地址

3) 修改网络配置文件

4) 图形界面配置IP地址

2.2、Linux网络配置文件

2.3、虚拟机网络参数配置

3、Linux网络命令

3.1、网络环境查看命令

在一台服务器里,连内网的网卡是不能设置网关的。

nslookup命令用来翻译域名对应哪个IP

3.2、网络测试命令

ICMP是(Internet Control Message Protocol)Internet控制报文协议。

抓包命令

4、远程登录

4.1、SSH协议原理

展开全文 >>

Linux基本命令

2019-11-17

文件和目录命令

ls 命令最基本的形式会显示当前目录下的文件和目录
-F 参数在目录名后加了正斜线(/),以方便用户在输出中分辨它们。类似地,它会在可执行文件(比如上面的my_script文件)的后面加个星号,以便用户找出可在系统上运行的文件

-R 参数是递归选项。它列出了当前目录下包含的子目录中的文件。

-l 参数会产生长列表格式的输出,包含了目录中每个文件的更多相关信息。

ls 命令还支持在命令行中定义过滤器进行简单文本匹配。ls 命令能够识别标准通配符(问号(? )代表一个字符;星号(* )代表零个或多个字符)。例如:
$ ls -l my*
可以使用中括号表示一个字符位置并给出多个可能的选择,例如字母范围[a - i]。
$ ls -l f[a-i]ll
另外,可以使用感叹号(!)将不需要的内容排除在外。

1
2
3
4
5
6
7
$ ls -l f[!a]ll
-rw-rw-r-- 1 christine christine 0 May 21 13:44
fell
-rw-rw-r-- 1 christine christine 0 May 21 13:44
fill
-rw-rw-r-- 1 christine christine 0 May 21 13:44
Full

处理文件命令

touch 命令创建空文件或改变文件的修改时间。这个操作并不需要改变文件的内容。

cp 命令复制文件,需要两个参数——源对象和目标对象。如果目标文件已经存在,cp 命令可能并不会提醒这一点。最好是加上**-i** 选项,强制shell询问是否需要覆盖已有文件。

1
2
$ cp -i test_one  test_two
cp: overwrite 'test_two'? n

如果不回答y ,文件复制将不会继续。
*-R*** 参数在一条命令中递归地复制整个目录的内容。也可以在cp 命令中使用通配符。
在使用命令行输入文件名或目录名时按一下*
制表键***,shell可以将剩下的文件名自动补充完整。

mv 命令可以将文件和目录移动到另一个位置或重新命名。和cp 命令类似,也可以在mv 命令中使用**-i** 参数。这样在命令试图覆盖已有的文件时,你就会得到提示。

rm命令删除文件 。文件一旦删除,就无法再找回。
*-i**命令参数提示你是不是要真的删除该文件。
如果要删除很多文件且不受提示符的打扰,可以用
-f*** 参数强制删除。

处理目录命令

mkdir 命令创建目录
-p 参数可以根据需要创建缺失的父目录,例如:
$ mkdir -p New_Dir/Sub_Dir/Under_Dir

rmdir删除目录命令,默认情况下,rmdir 命令只删除空目录。rmdir 并没有*-i*** 选项来询问是否要删除目录。也可以在整个非空目录上使用rm 命令。*-R*** 参数同样可以递归地删除目录中的文件。

查看文件内容命令

file 命令查看文件类型

cat 命令是显示文本文件中所有数据。
-n 参数会给所有的行加上行号。如果只想给有文本的行加上行号,可以用**-b** 参数。

1
2
3
4
5
6
7
$ cat -n test1
1 hello
2
3 This is a test file.
4
5
6 That we'll use to test the cat command.

-T 参数会用^I 字符组合去替换文中的所有制表符。

more 命令是分页工具。会显示文本文件的内容,但会在显示每页数据之后停下来。

less 命令为more 命令的升级版。它提供了一些极为实用的特性,能够实现在文本文件中前后翻动,而且还有一些高级搜索功能。

tail 命令会显示文件最后几行的内容(默认显示文件的末尾10行)。
-n 参数修改所显示的行数。
**-f** 参数允许你在其他进程使用该文件时查看文件的内容。

head 命令会显示文件开头那些行的内容(默认显示文件前10行的文本).
-n 参数指定想要显示的行数

展开全文 >>

snmp整理笔记

2019-11-17

1. snmp是什么

SNMP是英文”Simple Network Management Protocol“的缩写,中文意思是”简单网络管理协议“。SNMP 是一种简单网络管理协议,它属于 TCP/IP 五层协议中的应用层协议,用于网络管理的协议。 SNMP 主要用于网络设备的管理。由于 SNMP 协议简单可靠 ,受到了众多厂商的欢迎,成为了目前最为广泛的网管协议。

  SNMP协议主要由两大部分构成:SNMP管理站和SNMP代理。SNMP管理站是一个中心节点,负责收集维护各个SNMP元素的信息,并对这些信息进行处理,最后反馈给网络管理员;而SNMP代理是运行在各个被管理的网络节点之上,负责统计该节点的各项信息,并且负责与SNMP管理站交互,接收并执行管理站的命令,上传各种本地的网络信息。

  SNMP管理站和SNMP代理之间是松散耦合。他们之间的通信是通过UDP协议完成的。一般情况下,SNMP管理站通过UDP协议向SNMP代理发送各种命令,当SNMP代理收到命令后,返回SNMP管理站需要的参数。但是当SNMP代理检测到网络元素异常的时候,也可以主动向SNMP管理站发送消息,通告当前异常状况。

SNMP 的基本思想:为不同种类的设备、不同厂家生产的设备、不同型号的设备,定义为一个统一的接口和协议,使得管理员可以是使用统一的外观面对这些需要管理的网络设备进行管理。通过网络,管理员可以管理位于不同物理空间的设备,从而大大提高网络管理的效率,简化网络管理员的工作。

  SNMP的工作方式:管理员需要向设备获取数据,所以SNMP提供了【读】操作;管理员需要向设备执行设置操作,所以SNMP提供了【写】操作;设备需要在重要状况改变的时候,向管理员通报事件的发生,所以SNMP提供了【Trap】操作。

2. snmp应用哪些场景

利用SNMP,一个管理工作站可以远程管理所有支持这种协议的网络设备,包括监视网络状态、修改网络设备配置、接收网络事件警告等。SNMP的基本思想:为不同种类的设备、不同厂家生产的设备、不同型号的设备,定义为一个统一的接口和协议,使得管理员可以使用统一的网管面对这些需要管理的网络设备进行管理。通过网络,管理员可以管理位于不同物理空间的设备,从而大大提高网络管理的效率,简化网络管理员的工作。

3. snmp能做什么

SNMP为管理员提供了一个网管平台(NMS),又称为【管理站】,负责网管命令的发出、数据存储、及数据分析。被监管的设备上运行一个SNMP代理(Agent)),代理实现设备与管理站的SNMP通信。

网络管理员使用SNMP功能可以查询设备信息、修改设备的参数值、监控设备状态、自动发现网络故障、生成报告等。

SNMP协议之所以易于使用,这是因为它对外提供了三种用于控制MIB对象的基本操作命令。它们是:Get、Set 和 Trap。

Get:管理站读取代理者处对象的值。它是SNMP协议中使用率最高的一个命令,因为该命令是从网络设备中获得管理信息的基本方式。

Set:管理站设置代理者处对象的值。它是一个特权命令,因为可以通过它来改动设备的配置或控制设备的运转状态。它可以设置设备的名称,关掉一个端口或清除一个地址解析表中的项等。

Trap: 代理者主动向管理站通报重要事件。它的功能就是在网络管理系统没有明确要求的前提下,由管理代理通知网络管理系统有一些特别的情况或问题 发生了。如果发生意外情况,客户会向服务器的162端口发送一个消息,告知服务器指定的变量值发生了变化。通常由服务器请求而获得的数据由服务器的161 端口接收。Trap 消息可以用来通知管理站线路的故障、连接的终端和恢复、认证失败等消息。管理站可相应的作出处理。

get-next-request操作:从代理进程处提取紧跟当前参数值的下一个参数值。

get-response操作:返回的一个或多个参数值。这个操作是由代理进程发出的。

4. snmp示例Demo

4.1、 Get操作命令:

4.1.1、获得本机的信息

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
package me.gacl.snmp;

import java.io.IOException;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

/**
* <p>ClassName: GetOID<p>
* <p>Description:获得本机的信息 <p>
*/
public class GetOID {

public static void main(String[] args) throws Exception{

try{
//设定CommunityTarget
CommunityTarget myTarget = new CommunityTarget();
//定义远程主机的地址
//Address deviceAdd = GenericAddress.parse("udp:10.20.61.120/161");
//定义本机的地址
Address localAdd = GenericAddress.parse("udp:localhost/161");
//设定远程主机的地址
//myTarget.setAddress(deviceAdd);
//设定本地主机的地址
myTarget.setAddress(localAdd);
//设置snmp共同体
myTarget.setCommunity(new OctetString("public"));
//设置超时重试次数
myTarget.setRetries(2);
//设置超时的时间
myTarget.setTimeout(5*60);
//设置使用的snmp版本
myTarget.setVersion(SnmpConstants.version2c);

//设定采取的协议
TransportMapping transport = new DefaultUdpTransportMapping();//设定传输协议为UDP
//调用TransportMapping中的listen()方法,启动监听进程,接收消息,由于该监听进程是守护进程,最后应调用close()方法来释放该进程
transport.listen();
//创建SNMP对象,用于发送请求PDU
Snmp protocol = new Snmp(transport);
//创建请求pdu,获取mib
PDU request = new PDU();
//调用的add方法绑定要查询的OID
request.add(new VariableBinding(new OID("1.3.6.1.2.1.1.1")));
request.add(new VariableBinding(new OID(new int[] {1,3,6,1,2,1,1,2})));
//调用setType()方法来确定该pdu的类型
request.setType(PDU.GETNEXT);
//调用 send(PDU pdu,Target target)发送pdu,返回一个ResponseEvent对象
ResponseEvent responseEvent = protocol.send(request, myTarget);
//通过ResponseEvent对象来获得SNMP请求的应答pdu,方法:public PDU getResponse()
PDU response=responseEvent.getResponse();
//输出
if(response != null){
System.out.println("request.size()="+request.size());
System.out.println("response.size()="+response.size());
//通过应答pdu获得mib信息(之前绑定的OID的值),方法:VaribleBinding get(int index)
VariableBinding vb1 = response.get(0);
VariableBinding vb2 = response.get(1);
System.out.println(vb1);
System.out.println(vb2);
//调用close()方法释放该进程
transport.close();

/**
* 输出结果:
* request.size()=2
response.size()=2
1.3.6.1.2.1.1.1.0 = Hardware: Intel64 Family 6 Model 60 Stepping 3 AT/AT COMPATIBLE - Software: Windows Version 6.3 (Build 17134 Multiprocessor Free)
1.3.6.1.2.1.1.2.0 = 1.3.6.1.4.1.311.1.1.3.1.1

*/
}

}catch(IOException e){
e.printStackTrace();
}
}
}

运行截图:

image.png

4.1.2、获取远程计算机的名称

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package me.gacl.snmp;

import java.io.IOException;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.ScopedPDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.TransportMapping;
import org.snmp4j.UserTarget;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.AuthMD5;
import org.snmp4j.security.PrivDES;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

public class Snmp4jFirstDemo {

private Snmp snmp = null;
private int version ;

public Snmp4jFirstDemo(int version) {
try {
this.version = version;
TransportMapping transport = new DefaultUdpTransportMapping();
snmp = new Snmp(transport);
if (version == SnmpConstants.version3) {
// 设置安全模式
USM usm = new USM(SecurityProtocols.getInstance(),new OctetString(MPv3.createLocalEngineID()), 0);
SecurityModels.getInstance().addSecurityModel(usm);
}
// 开始监听消息
transport.listen();
} catch (IOException e) {
e.printStackTrace();
}
}

public void sendMessage(Boolean syn, final Boolean bro, PDU pdu, String addr)
throws IOException {
// 生成目标地址对象
Address targetAddress = GenericAddress.parse(addr);
Target target = null;
if (version == SnmpConstants.version3) {
// 添加用户
snmp.getUSM().addUser(new OctetString("MD5DES"),new UsmUser(new OctetString("MD5DES"), AuthMD5.ID,new OctetString("MD5DESUserAuthPassword"),PrivDES.ID, new OctetString("MD5DESUserPrivPassword")));
target = new UserTarget();
// 设置安全级别
((UserTarget) target).setSecurityLevel(SecurityLevel.AUTH_PRIV);
((UserTarget) target).setSecurityName(new OctetString("MD5DES"));
target.setVersion(SnmpConstants.version3);
} else {
target = new CommunityTarget();
if (version == SnmpConstants.version1) {
target.setVersion(SnmpConstants.version1);
((CommunityTarget) target).setCommunity(new OctetString("public"));
} else {
target.setVersion(SnmpConstants.version2c);
((CommunityTarget) target).setCommunity(new OctetString("public"));
}

}
// 目标对象相关设置
target.setAddress(targetAddress);
target.setRetries(5);
target.setTimeout(1000);

if (!syn) {
// 发送报文 并且接受响应
ResponseEvent response = snmp.send(pdu, target);
// 处理响应
System.out.println("Synchronize(同步) message(消息) from(来自) "
+ response.getPeerAddress() + "\r\n"+"request(发送的请求):"
+ response.getRequest() + "\r\n"+"response(返回的响应):"
+ response.getResponse());
/**
* 输出结果:
* Synchronize(同步) message(消息) from(来自) 10.20.61.120/161
request(发送的请求):GET[requestID=680783532, errorStatus=Success(0), errorIndex=0, VBS[1.3.6.1.2.1.1.5.0 = Null]]
response(返回的响应):RESPONSE[requestID=680783532, errorStatus=Success(0), errorIndex=0, VBS[1.3.6.1.2.1.1.5.0 = IE11Win7]]

*/
} else {
// 设置监听对象
ResponseListener listener = new ResponseListener() {

public void onResponse(ResponseEvent event) {
if (bro.equals(false)) {
((Snmp) event.getSource()).cancel(event.getRequest(),this);
}
// 处理响应
PDU request = event.getRequest();
PDU response = event.getResponse();
System.out.println("Asynchronise(异步) message(消息) from(来自) "
+ event.getPeerAddress() + "\r\n"+"request(发送的请求):" + request
+ "\r\n"+"response(返回的响应):" + response);
}

};
// 发送报文
snmp.send(pdu, target, null, listener);
}
}

public static void main(String[] args) {
//Snmp的三个版本号
//int ver3 = SnmpConstants.version3;
int ver2c = SnmpConstants.version2c;
//int ver1 = SnmpConstants.version1;
Snmp4jFirstDemo manager = new Snmp4jFirstDemo(ver2c);
// 构造报文
PDU pdu = new PDU();
//PDU pdu = new ScopedPDU();
// 设置要获取的对象ID,这个OID代表远程计算机的名称
OID oids = new OID("1.3.6.1.2.1.1.5.0");
pdu.add(new VariableBinding(oids));
// 设置报文类型
pdu.setType(PDU.GET);
//((ScopedPDU) pdu).setContextName(new OctetString("priv"));
try {
// 发送消息 其中最后一个是想要发送的目标地址
manager.sendMessage(false, true, pdu, "udp:10.20.61.120/161");//10.20.61.120 Win7虚拟机
} catch (IOException e) {
e.printStackTrace();
}
}
}

运行截图:

image.png

4.2、 Set操作命令:

4.2.1、修改读取的远程计算机的名称

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package me.gacl.snmp;

import java.io.IOException;
import java.util.Vector;

import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

public class SnmpUtil {

private Snmp snmp = null;

private Address targetAddress = null;

public void initComm() throws IOException {

// 设置Agent方的IP和端口
targetAddress = GenericAddress.parse("udp:10.20.61.120/161");

TransportMapping transport = new DefaultUdpTransportMapping();

snmp = new Snmp(transport);

transport.listen();

}


public ResponseEvent sendPDU(PDU pdu) throws IOException {

// 设置 target
CommunityTarget target = new CommunityTarget();

target.setCommunity(new OctetString("public"));

target.setAddress(targetAddress);

// 通信不成功时的重试次数
target.setRetries(2);

// 超时时间
target.setTimeout(1500);

target.setVersion(SnmpConstants.version1);

// 向Agent发送PDU,并返回Response
return snmp.send(pdu, target);

}



public void setPDU() throws IOException {

// set PDU
PDU pdu = new PDU();

pdu.add(new VariableBinding(new OID(new int[] { 1, 3, 6, 1, 2, 1, 1, 5, 0 }), new OctetString("SNMPTEST")));

pdu.setType(PDU.SET);

sendPDU(pdu);

}



public void getPDU() throws IOException {

// get PDU
PDU pdu = new PDU();

pdu.add(new VariableBinding(new OID(new int[] { 1, 3, 6, 1, 2, 1, 1, 5, 0 })));

pdu.setType(PDU.GET);

readResponse(sendPDU(pdu));

}



public void readResponse(ResponseEvent respEvnt) {

// 解析Response
if (respEvnt != null && respEvnt.getResponse() != null) {

Vector<VariableBinding> recVBs = (Vector<VariableBinding>) respEvnt.getResponse().getVariableBindings();

for (int i = 0; i < recVBs.size(); i++) {

VariableBinding recVB = recVBs.elementAt(i);

System.out.println(recVB.getOid() + " : " + recVB.getVariable());

}

}

}


public static void main(String[] args) {

try {

SnmpUtil util = new SnmpUtil();

util.initComm();

util.setPDU();

util.getPDU();

} catch (IOException e) {

e.printStackTrace();

}

}

}

运行截图:

image.png

4.3、 Trap操作命令:

4.3.1、发送Trap

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
package me.gacl.snmp;

import java.io.IOException;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

public class TrapUtil {

private Snmp snmp = null;

private Address targetAddress = null;

private TransportMapping transport = null;

public void initComm() throws IOException {

// 设置Agent方的IP和端口
targetAddress = GenericAddress.parse("udp:10.20.61.109/162");
// 设置send trap的IP和端口
transport = new DefaultUdpTransportMapping(new UdpAddress("10.20.61.120/161"));
snmp = new Snmp(transport);

transport.listen();

}


public ResponseEvent sendPDU(PDU pdu) throws IOException {

// 设置 target
CommunityTarget target = new CommunityTarget();

target.setCommunity(new OctetString("public"));

target.setAddress(targetAddress);

// 通信不成功时的重试次数
target.setRetries(2);

// 超时时间
target.setTimeout(1500);

target.setVersion(SnmpConstants.version2c);

// 向Agent发送PDU,并返回Response
return snmp.send(pdu, target);

}



public void setTrap() throws IOException {

// 构造Trap PDU

PDU pdu = new PDU();

pdu.add(new VariableBinding(new OID(".1.3.6.1.2.3377.10.1.1.1.1"),

new OctetString("SnmpTrap")));

pdu.setType(PDU.TRAP);

sendPDU(pdu);

System.out.println("Trap sent successfully.");

}


public static void main(String[] args) {

try {

TrapUtil util = new TrapUtil();

util.initComm();

util.setTrap();

} catch (IOException e) {

e.printStackTrace();
}
}
}

运行截图:

image.png

4.3.2、接收Trap

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
package me.gacl.snmp;

import java.io.IOException;

import org.snmp4j.CommandResponder;
import org.snmp4j.CommandResponderEvent;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.transport.DefaultUdpTransportMapping;

public class Snmp4jTrapDemo {

private Snmp snmp = null;
private Address targetAddress = null;
private TransportMapping transport = null;

public void initComm() throws IOException {

// 设置Agent方的IP和端口
targetAddress = GenericAddress.parse("udp:10.20.61.120/161");
// 设置接收trap的IP和端口
transport = new DefaultUdpTransportMapping(new UdpAddress("10.20.61.109/162"));
snmp = new Snmp(transport);

CommandResponder trapRec = new CommandResponder() {

public synchronized void processPdu(CommandResponderEvent e) {

// 接收trap
PDU command = e.getPDU();
if (command != null) {
System.out.println(command.toString());
}
}
};

snmp.addCommandResponder(trapRec);
transport.listen();
}


public synchronized void listen() {

System.out.println("Waiting for traps..");

try {
this.wait();//Wait for traps to come in
} catch (InterruptedException ex) {
System.out.println("Interrupted while waiting for traps: " + ex);
System.exit(-1);
}

}



public static void main(String[] args) {

try {
Snmp4jTrapDemo traputil = new Snmp4jTrapDemo();
traputil.initComm();
traputil.listen();
} catch (IOException e) {
e.printStackTrace();
}

}
}

运行截图:

image.png

展开全文 >>

Spring知识点笔记

2019-11-17

1.1.1 Spring简介

Spring是一个开源的控制反转(IoC)和面向且切面(AOP)的容器框架。

IOC控制反转:应用本身不负责以来对象的创建和维护,以来对象的创建及维护由外部容器负责。这样控制权就转移到了外部容器。

Dependency Injection依赖注入:在运行期,由外部容器动态地将以来对象注入到组件中。

事务控制全部交给spring处理,不用手工编写事务的创建和提交


1.1.2 Spring配置和搭建

Eclipse下的配置

下载了spring-framework-2.5.6包,把dist文件夹下的spring.jar添加到工程,还要添加一个jar是common-logging的,在hibernate学习的时候下载过了,也添加进去。

在src目录下新建beans.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

</beans>

 初始化spring容器

1
ApplicationContext context = new ClassPathXmlApplicationContext( new String[] {"services.xml", "daos.xml"});//可以通过数组传递多个配置文件。

下面使用JUnit4进行测试,new->JUnit Test

image.png

(不要勾选setUpBeforeClass)

SprintTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SprintTest.java

package junit.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

@Test
public void instanceSpring(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

}

}

运行以后测试成功

下面写一个bean

PersonService.java 放在src中service包下

1
2
3
4
5
6
7
package service.impl; 

public interface PersonService { 

public abstract void save();

} 

PersonServiceBean.java 放在src中service.impl包下

1
2
3
4
5
6
7
8
9
10
11
12
13
 package service;  

import service.impl.PersonService;

public class PersonServiceBean implements PersonService {
/* (non-Javadoc)
* @see service.impl.PersonService#save()
*/
public void save(){
System.out.println("save方法");
}
}

下面配置beans.xml

在没联网的情况下需要添加提示文件才会出现关键字提示。

windows->perferences->XML->XML Catalog

add

1
2
3
4
5
Location:在spring包下找到dist/resources/spring-beans-2.5.xsd

Key Type:Namespace Name

Key:http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

完成添加。

在beans标签中间添加

1
<bean id="personService" class="service.PersonServiceBean"></bean>

使用bean标签的时候,id和name的区别:id不可以包含特殊字符,name可以。

 SpringTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 package junit.test;  

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import service.impl.PersonService;

public class SpringTest {
@Test
public void instanceSpring(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService)context.getBean("personService");
personService.save();
}

}

运行成功


1.1.3 Spring管理bean的原理

简单的一套管理bean的代码,就是一个模拟的ApplicationContext类。

主要方法有两个

1
2
this.readXML(filename);//读取XML配置文件
this.instanceBeans();//实例化bean

主要成员变量

1
2
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();//保存XML中的bean信息,包括id和class,组成BeanDefinition类 
private Map<String, Object> sigletons = new HashMap<String, Object>();//保存实例化以后的bean

具体的代码实现不细化研究了,大概流程:

首先使用dom4j读取XML中的bean标签,把id和class保存在beanDifines数组中,然后在instanceBeans()方法中遍历beanDifines数组,取出类名,利用发射技术进行实例化,如Class.forName(beanDefinition.getClassName()).newInstance());并把实例化对象保存在sigletons的哈希表中。完成实例化

接下来就是简单的get方法来取出对应的bean供其他类调用。

可以发现bean的实例化就是在创建ApplicationContext对象的时候,通过构造方法进行实例化的。

代码:

ItcastClassPathApplicationContext.java

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
package junit.test;  

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

public class ItcastClassPathXMLApplicationContext {
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
private Map<String, Object> sigletons = new HashMap<String, Object>();

public ItcastClassPathXMLApplicationContext(String filename){
this.readXML(filename);
this.instanceBeans();
}
/**
* 完成bean的实例化
*/
private void instanceBeans() {
for(BeanDefinition beanDefinition : beanDefines){
try {
if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}

}
/**
* 读取xml配置文件
* @param filename
*/
private void readXML(String filename) {
SAXReader saxReader = new SAXReader();
Document document=null;
try{
URL xmlpath = this.getClass().getClassLoader().getResource(filename);
document = saxReader.read(xmlpath);
Map<String,String> nsMap = new HashMap<String,String>();
nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间
XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径
xsub.setNamespaceURIs(nsMap);//设置命名空间
List<Element> beans = xsub.selectNodes(document);//获取文档下所有bean节点
for(Element element: beans){
String id = element.attributeValue("id");//获取id属性值
String clazz = element.attributeValue("class"); //获取class属性值
BeanDefinition beanDefine = new BeanDefinition(id, clazz);
beanDefines.add(beanDefine);
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 获取bean实例
* @param beanName
* @return
*/
public Object getBean(String beanName){
return this.sigletons.get(beanName);
}
}

BeanDefinition.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package junit.test;  

public class BeanDefinition {
private String id;
private String className;

public BeanDefinition(String id, String className) {
this.id = id;
this.className = className;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}

}

测试代码

1
2
3
4
5
ItcastClassPathXMLApplicationContext ctx = new ItcastClassPathXMLApplicationContext("beans.xml");  

PersonService personService = (PersonService)ctx.getBean("personService");

personService.save();

1.1.4 三种实例化bean的方式

1.使用类构造器实例化

1
<bean id="personService" class="service.impl.PersonServiceBean"></bean>

2.使用静态工厂方法实例化

beans.xml

1
<bean id="personService2" class="service.impl.PersonServiceBeanFactory" factory-method="createPersonServiceBean"></bean>  

PersonServiceBeanFactory.java

1
2
3
4
5
1. public static PersonServiceBean createPersonServiceBean(){  

2.     return new PersonServiceBean();  

3. } 

3.使用实例工厂方法实例化

beans.xml

1
2
3
<bean id="personServiceFactory" class="service.impl.PersonServiceBeanFactory"></bean>

<bean id="personService3" factory-bean="personServiceFactory" factory-method="createPersonServiceBean2"></bean>

PersonServiceBeanFactory.java

1
2
3
4
5
1. public PersonServiceBean createPersonServiceBean2(){  

2.         return new PersonServiceBean();  

3.     }  

三种方式测试成功

绝大部分都使用第一种实例化方法


1.1.5 spring管理bean的作用域

来看一段代码

1
2
3
4
5
6
7
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

PersonService personService1 = (PersonService)context.getBean("personService");

PersonService personService2 = (PersonService)context.getBean("personService");

System.out.println(personService1==personService2);

输出结果为true,说明getBean方法获取的是单实例

那么就来看bean的作用域

bean标签的scope属性来设置

singleton

默认情况下的单例模式,每次调用getBean方法获取的都是同一个bean对象

默认情况喜爱会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init属性来延迟初始化bean,这时候,只有第一次获取bean才会初始化bean。

1
<bean id="xxx" class="xxx" lazy_init="true"/>

如果对所有bean都应用延迟初始化

1
<beans default-lazy-init="true"...>

prototype

每次从容器获取bean都是一个新的对象

request

session

global session


1.1.6 spring管理bean的生命周期

从前面可以看到,

singleton模式下bean的实例化的时机是在ApplicationContext实例化的时候进行实例化,然而设置lazy-init=true的情况下在getBean方法调用的时候进行实例化。

prototype模式下bean的实例化时机是在getBean方法调用的时候进行实例化。

如果我们需要在bean初始化的时候,打开数据库资源等连接,那么指定一个初始化方法,这样在bean实例化的时候就会自动初始化。同样的指定一个销毁方法。

beans.xml

1
<bean id="personService" class="service.impl.PersonServiceBean" init-method="init" destroy-method="destroy"></bean>  

PersonServiceBean.java

1
2
3
4
5
6
7
8
9
10
11
public void init(){

System.out.println("inti初始化方法");  

}

public void destroy(){

System.out.println("destroy销毁方法");  

}

SprintTest.java

1
1.AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");  
  1. context.close();  

测试结果输出

inti初始化方法destroy销毁方法

lazy-init应该是一个优化spring很好的东西,就像hibernate里的懒加载,有些bean可能一直没有用到过,根本没必要初始化,但是视频里说尽量使用lazy-init=false,为了发现所有bean可能出现的错误,难道测试的时候要这么做?另外JUnit的测试也不是很明白。。。感觉和一般的差不多啊


1.1.7 剖析Spring依赖注入的原理

通过set方法注入

1
2
3
4
5
6
7
8
9
10
11
12
13
package dao.impl;  

import dao.PersonDao;

public class PersonDaoBean implements PersonDao {
/* (non-Javadoc)
* @see dao.impl.PersonDao#add()
*/
public void add(){
System.out.println(this.getClass().getName()+" add方法");
}
}

PersonServiceBean.java

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
•  package service.impl;  
•
• import dao.PersonDao;
• import service.PersonService;
•
•
• public class PersonServiceBean implements PersonService {
• private PersonDao personDao;
•
• public PersonDao getPersonDao() {
• return personDao;
• }
•
• public void setPersonDao(PersonDao personDao) {
• this.personDao = personDao;
• }
•
• public void save(){
• personDao.add();
• }
•
• public void init(){
• System.out.println("inti初始化方法");
• }
•
• public void destroy(){
• System.out.println("destroy销毁方法");
• }
• }

beans.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
•  <?xml version="1.0" encoding="UTF-8"?>  
• <beans xmlns="http://www.springframework.org/schema/beans"
• xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
• xsi:schemaLocation="http://www.springframework.org/schema/beans
• http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
•
• <bean id="personDao" class="dao.impl.PersonDaoBean"></bean>
• <bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">
• <property name="personDao" ref="personDao"></property>
• </bean>
•
• </beans>

SpringTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
•  package junit.test;  
•
• import org.junit.Test;
• import org.springframework.context.support.AbstractApplicationContext;
• import org.springframework.context.support.ClassPathXmlApplicationContext;
•
• import service.PersonService;
•
• public class SpringTest {
• @Test
• public void instanceSpring(){
• AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
• PersonService personService = (PersonService)context.getBean("personService");
• personService.save();
• context.close();
•
• }
•
• }

运行结果:

dao.impl.PersonDaoBean add方法

还有一种内部bean的注入方式

1
2
3
4
5
6
7
8
9
·<bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">  

· <property name="personDao">

· <bean class="dao.impl.PersonDaoBean"></bean>

· </property>

· </bean>

即在property标签内部添加bean标签,在bean标签里写出类名即可。效果一样。

实际上spring的操作流程是吧bean中的property也保存在一个数组中,初始化的时候遍历数组,找到需要注入的bean,利用反射技术调用set方法,参数为配置文件中的ref应用的bean,就完成了注入。


1.1.8 Spring装配基本属性的原理

上一篇的注入都是对象的注入,这篇来分析基本属性的注入。

以String类型为例

PersonServiceBean.java中加入

1
private String name;  

beans.xml

1
2
3
4
5
6
7
8
9
10
11
·<bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">  

· <property name="personDao">

· <bean class="dao.impl.PersonDaoBean"></bean>

· </property>

· <property name="name" value="pf"></property>

· </bean>

其他基本类型类似,均采用value属性方式赋值。


1.1.9 Spring如何装配各种集合类型的属性

set:
private Set<String> set;

1
2
3
4
5
6
7
8
9
10
11
12
13
· <property name="set">

· <set>

· <value>第一个</value>  

· <value>第二个</value>  

· <value>第三个</value>  

· </set>

· </property>

list:

private List<String> list;

1
2
3
4
5
6
7
8
9
10
11
12
13
· <property name="list">

· <list>

· <value>第一个</value>  

· <value>第二个</value>  

· <value>第三个</value>  

· </list>

· </property>

properties:

private Properties properties;

1
2
3
4
5
6
7
8
9
10
11
12
13
· <property name="properties">

· <props>

· <prop key="key1">value1</prop>

· <prop key="key2">value2</prop>

· <prop key="key3">value3</prop>

· </props>

· </property>

map:
peivate Map<String,String> map;

1
2
3
4
5
6
7
8
9
10
11
12
13
· <property name="map">

· <map>

· <entry key="key1" value="value1"></entry>

· <entry key="key2" value="value2"></entry>

· <entry key="key3" value="value3"></entry>

· </map>

· </property>

1.1.10 使用构造器装配属性

PersonServiceBean.java

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
•  package service.impl;  
•
• import java.util.Set;
•
• import dao.PersonDao;
• import service.PersonService;
•
•
• public class PersonServiceBean implements PersonService {
• private PersonDao personDao;
• private String name;
•
• public PersonServiceBean(PersonDao personDao, String name) {
• this.personDao = personDao;
• this.name = name;
• }
• @Override
• public String getName() {
• // TODO Auto-generated method stub
• return this.name;
• }
• @Override
• public PersonDao getPersonDao() {
• // TODO Auto-generated method stub
• return this.personDao;
• }
• @Override
• public void save() {
• // TODO Auto-generated method stub
• System.out.println(this.getClass().getName()+" save方法");
• }
• public void setPersonDao(PersonDao personDao) {
• this.personDao = personDao;
• }
• public void setName(String name) {
• this.name = name;
• }
•
• }

beans.xml

1
2
3
4
5
6
•  <bean id="personService" class="service.impl.PersonServiceBean">  
•
• <constructor-arg index="0" ref="personDao"></constructor-arg>
• <constructor-arg index="1" value="pf"></constructor-arg>
•
• </bean>

index指明了参数的位置,加了index就不用加type来说明了。


1.1.11 用@Resource注解完成属性装配

java代码注入配置,需要spring解压文件夹下lib/j2ee/common-annotation.jar这个库文件,添加玩以后,修改beans.xml

1
2
3
4
5
6
7
8
9
10
11
•  <?xml version="1.0" encoding="UTF-8"?>  
• <beans xmlns="http://www.springframework.org/schema/beans"
• xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
• xmlns:context="http://www.springframework.org/schema/context"
• xsi:schemaLocation="http://www.springframework.org/schema/beans
• http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
• http://www.springframework.org/schema/context
• http://www.springframework.org/schema/context/spring-context-2.5.xsd">
•
• <context:annotation-config/>
• </beans>

由于现在家里上spring的网站总是上不去,如果要出现标签提示,那么和前面一样在本地添加spring-context-2.5.xsd

现在配置工作完成了,现在开始用java代码来完成注入

@Autowired方式:默认按类型装配,默认情况下它要求以来对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用

1
2
3
@Autowired @Qualifier("personDaoBean")

private PersonDao personDao;

@Resource方式:默认按名称装配,名称可以通过@Resource的name属性指定,如果没有指定的name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找以来对象,当注解标注在属性的setter方法上,即迷人取属性名作为bean名称寻找以来对象。

1
2
3
@Resource(name="personDaoBean")

private PersonDao personDao;

推荐使用@Resource方式,因为@Resource是j2ee里的一个注解,而@AutoWired是spring里的注解,使用@Resource可以降低与框架的耦合度。

beans.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
•  <?xml version="1.0" encoding="UTF-8"?>  
• <beans xmlns="http://www.springframework.org/schema/beans"
• xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
• xmlns:context="http://www.springframework.org/schema/context"
• xsi:schemaLocation="http://www.springframework.org/schema/beans
• http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
• http://www.springframework.org/schema/context
• http://www.springframework.org/schema/context/spring-context-2.5.xsd">
•
• <context:annotation-config/>
•
• <bean id="personDao" class="dao.impl.PersonDaoBean"/>
• <bean id="personService" class="service.impl.PersonServiceBean"/>
•
• </beans>

PersonServiceBean.java

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
•  package service.impl;  
•
• import javax.annotation.Resource;
•
• import dao.PersonDao;
• import service.PersonService;
•
•
• public class PersonServiceBean implements PersonService {
• @Resource private PersonDao personDao;
• private String name;
•
• public PersonServiceBean() {
•
• }
• @Override
• public String getName() {
• // TODO Auto-generated method stub
• return this.name;
• }
• @Override
• public PersonDao getPersonDao() {
• // TODO Auto-generated method stub
• return this.personDao;
• }
• @Override
• public void save() {
• // TODO Auto-generated method stub
• System.out.println(this.getClass().getName()+" save方法");
• }
• public void setPersonDao(PersonDao personDao) {
• this.personDao = personDao;
• }
• public void setName(String name) {
• this.name = name;
• }
• }

运行发现注入成功,string类型就不需要用注解注入了,直接赋值就可以了。

另外吧@Resource放在setter方法上也是可以的,效果一样。

1.1.12 编码剖析@Resource注解的实现原理

ItcastResource.java

1
2
3
4
5
6
7
8
9
10
11
12
•  package junit.test;  
•
• import java.lang.annotation.ElementType;
• import java.lang.annotation.Retention;
• import java.lang.annotation.RetentionPolicy;
• import java.lang.annotation.Target;
•
• @Retention(RetentionPolicy.RUNTIME)
• @Target({ElementType.FIELD, ElementType.METHOD})
• public @interface ItcastResource {
• public String name() default "";
• }

PropertyDefinition .java

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
•  package junit.test;  
•
• public class PropertyDefinition {
• private String name;
• private String ref;
• private String value;
•
• public String getValue() {
• return value;
• }
•
• public void setValue(String value) {
• this.value = value;
• }
•
• public PropertyDefinition(String name, String ref, String value) {
• this.name = name;
• this.ref = ref;
• this.value = value;
• }
•
• public String getName() {
• return name;
• }
• public void setName(String name) {
• this.name = name;
• }
• public String getRef() {
• return ref;
• }
• public void setRef(String ref) {
• this.ref = ref;
• }
•
• }

BeanDefinition.java

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
•  package junit.test;  
•
• import java.util.ArrayList;
• import java.util.List;
•
• public class BeanDefinition {
• private String id;
• private String className;
• private List<PropertyDefinition> propertys = new ArrayList<PropertyDefinition>();
•
• public BeanDefinition(String id, String className) {
• this.id = id;
• this.className = className;
• }
• public String getId() {
• return id;
• }
• public void setId(String id) {
• this.id = id;
• }
• public String getClassName() {
• return className;
• }
• public void setClassName(String className) {
• this.className = className;
• }
• public List<PropertyDefinition> getPropertys() {
• return propertys;
• }
• public void setPropertys(List<PropertyDefinition> propertys) {
• this.propertys = propertys;
• }
• }

ItcastClassPathXMLApplicationContext.java

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
•  package junit.test;  
•
• import java.beans.Introspector;
• import java.beans.PropertyDescriptor;
• import java.lang.reflect.Field;
• import java.lang.reflect.Method;
• import java.net.URL;
• import java.util.ArrayList;
• import java.util.HashMap;
• import java.util.List;
• import java.util.Map;
•
• import org.apache.commons.beanutils.ConvertUtils;
• import org.dom4j.Document;
• import org.dom4j.Element;
• import org.dom4j.XPath;
• import org.dom4j.io.SAXReader;

• public class ItcastClassPathXMLApplicationContext {
• private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
• private Map<String, Object> sigletons = new HashMap<String, Object>();
•
• public ItcastClassPathXMLApplicationContext(String filename){
• this.readXML(filename);
• this.instanceBeans();
• this.annotationInject();
• this.injectObject();
• }
• /**
• * 通过注解实现注入依赖对象
• */
• private void annotationInject() {
• for(String beanName : sigletons.keySet()){
• Object bean = sigletons.get(beanName);
• if(bean!=null){
• try {
• PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
• for(PropertyDescriptor properdesc : ps){
• Method setter = properdesc.getWriteMethod();//获取属性的setter方法
• if(setter!=null && setter.isAnnotationPresent(ItcastResource.class)){
• ItcastResource resource = setter.getAnnotation(ItcastResource.class);
• Object value = null;
• if(resource.name()!=null && !"".equals(resource.name())){
• value = sigletons.get(resource.name());
• }else{
• value = sigletons.get(properdesc.getName());
• if(value==null){
• for(String key : sigletons.keySet()){
• if(properdesc.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())){
• value = sigletons.get(key);
• break;
• }
• }
• }
• }
• setter.setAccessible(true);
• setter.invoke(bean, value);//把引用对象注入到属性
• }
• }
• Field[] fields = bean.getClass().getDeclaredFields();
• for(Field field : fields){
• if(field.isAnnotationPresent(ItcastResource.class)){
• ItcastResource resource = field.getAnnotation(ItcastResource.class);
• Object value = null;
• if(resource.name()!=null && !"".equals(resource.name())){
• value = sigletons.get(resource.name());
• }else{
• value = sigletons.get(field.getName());
• if(value==null){
• for(String key : sigletons.keySet()){
• if(field.getType().isAssignableFrom(sigletons.get(key).getClass())){
• value = sigletons.get(key);
• break;
• }
• }
• }
• }
• field.setAccessible(true);//允许访问private字段
• field.set(bean, value);
• }
• }
• } catch (Exception e) {
• e.printStackTrace();
• }
• }
• }
• }
•
• /**
• * 为bean对象的属性注入值
• */
• private void injectObject() {
• for(BeanDefinition beanDefinition : beanDefines){
• Object bean = sigletons.get(beanDefinition.getId());
• if(bean!=null){
• try {
• PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
• for(PropertyDefinition propertyDefinition : beanDefinition.getPropertys()){
• for(PropertyDescriptor properdesc : ps){
• if(propertyDefinition.getName().equals(properdesc.getName())){
• Method setter = properdesc.getWriteMethod();//获取属性的setter方法 ,private
• if(setter!=null){
• Object value = null;
• if(propertyDefinition.getRef()!=null && !"".equals(propertyDefinition.getRef().trim())){
• value = sigletons.get(propertyDefinition.getRef());
• }else{
• value = ConvertUtils.convert(propertyDefinition.getValue(), properdesc.getPropertyType());
• }
• setter.setAccessible(true);
• setter.invoke(bean, value);//把引用对象注入到属性
• }
• break;
• }
• }
• }
• } catch (Exception e) {
• }
• }
• }
• }
• /**
• * 完成bean的实例化
• */
• private void instanceBeans() {
• for(BeanDefinition beanDefinition : beanDefines){
• try {
• if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
• sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
• } catch (Exception e) {
• e.printStackTrace();
• }
• }
•
• }
• /**
• * 读取xml配置文件
• * @param filename
• */
• private void readXML(String filename) {
• SAXReader saxReader = new SAXReader();
• Document document=null;
• try{
• URL xmlpath = this.getClass().getClassLoader().getResource(filename);
• document = saxReader.read(xmlpath);
• Map<String,String> nsMap = new HashMap<String,String>();
• nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间
• XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径
• xsub.setNamespaceURIs(nsMap);//设置命名空间
• List<Element> beans = xsub.selectNodes(document);//获取文档下所有bean节点
• for(Element element: beans){
• String id = element.attributeValue("id");//获取id属性值
• String clazz = element.attributeValue("class"); //获取class属性值
• BeanDefinition beanDefine = new BeanDefinition(id, clazz);
• XPath propertysub = element.createXPath("ns:property");
• propertysub.setNamespaceURIs(nsMap);//设置命名空间
• List<Element> propertys = propertysub.selectNodes(element);
• for(Element property : propertys){
• String propertyName = property.attributeValue("name");
• String propertyref = property.attributeValue("ref");
• String propertyValue = property.attributeValue("value");
• PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyref, propertyValue);
• beanDefine.getPropertys().add(propertyDefinition);
• }
• beanDefines.add(beanDefine);
• }
• }catch(Exception e){
• e.printStackTrace();
• }
• }
• /**
• * 获取bean实例
• * @param beanName
• * @return
• */
• public Object getBean(String beanName){
• return this.sigletons.get(beanName);
• }
• }

实际上也就是通过了反射技术来构造对象并且赋值,只是用到了注解的方法,并且利用@interface来构造自定义的注解类型。

1.1.13 @Autowire注解与自动装配

使用了@Autowired的注解方式,这种默认按类型查找符合的bean注入
@Autowired **private** PersonDao personDao;  
使用@Qualifier注明bean名称注入
@Autowired @Qualifier("personDao") **private** PersonDao personDao;  

还可以添加required属性,在没找到bean的情况下,如果required为false,则注入null,required为true,则报错。
@Autowired(required=true) @Qualifier("personDao") private PersonDao personDao;  
自动装配:

通过bean标签的autowire属性来配置,有5种值

no 不使用自动装配,必须通过ref元素指定依赖,默认设置。  

byName 根据属性名自动装配。此选项将检查容器并根据名字查找与                  

属性完全一致的bean,并将其与属性自动装配。  

byType 如果容器中存在一个与指定属性类型相同的bean,那么将与  

该属性自动装配;如果存在多个该类型bean,那么抛出异  

常,并指出不能使用byType方式进行自动装配;如果没有找  

到相匹配的bean,则什么事都不发生,也可以通过设置  

dependency-check=”objects”让Spring抛出异常。  

constructor 与byType方式类似,不同之处在于它应用于构造器参数。如  

果容器中没有找到与构造器参数类型一致的bean,那么抛出  

异常。  

autodetect 通过bean类的自省机制(introspection)来决定是使用  

constructor还是byType方式进行自动装配。如果发现默认的  

构造器,那么将使用byType方式。


1.1.14 让Spring自动扫描和管理Bean

让Spring自动扫描和管理Bean

1
<context:component-scan base-package="cn.test"></context:component-scan>      

其中base-package为需要扫描的包(含子包)

@Service用于标注业务层组件,@Controller用于标注控制层组件(如struts中的action),@Repository用于标注数据访问组件,即DAO组件,而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

bean的默认名称是类名,然后把第一个字母改为小写。可以通过@Service(“xxx”)修改bean名称。

这种bean默认是单例的,如果想改变,可以使用@Service(“aaaaa”) @Scope(“prototype”)来改变。

还可以通过@PostConstruct @PreDestroy设置初始化和销毁的函数

1
2
3
4
5
6
7
8
9
10
11
•  @PostConstruct  
• public void init(){
• System.out.println("初始化");
• }
•
•
•
• @PreDestroy
• public void destory(){
• System.out.println("开闭资源");
• }

1.1.15 使用JDK中的Proxy技术实现AOP功能

通过代理对象来调用对象的方法,从而做出权限控制。

目标对象必须实现接口才能使用proxy技术创建代理对象。

PersonService.java

1
2
3
4
5
6
7
•  package cn.pf.aop.service;  
•
• public interface PersonService {
• public void save(String name);
• public void update(String name, Integer personId);
• public String getName(Integer personId);
• }

PersonServiceBean.java

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
•  package cn.pf.aop.service.impl;  
•
• import cn.pf.aop.service.PersonService;
•
• public class PersonServiceBean implements PersonService {
• private String user = null;
•
• public PersonServiceBean() {
•
• }
•
• public PersonServiceBean(String user) {
• this.setUser(user);
• }
•
• @Override
• public String getName(Integer personId) {
• System.out.println(this.getClass().getName()+" getName方法");
• return "pf";
• }
•
• @Override
• public void save(String name) {
• System.out.println(this.getClass().getName()+" save方法");
• }
•
• @Override
• public void update(String name, Integer personId) {
• System.out.println(this.getClass().getName()+" update方法");
• }
•
• public void setUser(String user) {
• this.user = user;
• }
•
• public String getUser() {
• return user;
• }
•
• }

JDKProxyFactory.java

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
•  package cn.pf.aop;  
•
• import java.lang.reflect.InvocationHandler;
• import java.lang.reflect.Method;
• import java.lang.reflect.Proxy;
•
• import cn.pf.aop.service.impl.PersonServiceBean;
•
• public class JDKProxyFactory implements InvocationHandler {
• private Object targetObject;
•
• public Object createProxyIntance(Object targetObject){
• this.targetObject = targetObject;
• return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
• this.targetObject.getClass().getInterfaces(), this);
• }
•
• @Override
• public Object invoke(Object proxy, Method method, Object[] arg2)
• throws Throwable {
• PersonServiceBean personServiceBean = (PersonServiceBean)targetObject;
• Object result = null;
• if(personServiceBean.getUser() != null){
• result = method.invoke(targetObject, arg2);
• }
• return null;
• }
• }

AOPTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
•  package junit.test;  
•
• import org.junit.Test;
•
• import cn.pf.aop.JDKProxyFactory;
• import cn.pf.aop.service.PersonService;
• import cn.pf.aop.service.impl.PersonServiceBean;
•
•
• public class AOPTest {
• @Test public void proxyTest(){
• JDKProxyFactory factory = new JDKProxyFactory();
• PersonService personService = (PersonService) factory.createProxyIntance(new PersonServiceBean());
• personService.save("111");
• }
• }

Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this);

创建代理对象的时候,加入了该目标对象所有的接口,即对所有的方法进行监听,任何一个方法的调用都会触发代理对象的invoke方法。this表示触发哪个代理对象的invoke方法,这里我们设置当前代理对象。

调用personService的save方法的时候,可以理解为,save方法被监听,进入代理对象的invoke方法,如果user!=null,则invoke方法中调用了personService的save方法,如果user==null,则什么也不做。

通过反射技术调用方法其实可以简单的理解为

xxx.invoke(obj,args)返回的结果是obj.xxx(args)


1.1.16 使用CGLIB实现AOP功能与AOP概念解释

前面的proxy技术必须在类实现了接口的前提下才可以实现权限的控制,cglb可以在类不实现接口的情况下完成。

在spring文件夹下lib/cglib下找到cglib的jar库文件,加入工程。

CGlibProxyFactory.java

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
•  package cn.pf.aop;  
•
• import java.lang.reflect.Method;
•
• import cn.pf.aop.service.impl.PersonServiceBean;
•
• import net.sf.cglib.proxy.Enhancer;
• import net.sf.cglib.proxy.MethodInterceptor;
• import net.sf.cglib.proxy.MethodProxy;
•
• public class CGlibProxyFactory implements MethodInterceptor{
• private Object targetObject;
•
• public Object createProxyIntance(Object targetObject){
• this.targetObject = targetObject;
• Enhancer enhancer = new Enhancer();
• enhancer.setSuperclass(this.targetObject.getClass());
• enhancer.setCallback(this);
• return enhancer.create();
• }
•
• @Override
• public Object intercept(Object proxy, Method method, Object[] arg2,
• MethodProxy arg3) throws Throwable {
• PersonServiceBean personServiceBean = (PersonServiceBean)targetObject;
• Object result = null;
• if(personServiceBean.getUser() != null){
• result = method.invoke(targetObject, arg2);
• }
• return result;
• }
• }

AOPTest.java

1
2
3
4
5
6
•  @Test public void proxyTest2(){  
• CGlibProxyFactory factory = new CGlibProxyFactory();
• PersonServiceBean personServiceBean = (PersonServiceBean) factory.createProxyIntance(new PersonServiceBean("1"));
• personServiceBean.save("111");
• }

CGlib的enhance继承了目标类所有非final方法,对这些方法进行覆盖。创建的代理对象是目标对象的子类


1.1.17 使用Spring的注解方式实现AOP入门

首先添加包
``
/spring.jar

/lib/aspectj/aspectjrt.jar

/lib/aspectj/aspectjweaver.jar

/lib/j2ee/common-annotations.jar

/lib/jakarta-commons/common_logging.jar

/lib/cglib/cglib-nodep-2.1-3.jar
``
beans.xml

1
2
3
4
5
6
7
8
9
10
11
12
•  <?xml version="1.0" encoding="UTF-8"?>  
• <beans xmlns="http://www.springframework.org/schema/beans"
• xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
• xmlns:aop="http://www.springframework.org/schema/aop"
• xsi:schemaLocation="http://www.springframework.org/schema/beans
• http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
• http://www.springframework.org/schema/aop
• http://www.springframework.org/schema/context/spring-aop-2.5.xsd">
•
• <aop:aspectj-autoproxy/>
• </beans>

PersonService.java和PersonServiceBean.java和上篇一样

MyInterceptor.java

@Ascept声明了切面,即进行拦截的类。

@Pointcut声明了切入点,即进行拦截的方法。

@Pointcut(“execution(* cn.itcast.service...(..))”)

  • 代表返回值类型

cn.pf.service 需要拦截的包名

.. 代表队子包的类进行拦截

  • 代表进行拦截的类

  • 代表进行拦截的方法

(..) 代表方法的参数随意

(*代表任意)

下面来测试前置通知,后置通知,最终通知,例外通知以及环绕通知。

MyInterceptor.java

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
•  package cn.pf.aop.service;  
•
• import org.aspectj.lang.ProceedingJoinPoint;
• import org.aspectj.lang.annotation.After;
• import org.aspectj.lang.annotation.AfterReturning;
• import org.aspectj.lang.annotation.AfterThrowing;
• import org.aspectj.lang.annotation.Around;
• import org.aspectj.lang.annotation.Aspect;
• import org.aspectj.lang.annotation.Before;
• import org.aspectj.lang.annotation.Pointcut;
•
• @Aspect
• public class MyInterceptor {
• @Pointcut("execution(* cn.pf.aop.service.impl.PersonServiceBean.*(..))")
• private void anyMethod(){}
•
• @Before("anyMethod()")
• public void doAccessCheck(){
• System.out.println("前置通知");
• }
•
• @AfterReturning("anyMethod()")
• public void doAfterReturning(){
• System.out.println("后置通知");
• }
•
• @After("anyMethod()")
• public void doAfter(){
• System.out.println("最终通知");
• }
•
• @AfterThrowing("anyMethod()")
• public void doAfterThrowing(){
• System.out.println("例外通知");
• }
•
• @Around("anyMethod()")
• public Object doBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
• System.out.println("进入环绕方法");
• Object result = pjp.proceed();
• System.out.println("退出环绕方法");
• return result;
• }
• }

SpringAOPTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
•  import org.junit.Test;  
• import org.springframework.context.ApplicationContext;
• import org.springframework.context.support.ClassPathXmlApplicationContext;
•
• import cn.pf.aop.service.PersonService;
•
•
•
• public class SpringAOPTest {
• @Test public void interceptorTest(){
• ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
• PersonService personService = (PersonService)context.getBean("personService");
• personService.save("1");
• }
• }

控制台输出:

前置通知

进入环绕方法

cn.pf.aop.service.impl.PersonServiceBean save方法

后置通知

最终通知

退出环绕方法

那么如何获得输入参数,返回值,异常呢,那么稍作修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
•  @Before("anyMethod() && args(name)")  
• public void doAccessCheck(String name) {
• System.out.println("前置通知:"+ name);
• }
• @AfterReturning(pointcut="anyMethod()",returning="result")
• public void doAfterReturning(String result) {
• System.out.println("后置通知:"+ result);
• }
• @After("anyMethod()")
• public void doAfter() {
• System.out.println("最终通知");
• }
• @AfterThrowing(pointcut="anyMethod()",throwing="e")
• public void doAfterThrowing(Exception e) {
• System.out.println("例外通知:"+ e);
• }

 其实切面就感觉像servlet里面的过滤器,在方法的前后加上一些关卡,进行筛选,判定权限,通过指定好的一些切面后,才可以真正调用目标对象的方法。


1.1.18 基于XML配置方式声明切面

基于XML配置方式声明切面

与注释方法没什么太大的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
•  <bean id=”orderservice” class=”cn.service.OrderServiceBean” />  
•
• <bean id=”log” class=”cn.service.LogPrint” />
•
• <aop:config>
•
• <aop:aspect id=”myaop” ref=”log”>
•
• <aop:pointcut id=”mycut” expression=”execution(* cn.service..*.*(..))”/>
•
• <aop:before pointcut-ref=”mycut” method=”doAccessCheck” />
•
• <aop:after-returning pointcut-ref=”mycut” method=”doReturnCheck” />
•
• <aop:after-throwing pointcut-ref=”mycut” method=”doExceptionAction” />
•
• <aop:after pointcut-ref=”mycut” method=”doReleaseAction” />
•
• <aop:around pointcut-ref=”mycut” method=”doBasicProfiling” />
•
• </aop:aspect>
•
• </aop:config>


1.1.19 aspectj的切入点语法定义细节

execution(* cn.pf.aop.service.impl.PersonServiceBean.*(..))所有非final方法

execution(!void cn.pf.aop.service.impl.PersonServiceBean.*(..))非void非final方法

execution(java.lang.String cn.pf.aop.service.impl.PersonServiceBean.*(..))非final且返回类型为String的方法

execution(java.lang.String cn.pf.aop.service.impl.PersonServiceBean.*(java.lang.String,..))第一个参数为String的非final方法

execution(* cn.pf.aop.service.impl..*.*(..))对包下所有类进行拦截


展开全文 >>

CSS设计指南

2019-11-17

1.1 CSS 语法

CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明。

1
selector {declaration1; declaration2; ... declarationN }

选择器通常是您需要改变样式的 HTML 元素。

每条声明由一个属性和一个值组成。

属性(property)是您希望设置的样式属性(style attribute)。每个属性有一个值。属性和值被冒号分开。

1
selector {property: value}

下面这行代码的作用是将 h1 元素内的文字颜色定义为红色,同时将字体大小设置为 14 像素。

在这个例子中,h1 是选择器,color 和 font-size 是属性,red 和 14px 是值。

1
h1 {color:red; font-size:14px;}

下面的示意图为您展示了上面这段代码的结构:

image.png

1.1.1 字体属性:

属性 含义 属性值

1
2
3
4
5
6
7
8
9
10
11
font-family 字体 各种字体

font-style 字体样式 italic、oblique

font-variant 小体大写 small-caps

font-weight 字体粗细 bold、bolder、lighter…

font-size 字体大小 absolute、relative、%

color 字体颜色 颜色值

1.1.2 颜色与背景属性:

属性 含义 属性值

1
2
3
4
5
6
7
8
9
10
11
Color 颜色  颜色值

Background-color 背景色 颜色值

Background-image 背景图案 图片路径

Background-repeat 背景图案重复方式 Repeat-x | repeat-y | no-repeat

Background-attachment 背景的滚动 Scroll | fix

Background-position 背景图案初始位置 % | n em | top | left | right | bottom

1.1.3 文本属性:

属性 含义 属性值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
word-spacing 单词间距 n em

letter-spacing 字母间距 n em

text-decoration 装饰样式 underline| overline| line-through| blink

vertical-align 垂直方向位置 sub| super |top |text-top| middle| bottom| text-bottom

text-transform 转为其他形式 capitalize| uppercase| lowercase

text-align 对齐 left| right| center| justify

text-indent 缩进 n em| %

line-height 行高 pixels、n em、%

1.1.4 边距属性:

 属性 含义 属性值

1
2
3
4
5
6
7
margin-top 上边距 n em | %

margin-right 右 n em | %

margin-bottom 下 n em | %

margin-left 左 n em | %

1.1.5 边框属性:

属性 含义 属性值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Border-top-width 上边框宽度 n em | thin | medium | thick

Border-right-width 右 同上

Border-bottom-width 下 同上

Border-left-width 左 同上

Border-width 四边 同上

Border-color 边框颜色 Color

Border-style 边框样式 Dotted | dash | solid | double | groove | ridge | inset | outset

Border-top|right|bottom|left 上(右|底|左)所有属性 Border-width | border-style | color

1.1.6 图文混排:

 属性 含义 属性值

1
2
3
4
5
6
7
Width 宽度 n em | %

Height 高度 n em

Float 文字环绕 Left | right

clear 去除文字环绕 Left | right | both

1.1.7 列表属性:

属性 含义 属性值

1
2
3
4
5
6
7
8
9
10
11
Display 是否显示 Block | inline | list-item | none

White-space 空白部分 Pre | nowrap | normal(是否合并)

List-style-type 项目编号 Disc|circle|square|decimal|lower-roman|upper-roman|lower-alpha|upper-alpha|none

List-style-image 项目前图片 Img-url

List-style-position 第二行位置 Inside | outside

List-style 全部属性 Keyword | position | url

1.1.8 鼠标属性:

属性值 含义 属性值 含义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Auto 自动 N-resize 上箭头

Crosshair "+" Se-resize 右下

Default 默认 Sw-resize 左下

Hand 手形 S-resize 下箭头

Move 移动 W-resize 左箭头

E-resize 右箭头 Text "I"

Ne-resize 右上 Wait 沙漏

Nw-resize 左上 help 帮助

1.2 CSS选择器

1.2.1 li标签选择器

html:

1
<li>列表项1</li>(改变标签里的样式类型)

css:

针对所有列表

1
2
3
4
5
6
7
li{

color: rgb(255,0,0); /*#ff0000 red*/

font-size: 30px;

}

1.2.2 类选择器 class(把一堆样式划为一类)

1
2
<pre style="margin-bottom:18.0pt;line-height:16.5pt;background:#F6F8FA;
word-break:break-all">div`.topBar +Tab = <`divclass`=`"topBar"`></`div`>`</pre>

当前页面内可以多个同样 html:

1
2
<pre style="margin-bottom:18.0pt;line-height:16.5pt;background:#F6F8FA;
word-break:break-all"><li class="blue">`列表项``2`</li></pre>

css:

1
2
3
4
5
<pre style="margin-bottom:18.0pt;
line-height:16.5pt;background:#F6F8FA;word-break:break-all"> color: #0000ff;</pre>

<pre style="margin-bottom:18.0pt;
line-height:16.5pt;background:#F6F8FA;word-break:break-all"> }</pre>

1.2.3 ID选择器 id(也可控制某个DIV样式)

当前页面内唯一id; 如果页面内出现多个相同id,虽然可以解析出,但不规范,不建议

1
2
3
4
5
6
7
8
9
<li id="item">列表项1</li>

#item{

color: #00FF00;

font-size:40px;

}

权重越大,优先级越高,优先级高的覆盖优先级低的

各种选择器可以用在列表li、容器div等中

html:

1
<div>这是一个Div</div>

CSS:

1
2
3
4
5
div{

background-color: #ccc;

}

1.3 CSS样式规则

1.3.1 三种定义样式的方法

①**在本文件下的表示方法**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 <head>

<style type="text/css"> //样式

选择器(即修饰对象){ //.g{ }

对象属性1:属性值1; //font:

对象属性2:属性值2; //height:50px;

}

</style>

</head>

②在1.css文件中的表示方法

1
<link rel="stylesheet"  type="text/css" href="1.css">

③直接跟着定义样式

1
<div class=" size show show-1"></div>

每个样式间用空格隔开,有三个样式 size、show、show-1

1.3.2 颜色

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
red = #FF0000 = rgb(255,0,0) 红色

blue = #0000ff = 蓝色

green = #00FF00 = 绿色

#ccc = #cccccc 灰色

#fff = #ffffff 白色

black = #333 黑色

background: #fff; 背景为白色

background: #fff; 背景为白色

cursor: pointer; 当移动到当前位置时(配合li:hover)变成小手

transition: all 1s ease; 渐变效果(有些浏览器不支持)

-webkit-transition:all 1s ease;

-o-transition:all 1s ease;

-moz-transition:all 1s ease;

-ms-transition: all 1s ease; 对于不支持的浏览器,解决方案

1.3.3 字体font

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 width: 200px;宽度

font-weight: bold; 字体粗细(粗体)

font-size: 12px; 字体大小

color: #ff7300; 字体颜色

background-color: #ccc; 背景色

height 高度

width 宽度(可以认为是长度)

font-family:隶书;字体

长度单位:

1
px 像素

颜色

1
2
3
十六进制:#ffffff

颜色名称:red

尺寸属性:

1
2
3
Height、max_height、min_height

width、max_width、min_width

字体、字号:

1
2
3
4
5
6
7
8
9
font 缩写形式

font-weight 粗细

font-size 大小

font-family 字体系列

font-style 字体样式

字体颜色

1
2
3
color

opacity 透明度 css3

行距、对齐等

1
2
3
4
5
6
7
8
9
10
11
12
13
line-height 行高

text-align 对齐

letter-spacing 字符间距

text-decoration 文本修饰

overflow 浮动

text-overflow

text-indent

1.3.4 列表宽度决定了列表项宽度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
height: 100px;

line-height: 100px; 使其上下居中对齐

text-align: right; 水平对齐方式:水平靠右

text-align: center; 水平对齐方式:水平居中

letter-spacing: 10px; 字间距

text-decoration: none; 下划线设置(去除)

white-space: nowrap; 设为一行显示

overflow: hidden;

display: block; 隐藏多余内容

1.3.5 图片

1
2
3
4
5
6
7
background-image: url(images/1.jpg); 插入图片

background-image: url(../images/1.jpg); 插入上一级文件夹中images文件夹中的图片1.jpg

background-repeat: no-repeat; 图片默认多大就是多大(不加自动复制显示)

background-position: 50px 50px; 移动图片(正数为右下角,负数为左上角)

1.3.6 块级元素 默认占一行

1
2
3
4
5
6
7
8
9
10
11
★float: left; 浮动 将块级元素变为行级元素 让多行在一行  如果一行放不下,自动转第二行

height: 50px; /*使其上下居中对齐*/

line-height: 50px; /*使其上下居中对齐*/

text-align: center; 水平对齐方式:水平居中

margin-right: 5px; 每个元素间间隔5px

font-size: 20px; 字体大小

1.3.7 焦点

鼠标移到当前位置时的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
a:hover{

color: red;

}

/*鼠标激活选定状态*/

a:active{

color: green;

}

1.3.8 超链接样式的四种状态

1
2
3
4
5
6
7
未访问状态 a:link

已访问状态 a:visited

鼠标移上状态 a:hover

激活选定状态 a:active

、

1.3.9 盒子(矩形方框)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
margin(外边距/边界)

border(边框)

padding(内边距/填充)

width 宽度

height 高度

margin 外边距/边界

margin:1px 2px 3px 4px 外边距/边界(上右下左)

margin:1px 2px 外边距/边界(上下、左右)

margin:1px 外边距/边界(上下左右、通常表现出来上左)

margin

一个值:4边

两个值:上下、左右

四个值:上右下左

1
2
3
4
5
6
7
margin-left: auto; 左边距自动

margin-left: auto; /水平居中/

margin-right: auto; /水平居中/

margin: 0px auto; /水平居中,有的浏览器不支持/

默认下,div没有边框border(有颜色)

默认不显示border

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
border-color: blue;

border-width: 10px;

border-style: solid; 以上三句话使其最终显示 solid实心的

border width style color

border:20px solid green 一句话也可以显示

border-top:30px solid #ccc; 设置上边框属性

padding-top: 20px; 内边距(上)

padding: 20px; 内边距(上下左右)

padding: 20px 50px; 内边距(上下、左右)

对同一属性做设置,后面的优先级高

对外边距/边界设置,一般统一格式,不要上边设下边距,下边设上边距;如果设置,按大的显示

1
2
3
margin-bottom: 30px;

.topbar .search .topabar .link{float:right;} 共用一种样式{浮动 将块级元素变为行级元素}

1.3.10 解决浮动问题,使用尾类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.fix{*zoom:1;*clear:both;} 

.fix:before,.fix:after{

display: table;clear:both;

content: '';

}

<div class="wrap fix"> +fix谁的子元素浮动,就可以清除影响

.fix{*zoom:1;*clear:both;}

.fix:before,.fix:after{

display: table;clear:both;

content: '';

}

1.3.11 定位

1
2
3
position: absolute; 绝对定位

position: relative; 相对定位

参照物是距离最近 定位的父元素

1.3.12 对定位元素显示排序

1
z-index: 1; 数字越大,优先级越高

设置 鼠标指到哪里,哪里在上边

1
2
3
4
5
.c4:hover{

z-index: 3;

}

1.3.13 响应式

1
2
3
4
5
viewport

 设备方向:Orientation

 设备方向:Orientation

1.3.14 其他样式

header、footer、center、left、right

头 尾 中间 左 右

链接 默认带下划线

1
<div class=" size show show-1"></div>

每个样式间用空格隔开,有三个样式 size、show、show-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
list-style:none; 去除前面的列表序列号

border 边框

border: 1px solid red; 添加边框

<div style="clear: both"></div> 用于消除浮动效果导致的顺序错乱

height 高度

width 宽度(可以认为是长度)

font-family:隶书;字体

top: 25px; 距离上方25px

left: 15px; 距离左边15px

display: none;不显示

@media screen and (max-width: 900px){ } 当分辨率小于900px时

line-height: 1.8; 行高

1.4 为CSS单独建立文件(建立链接link)

1
2
3
4
5
6
7
  <link rel="stylesheet"  type="text/css" href="style.css">

<link + Enter

style.css 新建文件

@charset 'utf-8';

展开全文 >>

JavaScript学习指南

2019-11-17

1.1 JavaScript基础概念:

JavaScript (ECMAScript) :JavaScript 是脚本语言。JavaScript和ECMAScript通常被人用来表达相同的含义,但是JavaScript并不是这么一点含义,它是由ECMAScript 核心. DOM 文档对象模型. BOM 浏览器对象模型 这三部分组成。浏览器会在读取代码时,逐行地执行脚本代码。而对于传统编程来说,会在执行前对所有代码进行编译。

1.1.1 ECMAScript

组成部分:语法,类型,语句,关键字,保留字,操作符,对象。

1.1.2 DOM

文档对象模型(DOM , Document Object Model)是针对XML但是经过拓展用于HTML的应用程序编程接口。DOM把整个页面

映射为一个多层节点结构,开发人员借助DOM Api对节点进行操作。

HTML DOM (文档对象模型)

当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。

HTML DOM 模型被构造为对象的树。

HTML DOM 树:

image.png

通过可编程的对象模型,JavaScript 获得了足够的能力来创建动态的 HTML。

· JavaScript 能够改变页面中的所有 HTML 元素

· JavaScript 能够改变页面中的所有 HTML 属性

· JavaScript 能够改变页面中的所有 CSS 样式

· JavaScript 能够对页面中的所有事件做出反应

1.1.3 BOM

浏览器对象模型(Browser Object Model)使用BOM控制浏览器显示页面意外的部分。

1.2 在HTML中使用JavaScript方式

1.2.1 javaScript脚本加载的位置

1 通过在网页中加入标记JavaScript的开始和结束,将JavaScript代码放到之间

2 也可以引入一个外部的JavaScript文件,这个JavaScript文件一般以.js作为扩展名

3 原则上,放在之间。但视情况可以放在网页的任何部分

4 一个页面可以有几个

  • 所有文章
  • 友链
  • 关于我

tag:

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • 友情链接1
  • 友情链接2
很惭愧<br><br>只做了一点微小的工作<br>了解更多内容请关注微信公众号:【海若Hero】<br>谢谢大家