• Linux 笔记


    博客地址:http://www.moonxy.com

    一、前言

    LAMP 环境搭建好之后,其实仅仅是安装上了软件,我们还需要掌握 httpd 和 PHP 的配置。

    二、httpd 配置

    2.1 创建虚拟主机

    我们以搭建 discuz 论坛为例,介绍如何在 Apache 中创建虚拟主机。

    下载 discuz 论坛建站程序包之后,解压到指定目录,并删掉无用的文件及目录。

    # mkdir /data/www
    # cp ComsenzDiscuz-DiscuzX-3.4.zip /data/www/
    # cd /data/www
    # unzip ComsenzDiscuz-DiscuzX-3.4.zip
    # rm -rf ComsenzDiscuz-DiscuzX-3.4.zip
    # cd DiscuzX
    # rm -rf readme utility README.md
    # mv upload/* .
    # rm -rf upload

    编辑 httpd 的主配置文件:/usr/local/apache24/conf/httpd.conf

    去掉#号,修改为:

    保存后,编辑虚拟主机配置文件:/usr/local/apache24/conf/extra/httpd-vhosts.conf,其中 ServerAdmin 为服务器管理员的邮箱;DocumentRoot为虚拟主机站点的根目录;ServerName 为网站的域名,只能定义一个;ServerAlias 为域名的别名,可以写多个,用分号分隔;ErrorLog 为站点的错误日志,Customer 为站点的访问日志。

    <VirtualHost *:80>
        #ServerAdmin webmaster@dummy-host.example.com
        DocumentRoot "/data/www/DiscuzX"
        ServerName www.lamper.com
        ServerAlias lamper.com
        ErrorLog "logs/www.lamper.com-error_log"
        CustomLog "logs/www.lamper.com-access_log" common
    </VirtualHost>

    在启动状态中,可以重新加载 httpd,命令如下:

    # apachectl graceful

    如果我们在 Windows 中访问我们 Linux 服务器中搭建的 LAMP 应用,可以修改 hosts 文件来自定义域名解析映射,如下:

    C:WindowsSystem32driversetchosts 文件中添加如下映射:

    192.168.1.121 www.lamper.com

    然后在浏览器中访问:

    http://www.lamper.com

    自动跳转到discuz安装页面:

    点击 "我同意"

    发现目录、文件权限检查不符合所需状态,查看 apache 运行进程所属用户为 daemon,如下:

    修改上面列出的不符合权限的目录所属主为 daemon,如下:

    # chown -R daemon config data uc_client uc_server

    修改后,再次刷新刚才的安装页面:

    发现已经满足所需的权限了。

    点击 "下一步" 后进入:

    选择 "全新安装",点击下一步进入安装数据库的页面。

    此时,我们先创建数据库和用户:

    [root@ryan DiscuzX]# mysql -uroot -p123456
    mysql> create database discuz;
    Query OK, 1 row affected (0.00 sec)
    mysql> grant all on discuz.* to 'rain'@'localhost' identified by '123456';
    Query OK, 0 rows affected, 1 warning (0.00 sec)
    mysql> quit
    Bye

    注意:当数据库存在用户的时候 GRANT 会对用户进行授权,但当数据库不存在该用户的时候,就会创建相应的用户并进行授权。

    填入我们创建的数据库和用户:

    点击下一步,进入初始化过程中:

    初始化完成之后,自动跳入结果页面:

    返回我们搭建的轮胎:

    2.2 添加用户认证

    首先创建密码文件:

    [root@ryan extra]# /usr/local/apache24/bin/htpasswd -cm /data/.htpasswd admin
    New password:
    Re-type new password:
    Adding password for user admin

    其中 /data/.htpasswd 文件为要创建的密码文件,可自定义路径;htpasswd 命令为创建用户的工具,-c 表示 create 创建,-m 表示使用 MD5 加密方式(默认加密方式),如果是第一次创建时需要使用 -c 选项,第二次时就不需要添加 -c 选项了,否则 /data/.htpasswd 会被覆盖,其余选项可以通过帮助:# /usr/local/apache24/bin/htpasswd --help

    [root@ryan extra]# /usr/local/apache24/bin/htpasswd --help
    Usage:
            htpasswd [-cimBdpsDv] [-C cost] passwordfile username
            htpasswd -b[cmBdpsDv] [-C cost] passwordfile username password
    
            htpasswd -n[imBdps] [-C cost] username
            htpasswd -nb[mBdps] [-C cost] username password
     -c  Create a new file.
     -n  Don't update file; display results on stdout.
     -b  Use the password from the command line rather than prompting for it.
     -i  Read password from stdin without verification (for script usage).
     -m  Force MD5 encryption of the password (default).
     -B  Force bcrypt encryption of the password (very secure).
     -C  Set the computing time used for the bcrypt algorithm
         (higher is more secure but slower, default: 5, valid: 4 to 31).
     -d  Force CRYPT encryption of the password (8 chars max, insecure).
     -s  Force SHA encryption of the password (insecure).
     -p  Do not encrypt the password (plaintext, insecure).
     -D  Delete the specified user.
     -v  Verify password for the specified user.
    On other systems than Windows and NetWare the '-p' flag will probably not work.
    The SHA algorithm does not use a salt and is less secure than the MD5 algorithm.

    然后在虚拟主机中添加用户认证,可以添加 Directory,表示指定认证的目录信息:

    对目录进行用户认证

    <VirtualHost *:80>
        #ServerAdmin webmaster@dummy-host.example.com
        DocumentRoot "/data/www/DiscuzX"
        ServerName www.lamper.com
        ServerAlias lamper.com
        ErrorLog "logs/www.lamper.com-error_log"
        CustomLog "logs/www.lamper.com-access_log" common
        <Directory "/data/www/DiscuzX">
            AllowOverride AuthConfig
            AuthName "lamper.com user auth"
            AuthType Basic
            AuthUserFile /data/.htpasswd
            require valid-user
        </Directory>
    </VirtualHost>

    其中 Directory 为指定认证的目录,AllowOverride AuthConfig 相当于打开认证的开关, AuthName 自定义认证的名字,AuthType 为认证类型,AuthUserFile 指定密码文件所在位置,require valid-user 说明需要有效的认证用户。

    保存后检查配置文件语法正确性并重新加载配置文件:

    [root@ryan extra]# apachectl -t
    Syntax OK
    [root@ryan extra]# apachectl graceful

    然后访问 http://www.lamper.com 时,浏览器会弹出对话框进行用户身份验证:

    点击取消后,浏览器会提示 401 Unauthorized:

    上面添加的配置是对整个站点添加的用户认证,其实用的最多的还是对某个目录或文件进行认证。

    对文件进行用户认证

    比如对文件认证,在 VirtualHost 中添加 FileMath 如下:

    <VirtualHost *:80>
        #ServerAdmin webmaster@dummy-host.example.com
        DocumentRoot "/data/www/DiscuzX"
        ServerName www.lamper.com
        ServerAlias lamper.com
        ErrorLog "logs/www.lamper.com-error_log"
        CustomLog "logs/www.lamper.com-access_log" common
        <FilesMatch "admin.php">
            AllowOverride AuthConfig
            AuthName "lamper.com user auth"
            AuthType Basic
            AuthUserFile /data/.htpasswd # 基于用户的
            # AuthGroupFile /data/.htgpasswd    # 基于组的
            # Require user csr      # 只允许csr用户认证
            Require valid-user  允许有效用户认证
            # Require group group-csr   允许group-csr组认证
        </FilesMatch>
    </VirtualHost>

    注意:FilesMatch 后面的文件是相对路径,而 Files、Directory则使用绝对路径。

    也可以将 Directory 中的目录认证直接改为文件据对路径,如下:

    <VirtualHost *:80>
        #ServerAdmin webmaster@dummy-host.example.com
        DocumentRoot "/data/www/DiscuzX"
        ServerName www.lamper.com
        ServerAlias lamper.com
        ErrorLog "logs/www.lamper.com-error_log"
        CustomLog "logs/www.lamper.com-access_log" common
        <Directory "/data/www/DiscuzX/admin.php">
            AllowOverride AuthConfig
            AuthName "lamper.com user auth"
            AuthType Basic
            AuthUserFile /data/.htpasswd
            require valid-user
        </Directory>
    </VirtualHost>

    验证和重载配置文件:

    [root@ryan extra]# apachectl -t
    Syntax OK
    [root@ryan extra]# apachectl graceful

    这样可以使只有我们访问管理员登录页时:http://www.lamper.com/admin.php 才出现用户认证,访问其他页面则不会出现用户认证对话框。

    2.3 域名 rewrite 重定向

    由于实现域名重定向需要rewrite模块支持,首先查找httpd是否已经加载该模块:

    [root@ryan conf]# grep -i rewrite /usr/local/apache24/conf/httpd.conf
    #LoadModule rewrite_module modules/mod_rewrite.so

    如果没有加载,则将其前面的#号删除,如下:

    [root@ryan conf]# vim /usr/local/apache24/conf/httpd.conf
    LoadModule rewrite_module modules/mod_rewrite.so

    然后在 httpd-vhosts.conf 的 VirtualHost 中添加 rewrite 模块:

    [root@ryan conf]# vim /usr/local/apache24/conf/extra/httpd-vhosts.conf
    <IfModule mod_rewrite.c>
        RewriteEngine on
        RewriteCond %{HTTP_HOST} ^www.amlinux.com$
        RewriteRule ^/(.*)$ http://www.lamper.com/$1 [R=301,L]
    </IfModule>

    此处将 www.amlinux.com 重定向到 www.lamper.com。

    修改 Windows 中的 hosts 文件:

    C:WindowsSystem32driversetchosts

    添加如下域名映射,注意同一个IP可以对应多个域名,多个域名之间用空格分隔,如下:

    192.168.1.121 www.lamper.com www.amlinux.com

    然后验证域名重定向:

    当在浏览器中输入 http://www.amlinux.com 后会重新跳转到 http://www.lamper.com;输入 http://www.amlinux.com/123.html 后浏览器会跳转到 http://www.lamper.com/123.html。

    如果需要将多个域名重定向到一个域名,则配置多个 RewriteCond 并用 OR 连接,可以将每一个 RewriteCond 看做一个条件表达式,满足这个条件时才执行下面的 RewriteRule 表达式。

    多域名重定向,如下:

    [root@ryan conf]# vim /usr/local/apache24/conf/extra/httpd-vhosts.conf
    <IfModule mod_rewrite.c>
        RewriteEngine on
        RewriteCond %{HTTP_HOST} ^www.amlinux.com$ [OR]
        RewriteCond %{HTTP_HOST} ^www.moonxy.com$
        RewriteRule ^/(.*)$ http://www.lamper.com/$1 [R=301,L]
    </IfModule>

    此处将 www.amlinux.com 和 www.moonxy.com 均重定向到 www.lamper.com。

    如果使用的是 360 安全浏览器,发现会出现域名重定向的提示:

    查看 http 请求和响应信息:

    状态码为 301,跳转后的网址 Location: http://www.lamper.com,L 表示 last,意思是跳转一次就结束。

    注意:

    域名重定向最常用到的是 301 和 302 这两个 http 状态码,301 重定向和 302 重定向的区别:

    301 重定向是永久的重定向,搜索引擎在抓取新的内容的同时也将旧的网址替换为了重定向之后的网址。

    302重定向只是临时的重定向,搜索引擎会抓取新的内容而保留旧的地址,因为服务器返回 302,所以,搜索搜索引擎认为新的网址是暂时的。302 又叫 redirect。

    2.4 配置访问日志

    系统的访问日志可以记录网站的访问情况,还可以在网站出现异常发生时帮助我们定位问题。

    要配置 httpd 的访问日志,首先需要在主配置文件中定义访问日志的格式,打开 httpd 的主配置文件:

    /usr/local/apache24/conf/httpd.conf

    [root@ryan conf]# grep -i LogFormat /usr/local/apache24/conf/httpd.conf
        LogFormat "%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"" combined
        LogFormat "%h %l %u %t "%r" %>s %b" common
          LogFormat "%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i" %I %O" combinedio

    可以看到httpd已经提供了两种格式,combined 和common 分别相当于对应格式的引用名称,当让也可以自定义日志格式和引用名称。要使用第三种格式 combinedio,则必须启用 mod_logio.c 模块。

    参数说明:

    %a 远端IP地址
    %A 本机IP地址
    %B 除HTTP头以外传送的字节数
    %b 以CLF格式显示的除HTTP头以外传送的字节数,也就是当没有字节传送时显示’-'而不是0。
    %{Foobar}C 在请求中传送给服务端的cookieFoobar的内容。
    %D 服务器处理本请求所用时间,以微为单位。
    %{FOOBAR}e 环境变量FOOBAR的值
    %f 文件名
    %h 远端主机
    %H 请求使用的协议
    %{Foobar}i 发送到服务器的请求头Foobar:的内容。
    %l 远端登录名(由identd而来,如果支持的话),除非IdentityCheck设为"On",否则将得到一个"-"%m 请求的方法
    %{Foobar}n 来自另一个模块的注解Foobar的内容。
    %{Foobar}o 应答头Foobar:的内容。
    %p 服务器服务于该请求的标准端口。
    %P 为本请求提供服务的子进程的PID。
    %{format}P 服务于该请求的PID或TID(线程ID),format的取值范围为:pid和tid(2.0.46及以后版本)以及hextid(需要APR1.2.0及以上版本)
    %q 查询字符串(若存在则由一个"?"引导,否则返回空串)
    %r 请求的第一行
    %s 状态。对于内部重定向的请求,这个状态指的是原始请求的状态,—%>s则指的是最后请求的状态。
    %t 时间,用普通日志时间格式(标准英语格式)
    %{format}t 时间,用strftime(3)指定的格式表示的时间。(默认情况下按本地化格式)
    %T 处理完请求所花时间,以秒为单位。
    %u 远程用户名(根据验证信息而来;如果返回status(%s)为401,可能是假的)
    %U 请求的URL路径,不包含查询字符串。
    %v 对该请求提供服务的标准ServerName。
    %V 根据UseCanonicalName指令设定的服务器名称。
    %X 请求完成时的连接状态:X= 连接在应答完成前中断。
    += 应答传送完后继续保持连接。
    -= 应答传送完后关闭连接。
    
    (在1.3以后的版本中,这个指令是%c,但这样就和过去的SSL语法:%{var}c冲突了)
    %I 接收的字节数,包括请求头的数据,并且不能为零。要使用这个指令你必须启用mod_logio模块。
    %O 发送的字节数,包括请求头的数据,并且不能为零。要使用这个指令你必须启用mod_logio模块。
    
    修饰符
    可以紧跟在"%"后面加上一个逗号分隔的状态码列表来限制记录的条目。例如,"%400,501{User-agent}i" 只记录状态码400和501发生时的User-agent头内容;不满足条件时用"-"代替。状态码前还可以加上"!"前缀表示否 定,"%!200,304,302{Referer}i"记录所有不同于200,304,302的状态码发生时的Referer头内容。
    
    "<"">"修饰符可以用来指定对于已被内部重定向的请求是选择原始的请求还是选择最终的请求。默认情况下,%s, %U, %T, %D, %r 使用原始请求,而所有其他格式串则选择最终请求。例如,%>s 可以用于记录请求的最终状态,而 %<u 则记录一个已经被内部重定向到非认证资源的请求的原始认证用户。

    继续编辑虚拟主机配置文件

    /usr/local/apache24/conf/extra/httpd-vhosts.conf

    [root@ryan conf]# vim /usr/local/apache24/conf/extra/httpd-vhosts.conf
    <VirtualHost *:80>
        #ServerAdmin webmaster@dummy-host.example.com
        DocumentRoot "/data/www/DiscuzX"
        ServerName www.lamper.com
        ServerAlias lamper.com
        ErrorLog "logs/www.lamper.com-error_log"
        CustomLog "logs/www.lamper.com-access_log" combined
        <IfModule mod_rewrite.c>
            RewriteEngine on
            RewriteCond %{HTTP_HOST} ^www.amlinux.com$
            RewriteRule ^/(.*)$ http://www.lamper.com/$1 [R=301,L]
        </IfModule>
        <Directory "/data/www/DiscuzX/admin.php">
            AllowOverride AuthConfig
            AuthName "lamper.com user auth"
            AuthType Basic
            AuthUserFile /data/.htpasswd
            require valid-user
        </Directory>
    </VirtualHost>

    我们将系统默认的 common 格式修改为 combined,这样记录的信息会更全面一些。

    保存之后,我们验证一下:

    [root@ryan conf]# /usr/local/apache24/bin/apachectl -t
    Syntax OK
    [root@ryan conf]# /usr/local/apache24/bin/apachectl graceful
    [root@ryan conf]# curl -x127.0.0.1:80 -I www.lamper.com
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 05:31:10 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    X-Powered-By: PHP/7.2.8
    Set-Cookie: P6bY_2132_saltkey=ZCE7eKcE; expires=Mon, 27-Aug-2018 05:31:10 GMT; Max-Age=2592000; path=/; HttpOnly
    Set-Cookie: P6bY_2132_lastvisit=1532752270; expires=Mon, 27-Aug-2018 05:31:10 GMT; Max-Age=2592000; path=/
    Set-Cookie: P6bY_2132_sid=PCAWeC; expires=Sun, 29-Jul-2018 05:31:10 GMT; Max-Age=86400; path=/
    Set-Cookie: P6bY_2132_lastact=1532755870%09index.php%09; expires=Sun, 29-Jul-2018 05:31:10 GMT; Max-Age=86400; path=/
    Set-Cookie: P6bY_2132_onlineusernum=1; expires=Sat, 28-Jul-2018 05:36:10 GMT; Max-Age=300; path=/
    Set-Cookie: P6bY_2132_sid=PCAWeC; expires=Sun, 29-Jul-2018 05:31:10 GMT; Max-Age=86400; path=/
    Content-Type: text/html; charset=utf-8
    
    [root@ryan conf]# tail /usr/local/apache24/logs/www.lamper.com-access_log
    192.168.1.101 - - [28/Jul/2018:13:01:48 +0800] "GET /static/image/admincp/btn_block.gif HTTP/1.1" 200 3484
    192.168.1.101 - - [28/Jul/2018:13:01:48 +0800] "GET /static/image/admincp/logo.gif HTTP/1.1" 200 3403
    192.168.1.101 - - [28/Jul/2018:13:01:48 +0800] "GET /static/image/admincp/bg_repx_h.gif HTTP/1.1" 200 4028
    192.168.1.101 - admin [28/Jul/2018:13:01:48 +0800] "GET /admin.php?action=index HTTP/1.1" 200 40
    192.168.1.101 - - [28/Jul/2018:13:01:49 +0800] "GET /static/image/admincp/logo_hover.gif HTTP/1.1" 200 4100
    192.168.1.101 - admin [28/Jul/2018:13:01:51 +0800] "GET /admin.php?action=setting&operation=basic HTTP/1.1" 200 7635
    192.168.1.101 - - [28/Jul/2018:13:01:51 +0800] "GET /static/image/admincp/bg_repx_hc.gif HTTP/1.1" 200 259
    192.168.1.101 - admin [28/Jul/2018:13:01:53 +0800] "GET /admin.php?action=nav HTTP/1.1" 200 7006
    192.168.1.101 - - [28/Jul/2018:13:01:53 +0800] "GET /static/image/admincp/btn_block_3.gif HTTP/1.1" 200 375
    127.0.0.1 - - [28/Jul/2018:13:31:10 +0800] "HEAD http://www.lamper.com/ HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.21 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"

    可以发现最后一行的日志信息是修改日志格式后产生的。

    curl 命令

    curl 命令可以用来执行下载、发送各种HTTP请求,指定 HTTP 头部等操作。如果系统没有 curl 可以使用 yum install curl 安装,也可以下载安装。curl 是将下载文件输出到 stdout,将进度信息输出到 stderr,不显示进度信息使用 --silent 选项。

    上面使用到了 -x 选项可以为 CURL 添加代理功能,如:

    1 # 指定代理主机和端口
    2 curl -x proxysever.test.com:3128 http://google.co.in

    -I/--head 表示只显示请求头信息,如:

    curl -I http://www.lamper.com

    -e/--referer 表示来源网址,可以用来伪造 referer(盗链)

    很多服务器会检查http访问的referer从而来控制访问。比如:你是先访问首页,然后再访问首页中的邮箱页面,这里访问邮箱的referer地址就是访问首页成功后的页面地址,如果服务器发现对邮箱页面访问的referer地址不是首页的地址,就断定那是个盗连了

    curl 中内置 option:-e可以让我们设定referer,如下:

    curl -e "http://www.amlinux.com" http://www.lamper.com

    日志过滤掉静态文件

    一个网站会有很多文件,其中就包括很多的图片、js、css 等静态文件,用户每请求一个页面就会访问这些静态元素,但是这些静态元素在日志里面存在的意义不大,为了防止日志文件过大,我们需要在记录日志时过滤掉这些静态文件,且需要敬日志按天归档,一天一个日志。

    修改虚拟主机配置文件:/usr/local/apache24/conf/extra/httpd-vhosts.conf

    [root@ryan conf]# vim /usr/local/apache24/conf/extra/httpd-vhosts.conf
    <VirtualHost *:80>
        #ServerAdmin webmaster@dummy-host.example.com
        DocumentRoot "/data/www/DiscuzX"
        ServerName www.lamper.com
        ServerAlias lamper.com
        ErrorLog "logs/www.lamper.com-error_log"
        SetEnvIf Request_URI ".*.gif$" image-request
        SetEnvIf Request_URI ".*.jpg$" image-request
        SetEnvIf Request_URI ".*.png$" image-request
        SetEnvIf Request_URI ".*.bmp$" image-request
        SetEnvIf Request_URI ".*.swf$" image-request
        SetEnvIf Request_URI ".*.js$" image-request
        SetEnvIf Request_URI ".*.css$" image-request
        CustomLog "logs/www.lamper.com-access_log" combined env=!image-request
        <IfModule mod_rewrite.c>
            RewriteEngine on
            RewriteCond %{HTTP_HOST} ^www.amlinux.com$
            RewriteRule ^/(.*)$ http://www.lamper.com/$1 [R=301,L]
        </IfModule>
        <Directory "/data/www/DiscuzX/admin.php">
            AllowOverride AuthConfig
            AuthName "lamper.com user auth"
            AuthType Basic
            AuthUserFile /data/.htpasswd
            require valid-user
        </Directory>
    </VirtualHost>

    如上,先添加一个 image-request 环境变量,把 gif、jpg、png、bmp、swf、js 和 css 等格式的文件全部归类到 image-request 里,后面的 env=!image-request 用到了一个 "!" 表示取反,意思是只将 image-requset 以外的类型文件记录到日志里。

    如果想将日志按天归档,可修改 CustomLog 为:

    CustomLog "|/usr/local/apache24/bin/rotatelogs -l logs/www.lamper.com-access_%Y%m%d.log 86400" combined env=!image-request

    正常 CustomLog 后面为日志文件名,但是这里使用一个管道,它会把日志内容交给后面的 rotatelogs(apache 自带的日志切割工具)命令处理,它会把访问日志按照我们定义的文件名进行切割,86400 单位为秒,相当于一天。

    保存之后验证日志:

    [root@ryan conf]# /usr/local/apache24/bin/apachectl -t
    Syntax OK
    [root@ryan conf]# /usr/local/apache24/bin/apachectl graceful
    [root@ryan conf]# curl -x127.0.0.1:80 -I www.lamper.com
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 06:15:24 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    X-Powered-By: PHP/7.2.8
    Set-Cookie: P6bY_2132_saltkey=o77ZVSKV; expires=Mon, 27-Aug-2018 06:15:24 GMT; Max-Age=2592000; path=/; HttpOnly
    Set-Cookie: P6bY_2132_lastvisit=1532754924; expires=Mon, 27-Aug-2018 06:15:24 GMT; Max-Age=2592000; path=/
    Set-Cookie: P6bY_2132_sid=jWISSI; expires=Sun, 29-Jul-2018 06:15:24 GMT; Max-Age=86400; path=/
    Set-Cookie: P6bY_2132_lastact=1532758524%09index.php%09; expires=Sun, 29-Jul-2018 06:15:24 GMT; Max-Age=86400; path=/
    Set-Cookie: P6bY_2132_onlineusernum=1; expires=Sat, 28-Jul-2018 06:20:24 GMT; Max-Age=300; path=/
    Set-Cookie: P6bY_2132_sid=jWISSI; expires=Sun, 29-Jul-2018 06:15:24 GMT; Max-Age=86400; path=/
    Content-Type: text/html; charset=utf-8
    
    [root@ryan conf]# ll /usr/local/apache24/logs
    total 120
    -rw-r--r-- 1 root root   974 Jul 21 16:34 access_log
    -rw-r--r-- 1 root root  7467 Jul 28 14:14 error_log
    -rw-r--r-- 1 root root     5 Jul 28 14:14 httpd.pid
    -rw-r--r-- 1 root root   203 Jul 28 14:15 www.lamper.com-access_20180728.log
    -rw-r--r-- 1 root root 93717 Jul 28 13:31 www.lamper.com-access_log
    -rw-r--r-- 1 root root   184 Jul 24 22:06 www.lamper.com-error_log

    可以看到新生成的日志已经带了日期戳:www.lamper.com-access_20180728.log,而且以后会每天生成一个按日期命名的日志文件。我们可以再添加一个定时任务,将日期间隔大于两个月的日志,定期清理掉,达到释放磁盘的目的。

    再来验证静态文件是否已经过滤:

    [root@ryan conf]# touch /data/www/DiscuzX/ryan.jpg
    [root@ryan conf]# touch /data/www/DiscuzX/ryan.txt
    [root@ryan conf]# curl -x127.0.0.1:80 www.lamper.com/ryan.jpg
    [root@ryan conf]# curl -x127.0.0.1:80 www.lamper.com/ryan.txt
    [root@ryan conf]# cat /usr/local/apache24/logs/www.lamper.com-access_20180728.log
    127.0.0.1 - - [28/Jul/2018:14:15:24 +0800] "HEAD http://www.lamper.com/ HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.21 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
    127.0.0.1 - - [28/Jul/2018:14:21:55 +0800] "GET http://www.lamper.com/ryan.txt HTTP/1.1" 200 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.21 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"

    在日志中只能看到请求 ryan.txt的日志记录,而看不到请求ryan.jpg的请求日志,表示日志已经过滤掉了静态元素。

    2.5 配置静态元素过期时间

    如果我们仔细查看日志文件,会发现存在状态码为304的日志记录,这个状态码表示该文件已经缓存到了用户的电脑里了,再次请求服务器的时候,不再下载,而是直接从用户的电脑缓存里面读取。这样可以提升用户访问网站的速度,且降低服务器的资源消耗。

    如果服务器上面某个图片已经更改,那就需要访问新的图片,这就涉及到静态文件缓存时长的问题,也称作 "缓存过期时间"。

    配置缓存过期时间需要使用到 mod_expires 模块,首先检查当前 httpd 是否支持 expires 模块:

    [root@ryan conf]# /usr/local/apache24/bin/apachectl -M|grep -i expires

    没有任何输出表示当前httpd并不支持expires模块,需要手动开启,如下:

    [root@ryan conf]# vim /usr/local/apache24/conf/httpd.conf
    //去掉前面的#注释,开启 expires 模块
    LoadModule expires_module modules/mod_expires.so
    [root@ryan conf]# /usr/local/apache24/bin/apachectl graceful
    [root@ryan conf]# /usr/local/apache24/bin/apachectl -M|grep -i expires
     expires_module (shared)

    有上面的输出,表示已经正确加载了 expires 模块。

    打开虚拟主机配置文件:/usr/local/apache24/conf/extra/httpd-vhosts.conf

    [root@ryan conf]# vim /usr/local/apache24/conf/extra/httpd-vhosts.conf
    <IfModule mod_expires.c>
        ExpiresActive on
        ExpiresByType image/gif "access plus 1 days"
        ExpiresByType image/jpeg "access plus 24 hours"
        ExpiresByType image/png "access plus 24 hours"
        ExpiresByType text/css "now plus 2 hours"
        ExpiresByType application/x-javascript "now plus 2 hours"
        ExpiresByType application/x-shockwave-flash "now plus 2 hours"
        ExpiresDefault "now plus 0 min"
    </IfModule>

    说明:

    ExpiresActive on 表示开启过期时间功能。"access plus 1 days" 意指浏览时起算1天。依照 Apache 官方说明文件,过期起算时间有三种,分别是 access、now 以及 modification。其中 access 与 now 意义相同,而 modification 指的是网页文件的 "最后编辑时间"。所以如果要以文档的最后编辑时间起算,可以写成这样:"modification plus 1 days"。而时间的指定也很简单,就是英文单词(years、months、weeks、days、hours、minutes、seconds)。例如,可以写成这样:"access plus 1 month 15 days 2 hours"。

    验证元素过期时间

    [root@ryan conf]# curl -x127.0.0.1:80 -I 'http://www.lamper.com/static/image/common/logo.png'
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 07:45:03 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Last-Modified: Mon, 04 Jun 2018 10:16:47 GMT
    ETag: "1149-56dce3b2c41c0"
    Accept-Ranges: bytes
    Content-Length: 4425
    Content-Type: image/png
    
    [root@ryan conf]# /usr/local/apache24/bin/apachectl -t
    Syntax OK
    [root@ryan conf]# /usr/local/apache24/bin/apachectl graceful
    [root@ryan conf]# curl -x127.0.0.1:80 -I 'http://www.lamper.com/static/image/common/logo.png'
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 07:45:58 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Last-Modified: Mon, 04 Jun 2018 10:16:47 GMT
    ETag: "1149-56dce3b2c41c0"
    Accept-Ranges: bytes
    Content-Length: 4425
    Cache-Control: max-age=86400
    Expires: Sun, 29 Jul 2018 07:45:58 GMT
    Content-Type: image/png

    可以看到修改后比修改前多了 max-age 等头信息,同样,86400单位为秒,表示一天,与上面的设置: ExpiresByType image/png "access plus 24 hours" 保持一致,表示设置成功,通常可以设置过期日期为 30 天。

    2.6 配置防盗链

    通过防盗链的方式,可以设置限制第三方的站点通过引用的方式获取服务器上的图片,数据等,如果想要获取本站点的图片数据,只能通过本站点访问获取,这样也有效的减少了服务器的资源。

    配置防盗链之前,需要了解什么是 referer?

    HTTP Referer是 request header 的一部分,当浏览器向 web 服务器发送请求的时候,一般会带上 Referer,告诉服务器我是从哪个页面链接过来的,服务器基此可以获得一些信息用于处理。

    比如:在A网站的某个页面 http://aaa.com/a.html 里面的链接去访问B网站的某个页面 http://bbb.com/b.html,那么 B 网站的 reffer 就是http://aaa.com/a.html。

    编辑虚拟主机配置文件,添加防盗链规则:

    [root@ryan conf]# vim /usr/local/apache24/conf/extra/httpd-vhosts.conf
    <Directory "/data/www/DiscuzX">
        SetEnvIfNoCase Referer "http://www.lamper.com" local_ref
        SetEnvIfNoCase Referer "http://lamper.com" local_ref
        SetEnvIfNoCase Referer "^$" local_ref
        <FilesMatch ".(txt|doc|mp3|zip|rar|jpg|gif|png)">
            Order Allow,Deny
            Allow from env=local_ref
    </FilesMatch>
    </Directory>

    首先定义允许访问链接的 referer,其中 ^$ 为空 refer,当直接在浏览器中输入图片们地址去访问它时,他的 referer 就为空。然后使用 FileMatch 来定义需要保护的文件类型,访问 txt、doc、mp3、zip、rar、jpg、gif、png 等格式的文件,当访问这样类型的文件时就会运用防盗链规则。

    验证防盗链

    [root@ryan conf]# curl -x 127.0.0.1:80 -I -e "http://www.amlinux.com" http://www.lamper.com/static/image/common/logo.png
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 09:26:31 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Last-Modified: Mon, 04 Jun 2018 10:16:47 GMT
    ETag: "1149-56dce3b2c41c0"
    Accept-Ranges: bytes
    Content-Length: 4425
    Cache-Control: max-age=86400
    Expires: Sun, 29 Jul 2018 09:26:31 GMT
    Content-Type: image/png
    
    [root@ryan conf]# /usr/local/apache24//bin/apachectl -t
    Syntax OK
    [root@ryan conf]# /usr/local/apache24//bin/apachectl graceful
    [root@ryan conf]# curl -x 127.0.0.1:80 -I -e "http://www.amlinux.com" http://www.lamper.com/static/image/common/logo.png
    HTTP/1.1 403 Forbidden
    Date: Sat, 28 Jul 2018 09:29:24 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Content-Type: text/html; charset=iso-8859-1

    对比配置文件生效前后,请求同一个 png 图片的请求结果,发现已经生效,状态码 403 Forbidden,表示禁止访问某些资源。-e 来定义 referer,这个 referer 建议以 http:// 开头,否则可能导致不生效。

    [root@ryan conf]# curl -x 127.0.0.1:80 -I -e "http://www.amlinux.com" http://www.lamper.com/rayn.jpg
    HTTP/1.1 403 Forbidden
    Date: Sat, 28 Jul 2018 09:39:47 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Content-Type: text/html; charset=iso-8859-1
    
    [root@ryan conf]# curl -x 127.0.0.1:80 -I -e "http://www.amlinux.com" http://www.lamper.com/rayn.txt
    HTTP/1.1 403 Forbidden
    Date: Sat, 28 Jul 2018 09:39:56 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Content-Type: text/html; charset=iso-8859-1
    
    [root@ryan conf]# touch /data/www/DiscuzX/rayn.html
    [root@ryan conf]# curl -x 127.0.0.1:80 -I -e "http://www.amlinux.com" http://www.lamper.com/rayn.html
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 09:40:38 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Last-Modified: Sat, 28 Jul 2018 09:40:27 GMT
    ETag: "0-5720c04b33a19"
    Accept-Ranges: bytes
    Cache-Control: max-age=0
    Expires: Sat, 28 Jul 2018 09:40:38 GMT
    Content-Type: text/html

    对于不是上面限制的格式,比如 .html,可以正常访问,没有运用防盗链规则。

    2.7 访问控制

    对于一些比较重要的网站内容,除了可以使用用户认证限制访问之外,还可以通过其他一些方法做到限制,比如可以限制 IP,也可以限制 user_agent。限制 IP 指的是限制访问网站的来源IP,而限制 user_agent,通常用来限制恶意或者不正常的请求。

    一般凡是涉及网站后台的访问都要做 IP 限制,只允许公司内网IP或者被信任的公司IP访问。这样就可以防止别有用心的人拿到网站后台权限,并获得数据。

    首先限制IP访问,编辑虚拟机主机配置文件,如下:

    [root@ryan conf]# vim /usr/local/apache24/conf/extra/httpd-vhosts.conf
    <Directory "/data/wwww/DiscuzX/admin/">
        Order deny,allow
        Deny from all
        Allow from 127.0.0.1
    </Directory>

    使用 <Directory> 来指定要限制访问的目录,Order 定义控制顺序,哪个在前面就先匹配哪个规则。本例中 deny 在前,所以先匹配 Deny from all,表示所有的IP都会被限制,然后匹配 Allow from 127.0.0.1,这样只允许127.0.0.1 这个 IP 访问目录: /data/wwww/DiscuzX/admin/,所以最终也只有 127.0.0.1 才能访问 /data/wwww/DiscuzX/admin/。

    验证访问控制:

    [root@ryan conf]# mkdir /data/www/DiscuzX/admin
    [root@ryan conf]# echo "admin" > /data/www/DiscuzX/admin/index.html
    [root@ryan conf]# curl -x 192.168.1.121:80 -I  http://www.lamper.com/admin/index.html
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 11:23:44 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Last-Modified: Sat, 28 Jul 2018 11:23:01 GMT
    ETag: "6-5720d737d5bf5"
    Accept-Ranges: bytes
    Content-Length: 6
    Cache-Control: max-age=0
    Expires: Sat, 28 Jul 2018 11:23:44 GMT
    Content-Type: text/html
    
    [root@ryan conf]# curl -x 127.0.0.1:80 -I  http://www.lamper.com/admin/index.html
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 11:24:01 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Last-Modified: Sat, 28 Jul 2018 11:23:01 GMT
    ETag: "6-5720d737d5bf5"
    Accept-Ranges: bytes
    Content-Length: 6
    Cache-Control: max-age=0
    Expires: Sat, 28 Jul 2018 11:24:01 GMT
    Content-Type: text/html
    
    [root@ryan conf]# /usr/local/apache24/bin/apachectl -t
    Syntax OK
    [root@ryan conf]# /usr/local/apache24/bin/apachectl graceful
    [root@ryan conf]# curl -x 192.168.1.121:80 -I  http://www.lamper.com/admin/index.html
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 11:24:55 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Last-Modified: Sat, 28 Jul 2018 11:23:01 GMT
    ETag: "6-5720d737d5bf5"
    Accept-Ranges: bytes
    Content-Length: 6
    Cache-Control: max-age=0
    Expires: Sat, 28 Jul 2018 11:24:55 GMT
    Content-Type: text/html
    
    [root@ryan conf]# curl -x 127.0.0.1:80 -I  http://www.lamper.com/admin/index.html
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 11:25:05 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Last-Modified: Sat, 28 Jul 2018 11:23:01 GMT
    ETag: "6-5720d737d5bf5"
    Accept-Ranges: bytes
    Content-Length: 6
    Cache-Control: max-age=0
    Expires: Sat, 28 Jul 2018 11:25:05 GMT
    Content-Type: text/html
    
    [root@ryan conf]# /usr/local/apache24/bin/apachectl -t
    Syntax OK
    [root@ryan conf]# /usr/local/apache24/bin/apachectl graceful
    [root@ryan conf]# curl -x 192.168.1.121:80 -I  http://www.lamper.com/admin/index.html
    HTTP/1.1 403 Forbidden
    Date: Sat, 28 Jul 2018 11:30:43 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Content-Type: text/html; charset=iso-8859-1
    
    [root@ryan conf]# curl -x 127.0.0.1:80 -I  http://www.lamper.com/admin/index.html
    HTTP/1.1 200 OK
    Date: Sat, 28 Jul 2018 11:30:51 GMT
    Server: Apache/2.4.34 (Unix) PHP/7.2.8
    Last-Modified: Sat, 28 Jul 2018 11:23:01 GMT
    ETag: "6-5720d737d5bf5"
    Accept-Ranges: bytes
    Content-Length: 6
    Cache-Control: max-age=0
    Expires: Sat, 28 Jul 2018 11:30:51 GMT
    Content-Type: text/html

    修改前用 Windows 的浏览器访问:

    修改后用 Windows 的浏览器访问:

    也可以针对单个文件来做限制

    <Directory "/data/www/DiscuzX">
        <FilesMatch "admin.php(.*)">
            Order Deny,Allow
            Deny from all
            Allow from 127.0.0.1
        </FilesMatch>
    </Directory>

    禁止某些目录解析PHP代码

    有些 PHP 网站运行上传文件,如果某些黑客上传了一个用PHP写的木马,由于网站可以执行PHP代码,这样就可能导致黑客拿到服务器权限,为了避免此类情况发送,我们需要把能上传文件的目录禁止解析 PHP代码。

    编辑虚拟主机配置文件,如下:

    #禁止解析某些目录的PHP代码
    <Directory "/data/www/DiscuzX/upload/">
        php_admin_flag engine off
    </Directory>

    验证过程如下:

    [root@ryan conf]# mkdir /data/www/DiscuzX/upload
    [root@ryan conf]# cp /usr/local/apache24/htdocs/test.php /data/www/DiscuzX/upload/
    [root@ryan conf]# ll /data/www/DiscuzX/upload
    total 4
    -rw-r--r-- 1 root root 29 Jul 28 19:58 test.php
    [root@ryan conf]# curl -x127.0.0.1:80 www.lamper.com/upload/test.php
    我的LAMP
    [root@ryan conf]# /usr/local/apache24/bin/apachectl -t
    Syntax OK
    [root@ryan conf]# /usr/local/apache24/bin/apachectl graceful
    [root@ryan conf]# curl -x127.0.0.1:80 www.lamper.com/upload/test.php
    <?php
            echo "我的LAMP";
    ?>

    说明 upload 目录下的 test.php 已经不能正常解析了。

    限制 user_agent

    user_agent 为浏览器标识,当我们用 Chrome 浏览器访问网站时,user_agent 为 "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",firefox 浏览器访问时的user_agent 为 "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:60.0) Gecko/20100101 Firefox/60.0",IE 浏览器访问时的 user_agent 为 "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)",通常可以针对一些恶意网络爬虫来做 user_agent 限制。

    修改虚拟主机配置文件,如下:

    [root@ryan conf]# vim /usr/local/apache24/conf/extra/httpd-vhosts.conf
    #限制某些USER_AGENT访问网站
    <IfModule mod_rewrite.c>
        RewriteEngine on
        RewriteCond %{HTTP_USER_AGENT} .*Firefox.* [NC,OR]
        RewriteCond %{HTTP_USER_AGENT} .*Chrome.* [NC]
        RewriteRule .* - [F]
    </IfModule>

    这里也使用到了 rewrite 模块,%{HTTP_USER_AGENT} 为user_agent 的内置变量,NC表示不区分大小写,OR表示或者,F相当于Forbidden,此例中,限制火狐 Firefox 和谷歌 Chrome 浏览器访问本网站。

    使配置生效

    [root@ryan conf]# /usr/local/apache24/bin/apachectl -t
    Syntax OK
    [root@ryan conf]# /usr/local/apache24/bin/apachectl graceful

    验证

    Firefox 浏览器:

    Chorome 浏览器:

    IE 浏览器:

    发现 Firefox 和 Chrome 浏览器已经被限制访问了。

    三、PHP 配置

    虽然 PHP 是以 httpd 一个模块的形式存在的,但是 PHP 本身也有自己的配置文件。查看 PHP 配置文件所在位置的命令:

    [root@ryan ~]# /usr/local/php7/bin/php -i |grep -i configuration
    Configuration File (php.ini) Path => /usr/local/php7/etc
    Loaded Configuration File => /usr/local/php7/etc/php.ini
    Configuration

    php.ini 为 PHP 的配置文件,可以看出其在 /usr/local/php7/etc/php.ini,比如可以设置时区为:

    [root@ryan ~]# fgrep date.timezone /usr/local/php7/etc/php.ini
    ; http://php.net/date.timezone
    ;date.timezone = Asia/Shanghai

    分号;在 php.ini 中表示注释。

    3.1 配置 PHP 的 disable_functions

    PHP 有很多的内置函数,有一些函数会直接调用Linux系统命令,如果开放此函数的话将会带来很大的危险。因此,基于风险考虑,可以将这些不安全的函数禁掉。如下:

    [root@ryan ~]# vim /usr/local/php7/etc/php.ini
    disable_functions = phpinfo,eval,passthru,exec,system,chroot,scandir,chgrp,chown,shell_exec,proc_open,proc_get_status,ini_alter,ini_alter,ini_restore,dl,pfsockopen,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server,fsocket,fsockopen

    这些被禁掉的函数是不能在PHP代码中调用的。更改完php.ini后,由于需要在httpd中调用PHP,所以还需要重启httpd服务使其生效。

    比如在网站根目录下新建一个phpinfo.php文件,添加如下内容:

    <?php
    phpinfo();
    ?>

    然后访问:http://www.lamper.com/phpinfo.php,保存前发现可以正常访问,保存后再次访问时发现是 "白屏"。

    [root@ryan ~]# tail /usr/local/apache24/logs/www.lamper.com-error_log
    [Sun Jul 29 20:54:54.458570 2018] [php7:warn] [pid 2226:tid 140718538475264] [client 127.0.0.1:45105] PHP Warning:  phpinfo() has been disabled for security reasons in /data/www/DiscuzX/phpinfo.php on line 2

    查看日志发现给出了提示:phpinfo() has been disabled for security reasons in /data/www/DiscuzX/phpinfo.php on line 2

    3.2 配置 error_log

    PHP的日志对于程序员来说非常重要,通过它可以排查好多问题。

    设置如下:

    [root@ryan ~]# vim /usr/local//php7/etc/php.ini
    log_errors = On
    error_log = /var/log/php7/php_errors.log
    error_reporting = E_ALL & ~E_NOTICE
    display_errors = Off

    说明:log_errors 可以设置为 on 或者 off,如果想让 PHP 记录错误日志,需要设置为 on。error_log 设定错误日志路径。error_reporting 设定错误日志的级别,E_ALL 为所有类型的日志,不管是提醒还是警告都会记录。在开发环境下面设置为 E_ALL,可以方便开发人员排查问题,&表示并且,~表示排除,所以两者组合在一起使用时,就表示在E_ALL的基础上排除掉notice相关的日志。display_errors 设置为 on,则会把错误信息直接显示在浏览器的页面中,这样对用户来说不友好,而且还会暴露网站的一些文件的路径等信息,所以将其设置为 off。

    [root@ryan ~]# mkdir /var/log/php7
    [root@ryan ~]# chmod 777 /var/log/php7
    [root@ryan ~]# apachectl graceful

    需要手动创建日志目录,并且权限可写。

    3.3 配置 open_basedir

    open_basedir 的作用是将网站限定在指定目录里,就算该站点被黑了,黑客也只能在该目录下面操作,而不能操作其他目录。如果你的服务器上只有一个站点,那可以直接在 php.ini 中设置 open_basedir 参数。但如果服务器上跑的站点比较多,那在 php.ini 中设置就不合适了,因为在php.ini中只能定义一次,也就是所所有站点都一起定义限定的目录,那这样似乎起不到隔离多个站点的目的,此时可以将其定义在虚拟主机的配置文件中。

    先来在 php.ini 中配置 open_basedir,如下:

    [root@ryan ~]# vim /usr/local//php7/etc/php.ini
    open_basedir = /data/www/DiscuzX:/tmp

    open_basedir 可以是多个目录,之间用:分隔。

    也可以在虚拟主机配置文件中设置,如下:

    [root@ryan ~]# vim /usr/local/apache24/conf/extra/httpd-vhosts.conf
    php_admin_value open_basedir "/data/www/DiscuzX/:/tmp/"

    可以将 php_admin_value 这一行添加到 CustomLog 这一行下面。

    附录

    关于USER_AGENT:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/User-Agent

    Php与Apache的三种结合方式以及各自优缺点:https://blog.csdn.net/sinat_22991367/article/details/73431316

  • 相关阅读:
    Mysql游标的简明写法
    Sublime Text 介绍、用法、插件等
    [LeetCode#13] Roman to Integer
    [LeetCode#50] Pow(x, n)
    [LeetCode#240] Search a 2D Matrix II
    [LeetCode#238]Product of Array Except Self
    [LeetCode#171]Excel Sheet Column Number
    [LeetCode#258]Add Digits
    [LeetCode#264]Ugly Number II
    [LeetCode#263]Factorial Trailing Zeroes
  • 原文地址:https://www.cnblogs.com/cnjavahome/p/9350153.html
Copyright © 2020-2023  润新知