月度归档:2012年04月

Generating Extended Validation (EV) SSL Certificates

Just did a project for the “Innovation” team of a large corp. They wanted to see the EV SSL certificates make the IE 7 browser to go green on their test site.

To generate the EV SSL Cert request, I used the following extensions in the openssl .cnf file:

[ ev_req ]

basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

# For the EV cert policy
certificatePolicies=ia5org,@xyzpolicy

[ ev_cert ]

basicConstraints=CA:FALSE

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always

# For the EV cert policy
certificatePolicies=ia5org,@xyzpolicy

[ xyzpolicy ]

policyIdentifier=1.3.1.1.1.1
CPS.1=”http://www.xyz.com/cps-statement.html
userNotice.1=@xyznotice

[ xyznotice ]

explicitText=”Any use of this Certificate constitutes acceptance of the XYZ EV Demo”

Use the above setting in the ev.cnf file and then generate the cert request using the following command:

openssl req -config ev.cnf -key key.pem -out evreq.pem -subj “/C=US/ST=California/O=XYZ/CN=www.xyz.com” -new -reqexts ev_req

OpenSSL Command

NAME
       ca - sample minimal CA application

SYNOPSIS
       openssl ca [-verbose] [-config filename] [-name section] [-gencrl] [-revoke file] [-crl_reason reason]
       [-crl_hold instruction] [-crl_compromise time] [-crl_CA_compromise time] [-subj arg] [-crldays days] [-crl-
       hours hours] [-crlexts section] [-startdate date] [-enddate date] [-days arg] [-md arg] [-policy arg] [-key-
       file arg] [-key arg] [-passin arg] [-cert file] [-in file] [-out file] [-notext] [-outdir dir] [-infiles]
       [-spkac file] [-ss_cert file] [-preserveDN] [-noemailDN] [-batch] [-msie_hack] [-extensions section] [-extfile
       section] [-engine id]

DESCRIPTION
       The ca command is a minimal CA application. It can be used to sign certificate requests in a variety of forms
       and generate CRLs it also maintains a text database of issued certificates and their status.

       The options descriptions will be divided into each purpose.

CA OPTIONS
       -config filename
           specifies the configuration file to use.

       -name section
           specifies the configuration file section to use (overrides default_ca in the ca section).

       -in filename
           an input filename containing a single certificate request to be signed by the CA.

       -ss_cert filename
           a single self signed certificate to be signed by the CA.

       -spkac filename
           a file containing a single Netscape signed public key and challenge and additional field values to be
           signed by the CA. See the SPKAC FORMAT section for information on the required format.

       -infiles
           if present this should be the last option, all subsequent arguments are assumed to the the names of files
           containing certificate requests.

       -out filename
           the output file to output certificates to. The default is standard output. The certificate details will
           also be printed out to this file.

       -outdir directory
           the directory to output certificates to. The certificate will be written to a filename consisting of the
           serial number in hex with ".pem" appended.

       -cert
           the CA certificate file.

       -keyfile filename
           the private key to sign requests with.

       -key password
           the password used to encrypt the private key. Since on some systems the command line arguments are visible
           (e.g. Unix with the 'ps' utility) this option should be used with caution.

       -passin arg
           the key password source. For more information about the format of arg see the PASS PHRASE ARGUMENTS sec-
           tion in openssl(1).

       -verbose
           this prints extra details about the operations being performed.

       -notext
           don't output the text form of a certificate to the output file.

       -startdate date
           this allows the start date to be explicitly set. The format of the date is YYMMDDHHMMSSZ (the same as an
           ASN1 UTCTime structure).

       -enddate date
           this allows the expiry date to be explicitly set. The format of the date is YYMMDDHHMMSSZ (the same as an
           ASN1 UTCTime structure).

       -days arg
           the number of days to certify the certificate for.

       -md alg
           the message digest to use. Possible values include md5, sha1 and mdc2.  This option also applies to CRLs.

       -policy arg
           this option defines the CA "policy" to use. This is a section in the configuration file which decides
           which fields should be mandatory or match the CA certificate. Check out the POLICY FORMAT section for more
           information.

       -msie_hack
           this is a legacy option to make ca work with very old versions of the IE certificate enrollment control
           "certenr3". It used UniversalStrings for almost everything. Since the old control has various security
           bugs its use is strongly discouraged. The newer control "Xenroll" does not need this option.

       -preserveDN
           Normally the DN order of a certificate is the same as the order of the fields in the relevant policy sec-
           tion. When this option is set the order is the same as the request. This is largely for compatibility with
           the older IE enrollment control which would only accept certificates if their DNs match the order of the
           request. This is not needed for Xenroll.

       -noemailDN
           The DN of a certificate can contain the EMAIL field if present in the request DN, however it is good pol-
           icy just having the e-mail set into the altName extension of the certificate. When this option is set the
           EMAIL field is removed from the certificate' subject and set only in the, eventually present, extensions.
           The email_in_dn keyword can be used in the configuration file to enable this behaviour.

       -batch
           this sets the batch mode. In this mode no questions will be asked and all certificates will be certified
           automatically.

       -extensions section
           the section of the configuration file containing certificate extensions to be added when a certificate is
           issued (defaults to x509_extensions unless the -extfile option is used). If no extension section is
           present then, a V1 certificate is created. If the extension section is present (even if it is empty), then
           a V3 certificate is created.

       -extfile file
           an additional configuration file to read certificate extensions from (using the default section unless the
           -extensions option is also used).

       -engine id
           specifying an engine (by it's unique id string) will cause req to attempt to obtain a functional reference
           to the specified engine, thus initialising it if needed. The engine will then be set as the default for
           all available algorithms.

CRL OPTIONS
       -gencrl
           this option generates a CRL based on information in the index file.

       -crldays num
           the number of days before the next CRL is due. That is the days from now to place in the CRL nextUpdate
           field.

       -crlhours num
           the number of hours before the next CRL is due.

       -revoke filename
           a filename containing a certificate to revoke.

       -crl_reason reason
           revocation reason, where reason is one of: unspecified, keyCompromise, CACompromise, affiliationChanged,
           superseded, cessationOfOperation, certificateHold or removeFromCRL. The matching of reason is case insen-
           sitive. Setting any revocation reason will make the CRL v2.

           In practive removeFromCRL is not particularly useful because it is only used in delta CRLs which are not
           currently implemented.

       -crl_hold instruction
           This sets the CRL revocation reason code to certificateHold and the hold instruction to instruction which
           must be an OID. Although any OID can be used only holdInstructionNone (the use of which is discouraged by
           RFC2459) holdInstructionCallIssuer or holdInstructionReject will normally be used.

       -crl_compromise time
           This sets the revocation reason to keyCompromise and the compromise time to time. time should be in Gener-
           alizedTime format that is YYYYMMDDHHMMSSZ.

       -crl_CA_compromise time
           This is the same as crl_compromise except the revocation reason is set to CACompromise.

       -subj arg
           supersedes subject name given in the request.  The arg must be formatted as
           /type0=value0/type1=value1/type2=..., characters may be escaped by \ (backslash), no spaces are skipped.

       -crlexts section
           the section of the configuration file containing CRL extensions to include. If no CRL extension section is
           present then a V1 CRL is created, if the CRL extension section is present (even if it is empty) then a V2
           CRL is created. The CRL extensions specified are CRL extensions and not CRL entry extensions.  It should
           be noted that some software (for example Netscape) can't handle V2 CRLs.

CONFIGURATION FILE OPTIONS
       The section of the configuration file containing options for ca is found as follows: If the -name command line
       option is used, then it names the section to be used. Otherwise the section to be used must be named in the
       default_ca option of the ca section of the configuration file (or in the default section of the configuration
       file). Besides default_ca, the following options are read directly from the ca section:
        RANDFILE
        preserve
        msie_hack With the exception of RANDFILE, this is probably a bug and may change in future releases. Many of the configuration file options are identical to command line options. Where the option is present in the configuration file and the command line the command line value is used. Where an option is described as mandatory then it must be present in the configuration file or the command line equivalent (if any) used. oid_file
           This specifies a file containing additional OBJECT IDENTIFIERS.  Each line of the file should consist of
           the numerical form of the object identifier followed by white space then the short name followed by white
           space and finally the long name.

       oid_section
           This specifies a section in the configuration file containing extra object identifiers. Each line should
           consist of the short name of the object identifier followed by = and the numerical form. The short and
           long names are the same when this option is used.

       new_certs_dir
           the same as the -outdir command line option. It specifies the directory where new certificates will be
           placed. Mandatory.

       certificate
           the same as -cert. It gives the file containing the CA certificate. Mandatory.

       private_key
           same as the -keyfile option. The file containing the CA private key. Mandatory.

       RANDFILE
           a file used to read and write random number seed information, or an EGD socket (see RAND_egd(3)).

       default_days
           the same as the -days option. The number of days to certify a certificate for.

       default_startdate
           the same as the -startdate option. The start date to certify a certificate for. If not set the current
           time is used.

       default_enddate
           the same as the -enddate option. Either this option or default_days (or the command line equivalents) must
           be present.

       default_crl_hours default_crl_days
           the same as the -crlhours and the -crldays options. These will only be used if neither command line option
           is present. At least one of these must be present to generate a CRL.

       default_md
           the same as the -md option. The message digest to use. Mandatory.

       database
           the text database file to use. Mandatory. This file must be present though initially it will be empty.

       serialfile
           a text file containing the next serial number to use in hex. Mandatory.  This file must be present and
           contain a valid serial number.

       x509_extensions
           the same as -extensions.

       crl_extensions
           the same as -crlexts.

       preserve
           the same as -preserveDN

       email_in_dn
           the same as -noemailDN. If you want the EMAIL field to be removed from the DN of the certificate simply
           set this to 'no'. If not present the default is to allow for the EMAIL filed in the certificate's DN.

       msie_hack
           the same as -msie_hack

       policy
           the same as -policy. Mandatory. See the POLICY FORMAT section for more information.

       nameopt, certopt
           these options allow the format used to display the certificate details when asking the user to confirm
           signing. All the options supported by the x509 utilities -nameopt and -certopt switches can be used here,
           except the no_signame and no_sigdump are permanently set and cannot be disabled (this is because the cer-
           tificate signature cannot be displayed because the certificate has not been signed at this point).

           For convenience the values default_ca are accepted by both to produce a reasonable output.

           If neither option is present the format used in earlier versions of OpenSSL is used. Use of the old format
           is strongly discouraged because it only displays fields mentioned in the policy section, mishandles multi-
           character string types and does not display extensions.

       copy_extensions
           determines how extensions in certificate requests should be handled.  If set to none or this option is not
           present then extensions are ignored and not copied to the certificate. If set to copy then any extensions
           present in the request that are not already present are copied to the certificate. If set to copyall then
           all extensions in the request are copied to the certificate: if the extension is already present in the
           certificate it is deleted first. See the WARNINGS section before using this option.

           The main use of this option is to allow a certificate request to supply values for certain extensions such
           as subjectAltName.

POLICY FORMAT
       The policy section consists of a set of variables corresponding to certificate DN fields. If the value is
       "match" then the field value must match the same field in the CA certificate. If the value is "supplied" then
       it must be present. If the value is "optional" then it may be present. Any fields not mentioned in the policy
       section are silently deleted, unless the -preserveDN option is set but this can be regarded more of a quirk
       than intended behaviour.

SPKAC FORMAT
       The input to the -spkac command line option is a Netscape signed public key and challenge. This will usually
       come from the KEYGEN tag in an HTML form to create a new private key.  It is however possible to create SPKACs
       using the spkac utility.

       The file should contain the variable SPKAC set to the value of the SPKAC and also the required DN components
       as name value pairs.  If you need to include the same component twice then it can be preceded by a number and
       a '.'.

EXAMPLES
       Note: these examples assume that the ca directory structure is already set up and the relevant files already
       exist. This usually involves creating a CA certificate and private key with req, a serial number file and an
       empty index file and placing them in the relevant directories.

       To use the sample configuration file below the directories demoCA, demoCA/private and demoCA/newcerts would be
       created. The CA certificate would be copied to demoCA/cacert.pem and its private key to demoCA/pri-
       vate/cakey.pem. A file demoCA/serial would be created containing for example "01" and the empty index file
       demoCA/index.txt.

       Sign a certificate request:

        openssl ca -in req.pem -out newcert.pem

       Sign a certificate request, using CA extensions:

        openssl ca -in req.pem -extensions v3_ca -out newcert.pem

       Generate a CRL

        openssl ca -gencrl -out crl.pem

       Sign several requests:

        openssl ca -infiles req1.pem req2.pem req3.pem

       Certify a Netscape SPKAC:

        openssl ca -spkac spkac.txt

       A sample SPKAC file (the SPKAC line has been truncated for clarity):

        SPKAC=MIG0MGAwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAn7PDhCeV/xIxUg8V70YRxK2A5
        CN=Steve Test
        emailAddress=steve AT openssl.org
        0.OU=OpenSSL Group
        1.OU=Another Group

       A sample configuration file with the relevant sections for ca:

        [ ca ]
        default_ca      = CA_default            # The default ca section

        [ CA_default ]

        dir            = ./demoCA              # top dir
        database       = $dir/index.txt        # index file.
        new_certs_dir  = $dir/newcerts         # new certs dir

        certificate    = $dir/cacert.pem       # The CA cert
        serial         = $dir/serial           # serial no file
        private_key    = $dir/private/cakey.pem# CA private key
        RANDFILE       = $dir/private/.rand    # random number file

        default_days   = 365                   # how long to certify for
        default_crl_days= 30                   # how long before next CRL
        default_md     = md5                   # md to use

        policy         = policy_any            # default policy
        email_in_dn    = no                    # Don't add the email into cert DN

        nameopt        = default_ca            # Subject name display option
        certopt        = default_ca            # Certificate display option
        copy_extensions = none                 # Don't copy extensions from request

        [ policy_any ]
        countryName            = supplied
        stateOrProvinceName    = optional
        organizationName       = optional
        organizationalUnitName = optional
        commonName             = supplied
        emailAddress           = optional

FILES
       Note: the location of all files can change either by compile time options, configuration file entries, envi-
       ronment variables or command line options.  The values below reflect the default values.

        /usr/local/ssl/lib/openssl.cnf - master configuration file
        ./demoCA                       - main CA directory
        ./demoCA/cacert.pem            - CA certificate
        ./demoCA/private/cakey.pem     - CA private key
        ./demoCA/serial                - CA serial number file
        ./demoCA/serial.old            - CA serial number backup file
        ./demoCA/index.txt             - CA text database file
        ./demoCA/index.txt.old         - CA text database backup file
        ./demoCA/certs                 - certificate output file
        ./demoCA/.rnd                  - CA random seed information

ENVIRONMENT VARIABLES
       OPENSSL_CONF reflects the location of master configuration file it can be overridden by the -config command
       line option.

RESTRICTIONS
       The text database index file is a critical part of the process and if corrupted it can be difficult to fix. It
       is theoretically possible to rebuild the index file from all the issued certificates and a current CRL: how-
       ever there is no option to do this.

       V2 CRL features like delta CRL support and CRL numbers are not currently supported.

       Although several requests can be input and handled at once it is only possible to include one SPKAC or self
       signed certificate.

BUGS
       The use of an in memory text database can cause problems when large numbers of certificates are present
       because, as the name implies the database has to be kept in memory.

       It is not possible to certify two certificates with the same DN: this is a side effect of how the text
       database is indexed and it cannot easily be fixed without introducing other problems. Some S/MIME clients can
       use two certificates with the same DN for separate signing and encryption keys.

       The ca command really needs rewriting or the required functionality exposed at either a command or interface
       level so a more friendly utility (perl script or GUI) can handle things properly. The scripts CA.sh and CA.pl
       help a little but not very much.

       Any fields in a request that are not present in a policy are silently deleted. This does not happen if the
       -preserveDN option is used. To enforce the absence of the EMAIL field within the DN, as suggested by RFCs,
       regardless the contents of the request' subject the -noemailDN option can be used. The behaviour should be
       more friendly and configurable.

       Cancelling some commands by refusing to certify a certificate can create an empty file.

WARNINGS
       The ca command is quirky and at times downright unfriendly.

       The ca utility was originally meant as an example of how to do things in a CA. It was not supposed to be used
       as a full blown CA itself: nevertheless some people are using it for this purpose.

       The ca command is effectively a single user command: no locking is done on the various files and attempts to
       run more than one ca command on the same database can have unpredictable results.

       The copy_extensions option should be used with caution. If care is not taken then it can be a security risk.
       For example if a certificate request contains a basicConstraints extension with CA:TRUE and the copy_exten-
       sions value is set to copyall and the user does not spot this when the certificate is displayed then this will
       hand the requestor a valid CA certificate.

       This situation can be avoided by setting copy_extensions to copy and including basicConstraints with CA:FALSE
       in the configuration file.  Then if the request contains a basicConstraints extension it will be ignored.

       It is advisable to also include values for other extensions such as keyUsage to prevent a request supplying
       its own values.

       Additional restrictions can be placed on the CA certificate itself.  For example if the CA certificate has:

        basicConstraints = CA:TRUE, pathlen:0

       then even if a certificate is issued with CA:TRUE it will not be valid.

SEE ALSO

openssl的证书格式转换

证书转换

PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准,PKCS 目前共发布过 15 个标准。 常用的有:
PKCS#7 Cryptographic Message Syntax Standard
PKCS#10 Certification Request Standard
PKCS#12 Personal Information Exchange Syntax Standard
X.509是常见通用的证书格式。所有的证书都符合为Public Key Infrastructure (PKI) 制定的 ITU-T X509 国际标准。
PKCS#7 常用的后缀是: .P7B .P7C .SPC
PKCS#12 常用的后缀有: .P12 .PFX
X.509 DER 编码(ASCII)的后缀是: .DER .CER .CRT
X.509 PAM 编码(Base64)的后缀是: .PEM .CER .CRT
.cer/.crt是用于存放证书,它是2进制形式存放的,不含私钥。
.pem跟crt/cer的区别是它以Ascii来表示。
pfx/p12用于存放个人证书/私钥,他通常包含保护密码,2进制方式
p10是证书请求
p7r是CA对证书请求的回复,只用于导入
p7b以树状展示证书链(certificate chain),同时也支持单个证书,不含私钥。

1. CA证书

用openssl创建CA证书的RSA密钥(PEM格式):

openssl genrsa -des3 -out ca.key 1024        

2. 创建CA证书有效期为一年

用openssl创建CA证书(PEM格式,假如有效期为一年):

openssl req -new -x509 -days 365 -key ca.key -out ca.crt -config openssl.cnf        

openssl是可以生成DER格式的CA证书的,最好用IE将PEM格式的CA证书转换成DER格式的CA证书。

3. x509转换为pfx

openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt     

4. PEM格式的ca.key转换为Microsoft可以识别的pvk格式

pvk -in ca.key -out ca.pvk -nocrypt -topvk        

5. PKCS#12 到 PEM 的转换

openssl pkcs12 -nocerts -nodes -in cert.p12 -out private.pem  
验证   
openssl pkcs12 -clcerts -nokeys -in cert.p12 -out cert.pem        

6. 从 PFX 格式文件中提取私钥格式文件 (.key)

openssl pkcs12 -in mycert.pfx -nocerts -nodes -out mycert.key     

7. 转换 pem 到到 spc

   openssl crl2pkcs7 -nocrl -certfile venus.pem  -outform DER -out venus.spc     

用 -outform -inform 指定 DER 还是 PAM 格式。例如:

openssl x509 -in Cert.pem -inform PEM -out cert.der -outform DER        

8. PEM 到 PKCS#12 的转换

openssl pkcs12 -export -in Cert.pem -out Cert.p12 -inkey key.pem        

IIS 证书

cd c:\openssl            set OPENSSL_CONF=openssl.cnf            
openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt                  

server.key和server.crt文件是Apache的证书文件,生成的server.pfx用于导入IIS

 

9. How to Convert PFX Certificate to PEM Format for SOAP

$ openssl pkcs12 -in test.pfx -out client.pem  
Enter Import Password:  MAC verified OK  Enter PEM pass phrase:  
Verifying - Enter PEM pass phrase:  

10. Private Key Convert to RSA

$ openssl rsa -in test.key -out test.rsa

Enter pass phrase for mail.xbcorp.com.key:
writing RSA key

 

OpenSSL 配置说明

########################
# OpenSSL 配置文件示范 #
########################
# [注意]这个示范文件并不是默认设置。
HOME = . # 默认的随机数种子文件,建议设置为 /dev/random 或 /dev/urandom
RANDFILE $ENV::HOME/.rnd # 扩展对象定义
# 比如,OpenSSL中并未定义X.509证书的扩展项,在使用到的时候就会从下面对扩展对象的定义中获取。
# 定义的方法有两种,第一种(反对使用)是存储在外部文件中,也就是这里"oid_file"变量定义的文件。
#oid_file = $ENV::HOME/.oid
# 第二种是存储在配置文件的一个字段中,也就是这里"oid_section"变量值所指定的字段。
oid_section new_oids new_oids ]
# 可以在这里添加扩展对象的定义,例如可以被’ca’和’req’使用。
# 格式如下:
# 对象简称 = 对象数字ID
req ] # 生成的证书中RSA密钥对的默认长度,取值是2的整数次方。建议使用4096以上。
default_bits 1024 # 保存生成的私钥文件的默认文件名
default_keyfile privkey.pem # 生成的私钥文件是否采用口令加密保护,可以设为yes或no。
encrypt_key yes # 读取输入私钥文件时的口令,如果未设置那么将会提示输入。
# input_password = secret
# 保存输出私钥文件时的口令,如果未设置那么将会提示输入。
# output_password = secret
default_md md5 # 为一些字段设置默认的字符串类型,比如证书请求中的城市和组织名称。可能的取值和解释如下:
# default: 包含了 PrintableString, T61String, BMPString 三种类型
# pkix  : 包含了 PrintableString, BMPString 两种类型
# utf8only: 只使用 UTF8 字符串。推荐使用这个,这样可以完美的包含任意字符。
# nombstr : 包含了 PrintableString, T61String 两种类型(不使用 BMPStrings 或 UTF8String 两种多字节字符类型)
string_mask nombstr # 如果设为yes,那么不管是命令行还是配置文件中的字符串都将按照UTF-8编码看待。默认值no表示仅使用ASCII编码。
utf8 no # 如果设为no,那么 req 指令将直接从配置文件中读取证书字段的信息,而不提示用户输入。
prompt yes # 定义输入用户信息选项的"特征名称"字段名,该扩展字段定义了多项用户信息。
distinguished_name req_distinguished_name # 定义证书请求属性的字段名,该扩展字段定义了证书请求的一些属性,但openssl的证书签发工具并不使用它们。
attributes req_attributes # 证书请求扩展的字段名,该扩展字段定义了要加入到证书请求中的一系列扩展项。
# req_extensions = v3_req
x509_extensions v3_ca ##### "特征名称"字段包含了用户的标识信息 #####
req_distinguished_name ]
countryName CN  #只能使用2字母的国家代码
stateOrProvinceName BeiJin
organizationName 
组织名
commonName 
公司名 ##### 证书请求属性字段定义了证书请求的一些属性(都不是必须的) #####
req_attributes ] ##### 要加入到证书请求中的一系列扩展项 #####
v3_req ]
basicConstraints CA:FALSE
keyUsage 
nonRepudiationdigitalSignaturekeyEncipherment #### 生成自签名证书时使用的证书扩展项 #####
### 因为这部分是非必须的,所以不翻译了,事实上你完全可以删除这部分内容
v3_ca ]
# PKIX recommendation.
subjectKeyIdentifier hash
authorityKeyIdentifier 
keyid:always,issuer:always # This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints CA:true # Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
ca ]
default_ca CA_default ##### 默认的CA主配置字段 #####
CA_default ] # 保存所有信息的文件夹,这个变量只是为了给后面的变量使用
dir = ./demoCA # 存放CA本身根证书的文件名
certificate $dir/cacert.pem # 存放CA自身私钥的文件名
private_key $dir/private/cakey.pem # 签发证书时使用的序列号文本文件,里面必须包含下一个可用的16进制数字。
serial $dir/serial # 存放新签发证书的默认目录,证书名就是该证书的系列号,后缀是.pem
new_certs_dir $dir/newcerts # 已生成的证书的默认保存目录
certs $dir/certs # 保存已签发证书的文本数据库文件,初始时为空。
database $dir/index.txt # 存放当前CRL编号的文件,对于v1版本的CRL则必须注释掉该行
crlnumber $dir/crlnumber # 当前CRL文件
crl $dir/crl.pem # 生成的证书撤销列表(CRL)的默认保存目录
crl_dir $dir/crl # 同一个subject是否只能创建一个证书,设为no表示可以创建多个
unique_subject yes # 签发新证书以及CRL时默认的摘要算法,可以使用:md5,md2,mdc2,sha1
default_md sha1 # 通常,证书签发的特种名称(DN)域的各个参数顺序与证书策略的参数顺序一致。
# 但是,如果这里设为yes则保持与证书请求中的参数顺序一致。
preserve no # 当用户需要确认签发证书时显示可读证书DN域的方式。可用值与 x509 指令的 -nameopt 选项相同。
name_opt ca_default
# 当用户需要确认签发证书时显示证书域的方式。
# 可用值与 x509 指令的 -certopt 选项相同,不过 no_signame 和 no_sigdump 总被默认设置。
cert_opt  ca_default # 新证书默认的生效日期,如果未设置则使用签发时的时间,格式为:YYMMDDHHNNSSZ(年月日时分秒Z)
# default_startdate = 080303223344Z
# 新证书默认的失效日期,格式为:YYMMDDHHNNSSZ(年月日时分秒Z)
# default_enddate = 090303223344Z
# 新签发的证书默认有效期,以天为单位
default_days 365 # 从当前CRL(证书撤销列表)到下次CRL发布的间隔天数
default_crl_days 30 # 是否将证书请求中的扩展项信息加入到证书扩展项中去。取值范围以及解释:
# none: 忽略所有证书请求中的扩展项
# copy: 将证书扩展项中没有的项目复制到证书中
# copyall: 将所有证书请求中的扩展项都复制过去,并且覆盖证书扩展项中原来已经存在的值。
copy_extensions none # 定义用于证书请求DN域匹配策略的字段,用于决定CA要求和处理证书请求提供的DN域的各个参数值的规则。
policy  policy_match # 定义X.509证书扩展项的字段。如果没有提供这个字段则生成X.509v1而不是v3格式的证书。
x509_extensions usr_cert # 定义生成CRL时需要加入的扩展项字段。如果没有定义则生成v1而不是v2版本的CRL。
# crl_extensions = crl_ext
policy_match ]
countryName  match
stateOrProvinceName 
match
organizationName 
match
commonName  
supplied
organizationalUnitName 
optional
emailAddress  
optional ### 下面的部分由于都是非必须的部分,因此也不翻译了。
### 事实上你完全可以从配置文件中删除这些内容
usr_cert ] # These extensions are added when ’ca’ signs a request. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
nsComment   "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier
=keyid,issuer # This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren’t
# deprecated according to PKIX.
# subjectAltName=email:move
crl_ext ] # CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
authorityKeyIdentifier=keyid:always,issuer:always proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
nsComment   "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier
=keyid,issuer:always # This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren’t
# deprecated according to PKIX.
# subjectAltName=email:move
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo 

########
# 语法 #
########
#
# 变量 = 值
#
# 语法很简单,一看就懂,但是有几点需要说明:
# 1. 字符串值最好使用双引号界定,并且其中可以使用"\n","\r","\t"这些转义序列("\"怎么表示?)。
# 2. 可以使用 ${变量名} 的形式引用同一字段中的变量,使用 ${字段名::变量名} 的形式引用其它字段中的变量。
# 3. 可以使用 ${EVP::环境变量} 的形式引用操作系统中定义的环境变量,若变量不存在则会导致错误。
# 4. 可以在默认字段定义与操作系统环境变量同名的变量作为默认值来避免环境变量不存在导致的错误。
# 5. 如果在同一字段内有多个相同名称的变量,那么后面的值将覆盖前面的值。
#

############
# 默认字段 #
############
# 此部分是默认字段[配置段],必须放在所有字段之前。
# 读取配置文件数据时,会首先根据字段名称去寻找相应的配置段,如果没有找到则会使用这里的默认字段。

# 定义 HOME 的默认值,防止操作系统中不存在 HOME 环境变量。
 

 

 

 

############################################################################################################
####################
##  证书请求配置  ##
####################
# 在申请证书之前通常需要首先生成符合 PKCS#10 标准的证书请求封装格式。
# openssl 的 req 指令实现了产生证书请求的功能,其相关选项的默认值就来自于这里的设置。
# 证书请求的配置分成几个字段,包括一个基本字段和几个附属字段。

##### 证书请求配置的"基本字段",其它附属字段都以它为入口 #####
 

 

 

 

 

# 签名默认使用的信息摘要算法,可以使用:md5,sha1,mdc2,md2
 

 

 

 

 

 

 

# 生成自签名证书时要使用的证书扩展项字段名,该扩展字段定义了要加入到证书中的一系列扩展项。
 

 

 

 

 

 

 

# Some might want this also
# nsCertType = sslCA, emailCA

# Include email address in subject alt name: another PKIX recommendation
# subjectAltName = email:copy
# Copy issuer details
# issuerAltName = issuer:copy

# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where ’obj’ is a standard or added object
# You can even override a supported extension:
# basicConstraints = critical, DER:30:03:01:01:FF

############################################################################################################
####################
##  证书签发配置  ##
####################
# openssl 的 ca 指令实现了证书签发的功能,其相关选项的默认值就来自于这里的设置。

# 这个字段只是通过唯一的default_ca变量来指定默认的CA主配置字段的入口(-name 命令行选项的默认值)
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

##### 证书请求信息的匹配策略 #####
# 变量名称是DN域对象的名称,变量值可以是:
# match: 该变量在证书请求中的值必须与CA证书相应的变量值完全相同,否则拒签。
# supplied: 该变量在证书请求中必须提供(值可以不同),否则拒签。
# optional: 该变量在证书请求中可以存在也可以不存在(相当于没有要求)。
# 除非preserve=yes或者在ca命令中使用了-preserveDN,否则在签发证书时将删除匹配策略中未提及的对象。
 

 

 

# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.

 

 

# This is OK for an SSL server.
# nsCertType   = server

# For an object signing certificate this would be used.
# nsCertType = objsign

# For normal client use this is typical
# nsCertType = client, email

# and for everything including object signing:
# nsCertType = client, email, objsign

# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment

# This will be displayed in Netscape’s comment listbox.
 

 

 

# Copy subject details
# issuerAltName=issuer:copy

#nsCaRevocationUrl  = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName

 

 

# issuerAltName=issuer:copy
 

 

# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.

 

 

# This is OK for an SSL server.
# nsCertType   = server

# For an object signing certificate this would be used.
# nsCertType = objsign

# For normal client use this is typical
# nsCertType = client, email

# and for everything including object signing:
# nsCertType = client, email, objsign

# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment

# This will be displayed in Netscape’s comment listbox.
 

 

 

# Copy subject details
# issuerAltName=issuer:copy

#nsCaRevocationUrl  = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName

# This really needs to be in place for it to be a proxy certificate

OpenSSL subjectAltName

参考:http://www.openssl.org/docs/apps/x509v3_config.html

 

SubjectAltNames

It is possible to arrange for a certificate to apply to more than one host (or Common Name) by using a certificate extension. Doing so requires

  • modifying the openssl configuration file
  • supplying the extra name information.

OpenSSL configuration file

I needed two modifications for the OpenSSL configuration file, /etc/ssl/openssl.cnf on my Ubuntu laptop. (You could modify a copy of the file and specify that on the command line, but I was lazy.)

First, enable the extensions:

[req]
req_extensions = v3_req

(In the req section, this line should already exist, but be commented out.)

Second, add an entry in the v3_req section to collect the alternative names. I set it up to read from an environment variable:

[ v3_req ]
subjectAltName=$ENV::ALTNAME

This requires the ALTNAME environment variable to be set to something meaningful every time the command is used, so it may just be easier to set the values in the file. The syntax for doing so is:

subjectAltName="DNS:<host1>,DNS:<host>" 

Or, a new section could be used:

subjectAltName=@alt_names

[alt_names]
DNS.1 = <host1>
DNS.2 = <host2>

Generating the certificate

To generate the self-signed certificate, use the (much more complex) command:

ALTNAME="DNS:<host1>,DNS:<host2>" \
  openssl req -new -x509 -key key.pem -out cert.pem -days 365 \
  -config /etc/ssl/openssl.cnf -extensions v3_req

The ALTNAME environment variable supplies the additional host names to be used in the SubjectAltName extension to the certificate. The additional -config and -extensions arguments are needed to get openssl req to read and used the extension configurations.

Theoretically, the canonical name (the hostname used for the CN of the certificate) should not be needed in the list of alternative names. However, when I tried omitting the canonical name from the alternative name list, Firefox did not allow the certificate to be used with the canonical name because it did not belong, as if the subjectAltNames extension was overriding the CN. According to all the information I have seen, it hurts nothing to add the CN to the subjectAltNames list, and may be necessary depending on whether I was or was not doing something wrong.

基于 OpenSSL 的 CA 建立及证书签发

建立 CA

建立 CA 目录结构

按照 OpenSSL 的默认配置建立 CA ,需要在文件系统中建立相应的目录结构。相关的配置内容一般位于 /usr/ssl/openssl.cnf 内,详情可参见 config (1) 。在终端中使用如下命令建立目录结构:

$ mkdir -p ./demoCA/{private,newcerts}
$ touch ./demoCA/index.txt
$ echo 01 > ./demoCA/serial

产生的目录结构如下:

.
`-- demoCA/
    |-- index.txt
    |-- newcerts/
    |-- private/
    `-- serial

生成 CA 证书的 RSA 密钥对

首先,我们要为 CA 建立 RSA 密钥对。打开终端,使用如下命令生成 RSA 密钥对:

$ openssl genrsa -des3 -out ./demoCA/private/cakey.pem 2048

参数解释

genrsa

用于生成 RSA 密钥对的 OpenSSL 命令。

-des3

使用 3-DES 对称加密算法加密密钥对,该参数需要用户在密钥生成过程中输入一个口令用于加密。今后使用该密钥对时,需要输入相应的口令。如果不加该选项,则不对密钥进行加密。

-out ./demoCA/private/cakey.pem

令生成的密钥对保存到文件 ./demoCA/private/cakey.pem

2048

RSA 模数位数,在一定程度上表征了密钥强度。

该命令输出如下,用户应输入自己的密钥口令并确认:

Generating RSA private key, 2048 bit long modulus
................................................+++
.........................+++
e is 65537 (0x10001)
Enter pass phrase for ./demoCA/private/cakey.pem:<enter your pass-phrase>Verifying - Enter pass phrase for ./demoCA/private/cakey.pem:<re-enter your pass-phrase>

生成 CA 证书请求

为了获取一个 CA 根证书,我们需要先制作一份证书请求。先前生成的 CA 密钥对被用于对证书请求签名。

$ openssl req -new -days 365 -key ./demoCA/private/cakey.pem -out careq.pem

参数解释

req

用于生成证书请求的 OpenSSL 命令。

-new

生成一个新的证书请求。该参数将令 OpenSSL 在证书请求生成过程中要求用户填写一些相应的字段。

-days 365

从生成之时算起,证书时效为 365 天。

-key ./demoCA/private/cakey.pem

指定 ./demoCA/private/cakey.pem 为证书所使用的密钥对文件。

-out careq.pem

令生成的证书请求保存到文件 careq.pem

该命令将提示用户输入密钥口令并填写证书相关信息字段,输出如下:

Enter pass phrase for ./demoCA/private/cakey.pem:<enter you pass-phrase>
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:ZJ
Locality Name (eg, city) []:HZ
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Some Ltd. Corp.
Organizational Unit Name (eg, section) []:Some Unit
Common Name (eg, YOUR name) []:Someone
Email Address []:some@email.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

对 CA 证书请求进行签名

在实际应用中,用户可以通过向知名 CA 递交证书请求来申请证书。但是在这里,我们需要建立的是一个根 CA ,只能由我们自己来对证书请求进行签名。所以我们让 OpenSSL 使用证书请求中附带的密钥对对该请求进行签名,也就是所谓的“ self sign ”:

$ openssl ca -selfsign -in careq.pem -out cacert.pem

参数解释

ca

用于执行 CA 相关操作的 OpenSSL 命令。

-selfsign

使用对证书请求进行签名的密钥对来签发证书。

-in careq.pem

指定 careq.pem 为证书请求文件。

-out ./demoCA/cacert.pem

指定 ./demoCA/cacert.pem 为输出的证书。

该命令要求用户输入密钥口令并输出相关证书信息,请求用户确认:

Using configuration from /usr/lib/ssl/openssl.cnf
Enter pass phrase for ./demoCA/private/cakey.pem:<enter your pass-phrase>
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Jan 16 13:05:09 2008 GMT
            Not After : Jan 15 13:05:09 2009 GMT
        Subject:
            countryName = CN
            stateOrProvinceName = ZJ
            organizationName = Some Ltd. Corp.
            organizationalUnitName = Some Unit
            commonName = Someone
            emailAddress = some@email.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                75:F5:3C:CC:C1:5E:6D:C3:8B:46:A8:08:E6:EA:29:E8:22:7E:70:03
            X509v3 Authority Key Identifier:
                keyid:75:F5:3C:CC:C1:5E:6D:C3:8B:46:A8:08:E6:EA:29:E8:22:7E:70:03

Certificate is to be certified until Jan 15 13:05:09 2009 GMT (365 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

一步完成 CA 证书请求生成及签名

以上两个步骤可以合二为一。利用 ca 命令的 -x509 参数,通过以下命令同时完成证书请求生成和签名从而生成 CA 根证书:

$ openssl req -new -x509 -days 365 -key ./demoCA/private/cakey.pem -out ./demoCA/cacert.pem

参数解释

req

用于生成证书请求的 OpenSSL 命令。

-new

生成一个新的证书请求。该参数将令 OpenSSL 在证书请求生成过程中要求用户填写一些相应的字段。

-x509

生成一份 X.509 证书。

-days 365

从生成之时算起,证书时效为 365 天。

-key ./demoCA/private/cakey.pem

指定 cakey.pem 为证书所使用的密钥对文件。

-out ./demoCA/cacert.pem

令生成的证书保存到文件 ./demoCA/cacert.pem

该命令输出如下,用户应输入相应的字段:

Enter pass phrase for ./demoCA/private/cakey.pem:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:ZJ
Locality Name (eg, city) []:HZ
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Some Ltd. Corp.
Organizational Unit Name (eg, section) []:Some Unit
Common Name (eg, YOUR name) []:Someone
Email Address []:some@email.com

至此,我们便已成功建立了一个私有根 CA 。在这个过程中,我们获得了一份 CA 密钥对文件 ./demoCA/private/cakey.pem 以及一份由此密钥对签名的 CA 根证书文件 ./demoCA/cacert.pem ,得到的 CA 目录结构如下:

.

|-- careq.pem

`-- demoCA/

    |-- cacert.pem
    |-- index.txt


    |-- index.txt.attr

    |-- index.txt.old

    |-- newcerts/

    |   `-- 01.pem

    |-- private/

    |   `-- cakey.pem

    |-- serial

    `-- serial.old

注:如果在 CA 建立过程中跳过证书请求生成的步骤,则不会产生 careq.pem 文件。

签发证书

下面我们就可以利用建立起来的 CA 进行证书签发了。

生成用户证书 RSA 密钥对

参照 CA 的 RSA 密钥对生成过程,使用如下命令生成新的密钥对:

$ openssl genrsa -des3 -out userkey.pem
Generating RSA private key, 512 bit long modulus
....++++++++++++
...++++++++++++
e is 65537 (0x10001)
Enter pass phrase for userkey.pem:<enter your pass-phrase>
Verifying - Enter pass phrase for userkey.pem:<re-enter your pass-phrase>

生成用户证书请求

参照 CA 的证书请求生成过程,使用如下命令生成新的证书请求:

$ openssl req -new -days 365 -key userkey.pem -out userreq.pem
Enter pass phrase for userkey.pem:<enter your pass-phrase>
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:ZJ
Locality Name (eg, city) []:HZ
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Some Ltd. Corp.
Organizational Unit Name (eg, section) []:Some Other Unit
Common Name (eg, YOUR name) []:Another
Email Address []:another@email.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

签发用户证书

现在,我们可以用先前建立的 CA 来对用户的证书请求进行签名来为用户签发证书了。使用如下命令:

$ openssl ca -in userreq.pem -out usercert.pem

参数解释

ca

用于执行 CA 相关操作的 OpenSSL 命令。

-in userreq.pem

指定用户证书请求文件为 userreq.pem

-out usercert.pem

指定输出的用户证书文件为 usercert.pem

该命令要求用户输入密钥口令并输出相关证书信息,请求用户确认:

Using configuration from /usr/lib/ssl/openssl.cnf
Enter pass phrase for ./demoCA/private/cakey.pem:<enter your pass-phrase>
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Jan 16 14:50:22 2008 GMT
            Not After : Jan 15 14:50:22 2009 GMT
        Subject:
            countryName               = CN
            stateOrProvinceName       = ZJ
            organizationName          = Some Ltd. Corp.
            organizationalUnitName    = Some Other Unit
            commonName                = Another
            emailAddress              = another@email.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                97:E7:8E:84:B1:45:27:83:94:A0:DC:24:79:7B:83:97:99:0B:36:A9
            X509v3 Authority Key Identifier:
                keyid:D9:87:12:94:B2:20:C7:22:AB:D4:D5:DF:33:DB:84:F3:B0:4A:EC:A2

Certificate is to be certified until Jan 15 14:50:22 2009 GMT (365 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

至此,我们便完成了 CA 的建立及用户证书签发的全部工作。不妨把所有 shell 命令放到一起纵览一下:

# 建立 CA 目录结构
mkdir -p ./demoCA/{private,newcerts}
touch ./demoCA/index.txt
echo 01 > ./demoCA/serial

# 生成 CA 的 RSA 密钥对
openssl genrsa -des3 -out ./demoCA/private/cakey.pem 2048

# 生成 CA 证书请求
openssl req -new -days 365 -key ./demoCA/private/cakey.pem -out careq.pem

# 自签发 CA 证书
openssl ca -selfsign -in careq.pem -out ./demoCA/cacert.pem

# 以上两步可以合二为一
openssl req -new -x509 -days 365 -key ./demoCA/private/cakey.pem -out ./demoCA/cacert.pem

# 生成用户的 RSA 密钥对
openssl genrsa -des3 -out userkey.pem

# 生成用户证书请求
openssl req -new -days 365 -key userkey.pem -out userreq.pem

# 使用 CA 签发用户证书
openssl ca -in userreq.pem -out usercert.pem

# 吊销证书, 其中crlexts 指定是否在吊销证书中显示更新点
openssl ca -gencrl -crldays 365 -crlexts crl_ext -out %CRL_PATH% -config %DIR%openssl.cnf

了解了这些基础步骤之后,就可以通过脚本甚至 makefile 的方式来将这些工作自动化。 CA.plCA.sh 便是对 OpenSSL 的 CA 相关功能的简单封装,在 Debian 系统中,安装了 OpenSSL 后,可以在 /usr/lib/ssl/misc/ 目录下找到这两个文件。而 makefile 的解决方案则可以参考这里

参考文献

Sphinx索引

Sphinx是一个基于SQL的全文检索引擎,可以结合MySQL,PostgreSQL做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。Sphinx特别为一些脚本语言设计搜索API接口,如PHP,Python,Perl,Ruby等,同时为MySQL也设计了一个存储引擎插件。

Sphinx 单一索引最大可包含1亿条记录,在1千万条记录情况下的查询速度为0.x秒(毫秒级)。Sphinx创建索引的速度为:创建100万条记录的索引只需 3~4分钟,创建1000万条记录的索引可以在50分钟内完成,而只包含最新10万条记录的增量索引,重建一次只需几十秒。

点击查看原图

Sphinx的主要特性包括:

高速索引 (在新款CPU上,近10 MB/秒);
高速搜索 (2-4G的文本量中平均查询速度不到0.1秒);
高可用性 (单CPU上最大可支持100 GB的文本,100M文档);
提供良好的相关性排名
支持分布式搜索;
提供文档摘要生成;
提供从MySQL内部的插件式存储引擎上搜索
支持布尔,短语, 和近义词查询;
支持每个文档多个全文检索域(默认最大32个);
支持每个文档多属性;
支持断词;
支持单字节编码与UTF-8编码;

 

---------------------------------------------------------------

 

1.为什么要使用Sphinx

假设你现在运营着一个论坛,论坛数据已经超过100W,很多用户都反映论坛搜索的速度非常慢,那么这时你就可以考虑使用Sphinx了(当然其他的全文检索程序或方法也行)。

2.Sphinx是什么

Sphinx由俄罗斯人Andrew Aksyonoff 开发的高性能全文搜索软件包,在GPL与商业协议双许可协议下发行。
全文检索是指以文档的全部文本信息作为检索对象的一种信息检索技术。检索的对象有可能是文章的标题,也有可能是文章的作者,也有可能是文章摘要或内容。

3.Sphinx的特性

高速索引 (在新款CPU,10 MB/);l
高速搜索 (2-4G的文本量中平均查询速度不到0.1);l
高可用性l (CPU上最大可支持100 GB的文本,100M文档);
提供良好的相关性排名
l
支持分布式搜索;l
提供文档摘要生成;l
提供从MySQL内部的插件式存储引擎上搜索l
支持布尔,短语,l 和近义词查询;
支持每个文档多个全文检索域(默认最大32);
l
支持每个文档多属性;l
支持断词;l
支持单字节编码与UTF-8编码;l

4.下载并安装Sphinx

打开网址http://www.coreseek.cn/news/7/52/ 找到适合自己的操作系统的版本,比如我是Windows那么我就可以下载Coreseek Win32通用版本,Linux下可以下载源码包,自己编译安装。这里解释下为什么我们下载的程序叫CoreseekCoreseek是基于 Sphinx开发的一款软件,对Sphinx做了一些改动,在中文方面支持得比Sphinx好,所以我们使用之。
下载完成后,将程序解压到你想解压的地方,比如我就想解压到E盘根目录,之后修改目录名为Coreseek,大功告成Coreseek安装完成了,安装的目录是在E:\coreseek\

5.使用Sphinx

我要使用Sphinx需要做以下几件事
1)
首先得有数据
2)
建立Sphinx配置文件
3)
生成索引
4)
启动Sphinx
5)
使用之(调用apisearch.exe程序进行查询)

1件:(导入数据)
我们建立测试所需要用到得数据库、表以及数据,篇幅有限,这些在附件中都有,下载后导入MySQL即可。

2件:(建立配置文件)
接下来我们需要建立一个Sphinx的配置文件 E:\coreseek\etc\mysql.conf,将其内容改为下面这些:
source mysql
{
type = mysql
sql_host = localhost
sql_user = root
sql_pass =
sql_db = test
sql_port = 3306
sql_query_pre = SET NAMES utf8
sql_query = SELECT id,addtime,title,content FROM post
sql_attr_timestamp = addtime
}

index mysql
{
source = mysql
path = E:/coreseek/var/data/mysql
charset_dictpath = E:/coreseek/etc/
charset_type = zh_cn.utf-8
}

searchd
{
listen = 9312
max_matches = 1000
pid_file = E:/coreseek/var/log/searchd_mysql.pid
log = E:/coreseek/var/log/searchd_mysql.log
query_log = E:/coreseek/var/log/query_mysql.log
}

先讲下这个配置文件中每项的含义。
source mysql{}
定义源名称为mysql,也可以叫其他的,比如:
source xxx{}
type
数据源类型

sql_*
数据相关的配置,比如sql_host,sql_pass什么的,这些不解释鸟
sql_query
建立索引时的查询命令,在这里尽可能不使用wheregroup by,将wheregroupby的内容交给sphinx,由sphinx进行条件过滤与groupby效率会更高,注意:select 的字段必须包括一个唯一主键以及要全文检索的字段,where中要用到的字段也要select出来
sql_query_pre
在执行sql_query前执行的sql命令, 可以有多条
sql_attr
以这个开头的配置项,表示属性字段,在where,orderby,groupby中出现的字段要分别定义一个属性,定义不同类型的字段要用不同的属性名,比如上面的sql_attr_timestamp就是时间戳类型。

index mysql{} 定义索引名称为mysql,也可以叫其他的,比如:index xxx{}
source
关联源,就是source xxx定义的。

path
索引文件存放路径,比如:E:/coreseek/var/data/mysql 实际存放在E:/coreseek/var/data/目录,然后创建多个名称为mysql后缀却不同的索引文件
charset_dictpath
指明分词法读取词典文件的位置,当启用分词法时,为必填项。在使用LibMMSeg作为分词库时,需要确保词典文件uni.lib在指定的目录下
charset_type
字符集,比如charset_type = zh_cn.gbk

searchd{} sphinx守护进程配置
listen
监听端口
max_matches
最大匹配数,也就是查找的数据再多也只返回这里设置的1000
pid_file pid
文件路径
log
全文检索日志
query_log
查询日志

好了,配置文件就这样,配置的参数还有很多,大家可以自己查文档。

3件:(生成索引)
开始 -> 运行 -> 输入cmd回车,打开命令行工具
e:\coreseek\bin\indexer --config e:\coreseek\etc\mysql.conf --all
这一串东西其实就是调用indexer程序来生成所有索引

如果只想对某个数据源进行索引,则可以这样:e:\coreseek\bin\indexer --config e:\coreseek\etc\mysql.conf 索引名称(索引名称指配置文件中所定义的)
--config,--all
这些都是indexer程序的参数,想了解更多参数的朋友可以查看文档
运行命令后如果你没看到FATAL,ERROR这些东西,那么索引文件就算生成成功了,比如我看到得就是
………
省略………
using config file 'e:\coreseek\etc\mysql.conf'...
indexing index 'mysql'...
collected 4 docs, 0.0 MB
………
省略………

4件:(启动Sphinx
同样命令行下
e:\coreseek\bin\searchd --config e:\coreseek\etc\mysql.conf
运行后提示了一大堆东西
using config file 'e:\coreseek\etc\mysql.conf'...
listening on all interfaces, port=9312
accepting connections
不用管这些鸟文是啥意思,反正Sphinx是启动好了。
现在有一串鸟文的这个命令行是不能关的,因为关了Sphinx也就关了,如果觉得这样不爽,可以将Sphinx安装成系统服务,在后台运行。
安装系统服务只需在命令行中输入以下命令
e:\coreseek\bin\searchd --config e:\coreseek\etc\mysql.conf --install
安装之后记得启动这个服务,不会启动那我没法,自己google

5步:(使用Sphinx
web根目录下建立一个search目录(当然不在根目录也行,同样目录名也可以随取),复制E:\coreseek\api\ sphinxapi.php文件到search目录(sphinxapi.php这个是sphinx官方提供的api),开始php程序的编写。
search目录建立一个文件,名字叫啥都行,我管它叫index.php,其内容如下
<?php

include 'sphinxapi.php'; // 加载Sphinx API

$sc = new SphinxClient(); // 实例化Api
$sc->setServer('localhost', 9312); //
设置服务端,第一个参数sphinx服务器地址,第二个sphinx监听端口

$res = $sc->query('sphinx', 'mysql'); //
执行查询, 第一个参数查询的关键字,第二个查询的索引名称,mysql索引名称(这个也是在配置文件中定义的),多个索引名称以,分开,也可以用*表示所有索引。
print_r($res);

打印结果:
Array
(
  ………
省略………
  [matches] => Array
  (
  [2] => Array
  (
  [weight] => 2
  [attrs] => Array
  (
  [addtime] => 1282622004
  )
  )
  [4] => Array
  (
  [weight] => 2
  [attrs] => Array
  (
  [addtime] => 1282622079
  )
  )
  )
  ………
省略
………
)
Matches
中就是查询的结果了,但是仿佛不是我们想要的数据,比如titilecontent字段的内容就没有查询出来,根据官方的说明是Sphinx并没有连接到MySQL去取数据,只是根据它自己的索引内容进行计算,因此如果想用Sphinx提供的API去取得我们想要的数据,还必须以查询的结果为依据,再次查询MySQL从而得到我们想要的数据。

查询结果中键值分别表示

2唯一主键

weight权重
attrs sql_attr_*
中配置
至此,搜索引擎算是完成一大半了,剩下的大家可以自行完成。

比如:

<?php
$ids = array_keys($res['matches']); //
获取主键
$ids = join(',', $ids);
$query = mysql_query("SELECT * FROM post WHERE id IN ({$ids})");
while($row = mysql_fetch_assoc($query)) {
  .....
}

Sphinx的更多配置,程序的参数等,大家可以查看Sphinx的文档。

 

 

构建实时索引

所谓事务性,本多指数据库的属性,包括ACID四个基本要素:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

我们这里主要讨论隔离性,Lucene的IndexReader和IndexWriter具有隔离性。

  • 当IndexReader.open打开一个索引的时候,相对于给当前索引进行了一次snapshot,此后的任何修改都不会被看到。
  • 仅当IndexReader.open打开一个索引后,才有可能看到从上次打开后对索引的修改。
  • 当IndexWriter没有调用Commit的时候,其修改的内容是不能够被看到的,哪怕IndexReader被重新打开。
  • 欲使最新的修改被看到,一方面IndexWriter需要commit,一方面IndexReader重新打开。

下面我们举几个例子来说明上述隔离性:

(1) 首先做准备,索引十篇文档

File indexDir = new File("TestIsolation/index");

IndexWriter writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED);

for(int i =0; i < 10; i++){

  indexDocs(writer);

}

writer.close();

(2) 然后再索引十篇文档,并不commit

writer = new IndexWriter(FSDirectory.open(indexDir), new StandardAnalyzer(Version.LUCENE_CURRENT), IndexWriter.MaxFieldLength.LIMITED);

for(int i =0; i < 10; i++){

  indexDocs(writer);

}

(3) 打开一个IndexReader,但是由于IndexWriter没有commit,所以仍然仅看到十篇文档。

IndexReader reader = IndexReader.open(FSDirectory.open(indexDir));

IndexSearcher searcher = new IndexSearcher(reader);

TopDocs docs = searcher.search(new TermQuery(new Term("contents","hello")), 50);

System.out.println(docs.totalHits);

(4) IndexWriter进行提交commit

writer.commit();

(5) 不重新打开IndexReader,进行搜索,仍然仅看到十篇文档。

docs = searcher.search(new TermQuery(new Term("contents","hello")), 50);

System.out.println(docs.totalHits);

(6) IndexReader重新打开,则可以看到二十篇文档。

reader = IndexReader.open(FSDirectory.open(indexDir));

searcher = new IndexSearcher(reader);

docs = searcher.search(new TermQuery(new Term("contents","hello")), 50);

System.out.println(docs.totalHits);

由于前一章所述的Lucene的事务性,使得Lucene可以增量的添加一个段,我们知道,倒排索引是有一定的格式的,而这个格式一旦写入是非常难以改变的,那么如何能够增量建索引呢?Lucene使用段这个概念解决了这个问题,对于每个已经生成的段,其倒排索引结构不会再改变,而增量添加的文档添加到新的段中,段之间在一定的时刻进行合并,从而形成新的倒排索引结构。

然而也正因为Lucene的事务性,使得Lucene的索引不够实时,如果想Lucene实时,则必须新添加的文档后IndexWriter需要commit,在搜索的时候IndexReader需要重新的打开,然而当索引在硬盘上的时候,尤其是索引非常大的时候,IndexWriter的commit操作和IndexReader的open操作都是非常慢的,根本达不到实时性的需要。

好在Lucene提供了RAMDirectory,也即内存中的索引,能够很快的commit和open,然而又存在如果索引很大,内存中不能够放下的问题。

所以要构建实时的索引,就需要内存中的索引RAMDirectory和硬盘上的索引FSDirectory相互配合来解决问题。

1、初始化阶段

首先假设我们硬盘上已经有一个索引FileSystemIndex,由于IndexReader打开此索引非常的慢,因而其是需要事先打开的,并且不会时常的重新打开。

我们在内存中有一个索引MemoryIndex,新来的文档全部索引到内存索引中,并且是索引完IndexWriter就commit,IndexReader就重新打开,这两个操作时非常快的。

如下图,则此时新索引的文档全部能被用户看到,达到实时的目的。

点击查看原图

2、合并索引阶段

然而经过一段时间,内存中的索引会比较大了,如果不合并到硬盘上,则可能造成内存不够用,则需要进行合并的过程。

当然在合并的过程中,我们依然想让我们的搜索是实时的,这是就需要一个过渡的索引,我们称为MergingIndex。

一旦内存索引达到一定的程度,则我们重新建立一个空的内存索引,用于合并阶段索引新的文档,然后将原来的内存索引称为合并中索引,并启动一个后台线程进行合并的操作。

在合并的过程中,如果有查询过来,则需要三个IndexReader,一个是内存索引的IndexReader打开,这个过程是很快的,一个是合并中索引的IndexReader打开,这个过程也是很快的,一个是已经打开的硬盘索引的IndexReader,无需重新打开。这三个IndexReader可以覆盖所有的文档,唯一有可能重复的是,硬盘索引中已经有一些从合并中索引合并过去的文档了,然而不用担心,根据Lucene的事务性,在硬盘索引的IndexReader没有重新打开的情况下,背后的合并操作它是看不到的,因而这三个IndexReader所看到的文档应该是既不少也不多。合并使用IndexWriter(硬盘索引).addIndexes(IndexReader(合并中索引)),合并结束后Commit。

如下图:

点击查看原图

3、重新打开硬盘索引的IndexReader

当合并结束后,是应该重新打开硬盘索引的时候了,然而这是一个可能比较慢的过程,在此过程中,我们仍然想保持实时性,因而在此过程中,合并中的索引不能丢弃,硬盘索引的IndexReader也不要动,而是为硬盘索引打开一个临时的IndexReader,在打开的过程中,如果有搜索进来,返回的仍然是上述的三个IndexReader,仍能够不多不少的看到所有的文档,而将要打开的临时的IndexReader将能看到合并中索引和原来的硬盘索引所有的文档,此IndexReader并不返回给客户。如下图:

点击查看原图

4、替代IndexReader

当临时的IndexReader被打开的时候,其看到的是合并中索引的IndexReader和硬盘索引原来的IndexReader之和,下面要做的是:

(1) 关闭合并中索引的IndexReader

(2) 抛弃合并中索引

(3) 用临时的IndexReader替换硬盘索引原来的IndexReader

(4) 关闭硬盘索引原来的IndexReader。

上面说的这几个操作必须是原子性的,如果做了(2)但没有做(3),如果来一个搜索,则将少看到一部分数据,如果做了(3)没有做(2)则,多看到一部分数据。

所以在进行上述四步操作的时候,需要加一个锁,如果这个时候有搜索进来的时候,或者在完全没有做的时候得到所有的IndexReader,或者在完全做好的时候得到所有的IndexReader,这时此搜索可能被block,但是没有关系,这四步是非常快的,丝毫不影响替代性。

如下图:

点击查看原图

经过这几个过程,又达到了第一步的状态,则进行下一个合并的过程。

5、多个索引

有一点需要注意的是,在上述的合并过程中,新添加的文档是始终添加到内存索引中的,如果存在如下的情况,索引速度实在太快,在合并过程没有完成的时候,内存索引又满了,或者硬盘上的索引实在太大,合并和重新打开要花费太长的时间,使得内存索引以及满的情况下,还没有合并完成。

为了处理这种情况,我们可以拥有多个合并中的索引,多个硬盘上的索引,如下图:

点击查看原图

  • 新添加的文档永远是进入内存索引
  • 当内存索引到达一定的大小的时候,将其加入合并中索引链表
  • 有一个后台线程,每隔一定的时刻,将合并中索引写入一个新的硬盘索引中取。这样可以避免由于硬盘索引过大而合并较慢的情况。硬盘索引的IndexReader也是写完并重新打开后才替换合并中索引的IndexReader,新的硬盘索引也可保证打开的过程不会花费太长时间。
  • 这样会造成硬盘索引很多,所以,每隔一定的时刻,将硬盘索引合并成一个大的索引。也是合并完成后方才替换IndexReader

大家可能会发现,此合并的过程和Lucene的段的合并很相似。然而Lucene的一个函数IndexReader.reopen一直是没有实现的,也即我们不能选择哪个段是在内存中的,可以被打开,哪些是硬盘中的,需要在后台打开然后进行替换,而IndexReader.open是会打开所有的内存中的和硬盘上的索引,因而会很慢,从而降低了实时性。

在有关Lucene的问题(7),讨论了使用Lucene内存索引和硬盘索引构建实时索引的问题。

然而有的读者提到,如果涉及到文档的删除及更新,那么如何构建实时的索引呢?本节来讨论这个问题。

1、Lucene删除文档的几种方式

  • IndexReader.deleteDocument(int docID)是用 IndexReader 按文档号删除。  
  • IndexReader.deleteDocuments(Term  term)是用 IndexReader 删除包含此词(Term)的文档。  
  • IndexWriter.deleteDocuments(Term  term)是用 IndexWriter 删除包含此词(Term)的文档。  
  • IndexWriter.deleteDocuments(Term[]  terms)是用 IndexWriter 删除包含这些词(Term)的文档。  
  • IndexWriter.deleteDocuments(Query  query)是用 IndexWriter 删除能满足此查询(Query)的文档。  
  • IndexWriter.deleteDocuments(Query[] queries)是用 IndexWriter 删除能满足这些查询(Query)的文档。

删除文档既可以用reader进行删除,也可以用writer进行删除,不同的是,reader进行删除后,此reader马上能够生效,而用writer删除后,会被缓存,只有写入到索引文件中,当reader再次打开的时候,才能够看到。

2、Lucene文档更新的几个问题

2.1、使用IndexReader还是IndexWriter进行删除

既然IndexReader和IndexWriter都能够进行文档删除,那么到底是应该用哪个来进行删除呢?

本文的建议是,用IndexWriter来进行删除。

因为用IndexReader可能存在以下的问题:

(1) 当有一个IndexWriter打开的时候,IndexReader的删除操作是不能够进行的,否则会报LockObtainFailedException

(2) 当IndexReader被多个线程使用的时候,一个线程用其进行删除,会使得另一个线程看到的索引有所改变,使得另一个线程的结果带有不确定性。

(3) 对于更新操作,在Lucene中是先删除,再添加的,然而删除的被立刻看到的,而添加却不能够立刻看到,造成了数据的不一致性。

(4) 即便以上问题可以通过锁来解决,然而背后的操作影响到了搜索的速度,是我们不想看到的。

2.2、如何在内存中缓存文档的删除

在上一节中,为了能够做到实时性,我们使用内存中的索引,而硬盘上的索引则不经常打开,即便打开也在背后线程中打开。

而要删除的文档如果在硬盘索引中,如果不重新打开则看不到新的删除,则需要将删除的文档缓存到内存中。

那如何将缓存在内存中的文档删除在不重新打开IndexReader的情况下应用于硬盘上的索引呢?

在Lucene中,有一种IndexReader为FilterIndexReader,可以对一个IndexReader进行封装,我们可以实现一个自己的FilterIndexReader来过滤掉删除的文档。

一个例子如下:

public class MyFilterIndexReader extends FilterIndexReader {

  OpenBitSet dels;

  public MyFilterIndexReader(IndexReader in) {

    super(in);

    dels = new OpenBitSet(in.maxDoc());

  }

  public MyFilterIndexReader(IndexReader in, List<String> idToDelete) throws IOException {

    super(in);

    dels = new OpenBitSet(in.maxDoc());

    for(String id : idToDelete){

      TermDocs td = in.termDocs(new Term("id", id)); //如果能在内存中Cache从Lucene的ID到应用的ID的映射,Reader的生成将快得多。

      if(td.next()){

        dels.set(td.doc());

      }

    }

  }

  @Override

  public int numDocs() {

    return in.numDocs() - (int) dels.cardinality();

  }

  @Override

  public TermDocs termDocs(Term term) throws IOException {

    return new FilterTermDocs(in.termDocs(term)) {

      @Override

      public boolean next() throws IOException {

        boolean res;

        while ((res = super.next())) {

          if (!dels.get(doc())) {

            break;

          }

        }

        return res;

      }

    };

  }

  @Override

  public TermDocs termDocs() throws IOException {

    return new FilterTermDocs(in.termDocs()) {

      @Override

      public boolean next() throws IOException {

        boolean res;

        while ((res = super.next())) {

          if (!dels.get(doc())) {

            break;

          }

        }

        return res;

      }

    };

  }

}

2.3、文档更新的顺序性问题

Lucene的文档更新其实是删除旧的文档,然后添加新的文档。如上所述,删除的文档是缓存在内存中的,并通过FilterIndexReader应用于硬盘上的索引,然而新的文档也是以相同的id加入到索引中去的,这就需要保证缓存的删除不会将新的文档也过滤掉,将缓存的删除合并到索引中的时候不会将新的文档也删除掉。

Lucene的两次更新一定要后一次覆盖前一次,而不能让前一次覆盖后一次。

所以内存中已经硬盘中的多个索引是要被保持一个顺序的,哪个是老的索引,哪个是新的索引,缓存的删除自然是应该应用于所有比他老的索引的,而不应该应用于他自己以及比他新的索引。

3、具有更新功能的Lucene实时索引方案

3.1、初始化

首先假设我们硬盘上已经有一个索引FileSystemIndex,被事先打开的,其中包含文档1,2,3,4,5,6。

我们在内存中有一个索引MemoryIndex,新来的文档全部索引到内存索引中,并且是索引完IndexWriter就commit,IndexReader就重新打开,其中包含文档7,8。

点击查看原图

3.2、更新文档5

这时候来一个新的更新文档5, 需要首先将文档5删除,然后加入新的文档5。

需要做的事情是:

  • 首先在内存索引中删除文档5,当然没有文档5,删除无效。
  • 其次将对文档5的删除放入内存文档删除列表,并与硬盘的IndexReader组成FilterIndexReader
  • 最后,将新的文档5加入内存索引,这时候,用户可以看到的就是新的文档5了。
  • 将文档5放入删除列表以及将文档5提交到内存索引两者应该是一个原子操作,好在这两者都是比较块的。

注:此处对硬盘上的索引,也可以进行对文档5的删除,由于IndexReader没有重新打开,此删除是删不掉的,我们之所以没有这样做,是想保持此次更新要么全部在内存中,要么全部在硬盘中,而非删除部分已经应用到硬盘中,而新文档却在内存中,此时,如果系统crash,则新的文档5丢失了,而旧的文档5也已经在硬盘上被删除。我们将硬盘上对文档5的删除放到从内存索引向硬盘索引的合并过程。

点击查看原图

如果再有一次对文档5的更新,则首先将内存索引中的文档5删除,添加新的文档5,然后将文档5加入删除列表,发现已经存在,则不必删除。

3.3、合并索引

然而经过一段时间,内存中的索引需要合并到硬盘上。

在合并的过程中,需要重新建立一个空的内存索引,用于合并阶段索引新的文档,而合并中的索引的IndexReader以及硬盘索引和删除列表所组成的FilterIndexReader仍然保持打开,对外提供服务,而合并阶段从后台进行。

后台的合并包括以下几步:

  • 将删除列表应用到硬盘索引中。
  • 将内存索引合并到硬盘索引中。
  • IndexWriter提交。

点击查看原图

3.4、合并的过程中更新文档5

在合并的过程中,如果还有更新那怎么办呢?

  • 首先将合并中索引的文档5删除,此删除不会影响合并,因为合并之前,合并中索引的IndexReader已经打开,索引合并中索引的文档5还是会合并到硬盘中去的。此删除影响的是此后的查询在合并中索引是看不到文档5的。
  • 然后将文档5的删除放入删除列表,并同合并中索引的删除列表,已经硬盘索引一起构成FilterIndexReader。
  • 将新的文档5添加到内存中索引。
  • 提交在合并中索引对文档5的删除,将文档5添加到删除列表,提交在内存索引中对文档5的添加三者应该是一个原子操作,好在三者也是很快的。

点击查看原图

3.5、重新打开硬盘索引的IndexReader

当合并中索引合并到硬盘中的时候,是时候重新打开硬盘上的索引了,新打开的IndexReader是可以看到文档5的删除的。

如果这个时候有新的更新,也是添加到内存索引和删除列表的,比如我们更新文档6.

点击查看原图

3.6、替代IndexReader 

当IndexReader被重新打开后,则需要删除合并中的索引及其删除列表,将硬盘索引原来的IndexReader关闭,使用新的IndexReader。

 
点击查看原图

NOTEPAD++添加右键菜单

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\*\Shell\Open With NotePad++]
[HKEY_CLASSES_ROOT\*\Shell\Open With NotePad++\Command]
@="\"C:\\Program Files (x86)\\Notepad++\\notepad++.exe\" \"%1\""

jquery array

1. $.each(array, [callback]) 遍历[常用]

解释: 不同于例遍 jQuery 对象的 $().each() 方法,此方法可用于例遍任何对象(不仅仅是数组哦~). 回调函数拥有两个参数:第一个为对象的成员或数组的索引, 第二个为对应变量或内容. 如果需要退出 each 循环可使回调函数返回 false, 其它返回值将被忽略.

each遍历,相信都不陌生,在平常的事件处理中,是for循环的变体,但比for循环强大.在数组中,它可以轻松的攻取数组索引及对应的值.例:

var _mozi=['墨家','墨子','墨翟','兼爱非攻','尚同尚贤']//本文所用到的数组, 下同
$.
each(_mozi,function(key,val){
    
//回调函数有两个参数,第一个是元素索引,第二个为当前值
    
alert('_mozi数组中 ,索引 : '+key+' 对应的值为: '+val);
});

相对于原生的for..in,each更强壮一点. for..in也可以遍历数组,并返回对应索引,但值是需要通过arrName[key]来获取;

2. $.grep(array, callback, [invert]) 过滤数组[常用]

解释: 使用过滤函数过滤数组元素.此函数至少传递两个参数(第三个参数为true或false,对过滤函数返回值取反,个人觉得用处不大): 待过滤数组和过滤函数. 过滤函数必须返回 true 以保留元素或 false 以删除元素. 另外,过滤函数还可以是可设置为一个字条串(个人不推荐,欲了解自行查阅);

$.grep(_mozi,function(val,key){
    
//过滤函数有两个参数,第一个为当前元素,第二个为元素索引
    
if(val=='墨子'){
        
alert('数组值为 墨子 的下标是: '+key);
    
}
});
 
var _moziGt1=$.grep(_mozi,function(val,key){
    
return key>1;
});
alert('_mozi数组中索引值大于1的元素为: '+_moziGt1);
 
var _moziLt1=$.grep(_mozi,function(val,key){
    
return key>1;
},true);
//此处传入了第三个可靠参数,对过滤函数中的返回值取反
alert('_mozi数组中索引值小于等于1的元素为: '+_moziLt1);

3. $.map(array,[callback])按给定条件转换数组 [一般]

解释:作为参数的转换函数会为每个数组元素调用, 而且会给这个转换函数传递一个表示被转换的元素作为参数. 转换函数可以返回转换后的值、null(删除数组中的项目)或一个包含值的数组, 并扩展至原始数组中.

这个是个很强大的方法,但并不常用. 它可以根据特定条件,更新数组元素值,或根据原值扩展一个新的副本元素.

var _mapArrA=$.map(_mozi,function(val){
    
return val+'[新加]';
});
var _mapArrB=$.map(_mozi,function(val){
    
return val=='墨子' ? '[只给墨子加]'+val : val;
});
var _mapArrC=$.map(_mozi,function(val){
    
//为数组元素扩展一个新元素
    
return [val,(val+'[扩展]')];
});
alert('在每个元素后面加\'[新加]\'字符后的数组为: '_mapArrA);
alert('只给元素 墨子 添加字符后的数组为: '_mapArrB);
alert('为原数组中每个元素,扩展一个添加字符\'[新加]\'的元素,返回的数组为 '+_mapArrC);

4 .$.inArray(val,array)判断值是否存在于数组中[常用]

解释: 确定第一个参数在数组中的位置, 从0开始计数(如果没有找到则返回 -1 ).

记得indexOf()方法了吗? indexOf()返回字符串的首次出现位置,而$.inArray()返回的是传入参数在数组中的位置,同样的,如果找到的,返回的是一个大于或等于0的值,若未找到则返回-1.现在, 知道怎么用了吧. 有了它, 判断某个值是否存在于数组中,就变得轻而易举了.

var _exist=$.inArray('墨子',_mozi);
var _inexistence=$.inArray('卫鞅',_mozi)
if(_exist>=0){
    
alert('墨子 存在于数组_mozi中,其在数组中索引值是: '+_exist);
}
if(_inexistence<0){
    
alert('卫鞅 不存在于数组_mozi中!,返回值为: '+_inexistence+'!');
}

5 .$.merge(first,second)合并两个数组[一般]

解释: 返回的结果会修改第一个数组的内容——第一个数组的元素后面跟着第二个数组的元素.

这个方法是用jQuery的方法替代原生concat()方法, 但功能并没有concat()强大, concat()可以同时合并多个数组.

//原生concat()可能比它还简洁点
_moziNew=$.merge(_mozi,['鬼谷子','商鞅','孙膑','庞涓','苏秦','张仪'])
alert('合并后新数组长度为: '+_moziNew.length+'. 其值为: '+_moziNew);

6 .$.unique(array)过滤数组中重复元素[不常用]

解释: 删除数组中重复元素. 只处理删除DOM元素数组,而不能处理字符串或者数字数组.

第一次看到这个方法,觉得这是个很便捷的方法, 可以过滤重复, 哈, 多完美, 但仔细一看, 仅限处理DOM元素. 功能8折了.所以, 我给它定义成了一个不常用的元素, 至少, 我用jQuery以来没用到过它.

var _h2Arr=$.makeArray(h2obj);
//将数组_h2Arr重复一次
_h2Arr=$.merge(_h2Arr,_h2Arr);
var _curLen=_h2Arr.length;
_h2Arr=$.unique(_h2Arr);
var _newLen=_h2Arr.length;
alert('数组_h2Arr原长度值为: '+_curLen+' ,过滤后为: '+_newLen
      +
' .共过滤 '+(_curLen-_newLen)+'个重复元素')

7. $.makeArray(obj) 将类数组对象转换为数组[不常用]

解释: 将类数组对象转换为数组对象, 类数组对象有 length 属性,其成员索引为0至 length-1.

这是个多余的方法, 无所不能的$本来就包含了这个功能. jQuery官网上解释的非常模糊. 其实, 它就是将某个类数组对象(比如用getElementsByTagName获取的元素对象集合)转换成数组对象.

var _makeArr=$.makeArray(h2obj);
alert('h2元素对象集合的数据类型转换为: '+_makeArr.constructor.name);//输出Array

8. $(dom).toArray()将所有DOM元素恢复成数组[不常用]

解释: 把jQuery集合中所有DOM元素恢复成一个数组;

并不常用的方法, 个人甚至觉得它和$.makeArray一样多余.

var _toArr=$('h2').toArray();
alert('h2元素集合恢复后的数据类型是: '+_toArr.constructor.name);