Loading...

简单的Python爬虫爬取网页信息

MILLEROS 发布于 2018-06-25

前言

从一开始使用Nodejs编写爬虫爬去网页信息开始,就对爬虫感兴趣了,以为可以做很多低级但是繁琐的事情,当然,这里的低级是一方面是因为以我现在的阶段不需要爬取太多深层次的东西,另一方面则是爬虫的编写水平还跟不上,当然肯定是后者才是重点hhhh。

爬虫的原理

编写网页爬虫一般是对页面进行解析,现在百度一搜索爬虫,最多的就是爬取豆瓣电影等网站的爬虫代码。

爬虫代码

# encoding=utf-8
import requests
import time
from bs4 import BeautifulSoup

_downLoadUrl = 'https://so.gushiwen.org/mingju/'
_count = 0
_all = 200

def getMs():
    millis = int(round(time.time() * 1000))
    return millis

def downLoadPage(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36'
    }
    data = requests.get(url,headers=headers).content
    return data

def getList(doc):
    soup = BeautifulSoup(doc,'html.parser')
    _list = soup.find('div',class_='sons')
    _pageCount = 0
    for _item in _list.find_all('div',class_='cont'):
        _pageCount += 1
        _detail = '[' + str(_pageCount) + '/' + str(_count) + ']' + _item.get_text()

        # print(_detail)
        file = open("gushiwen.txt","a")
        file.write(_detail)
        file.close()

    # global _all
    # _all = int(soup.find('label',id_='sumPage').get_text()) #获取所有页数

    page = soup.find('a',attrs={"class":"amore", "href": True}) #获取下一页
    if page:
        return _downLoadUrl + page['href'].replace('/mingju/','')
    return None

def main():
    url = _downLoadUrl
    _start = getMs()

    while url:
        global _count
        _count +=1

        doc = downLoadPage(url)
        url = getList(doc)

        print('第' + str(_count) + '页已经爬取完成,还剩' + str(_all - _count) + '页待爬取!')

        if (url == None):
            _end = getMs()
            print('爬取完成,耗时' + str(_end - _start) + '毫秒!')

if __name__ == '__main__':
    main()
继续阅读

Nuxt.js -- Vue.js 单页应用的服务端渲染

MILLEROS 发布于 2018-05-15

简介

Vuejs现在几乎成了单页应用的代名词,但作为单页应用,在搜索引擎的友好程度上显然是不行的,当网站爬虫爬取到vue应用时如果没有做过特殊的处理,爬虫通常是爬取不到页面的主要内容的,因为vue单页应用通常使用的都是前端渲染,而爬虫爬取通常是不执行JS的;所以在种种情况下Vue的服务端渲染显然是作为对SEO优化最好的选择,因此Nuxt.js应运而生。

Nuxt.js

Nuxt.js 是一个基于 Vue.js 的通用应用框架,它预设了利用 Vue.js 开发 服务端渲染(SSR, Server Side Render) 的应用所需要的各种配置,同时也可以一键生成静态站点。

作为框架,Nuxt.js 为 客户端/服务端 这种典型的应用架构模式提供了许多有用的特性,例如异步数据加载、中间件支持、布局支持等。区别于其他 vue SSR 框架,Nuxt.js 有以下比较明显的特性。

  • 自动代码分层
  • 强大的路由功能,支持异步数据(路由无需额外配置)
  • HTML头部标签管理(依赖 vue-meta 实现)
  • 内置 webpack 配置,无需额外配置

起步

项目创建

使用基于vue-cli的脚手架工具

$ vue init nuxt-community/starter-template <project-name>

通过此命令行命令初始化一个nuxt项目。

目录结构

├─assets                      资源目录,未编译的静态资源如less、js
├─components             组件目录,例如页面共用的头部,底部
├─layouts                    布局目录,例如所有页面都分成三部分,上中下
├─middleware             用于存放应用的中间件,允许您定义一个自定义函数运行在一个页面或一组页面渲染之前。
├─node_modules           
├─pages                      页面目录,放置页面除去组件后的主要部分
  ├─index.vue
  ├─....                 
├─plugins                    插件 例如jq,swiper等插件的文件
├─static                      静态文件目录
├─store                       vuex store 

vuex store : vuex状态树

配置项

// nuxt.config.js 文件配置
const path = require('path')

module.exports = {
  // Headers of the page
  head: {
    title: '默认共用title', //对应HTML<title />
    meta: [ //对应HTML<meta>
      { charset: 'utf-8' },
      { 'http-equiv': 'pragma', content: 'no-cache' },
      { 'http-equiv': 'cache-control', content: 'no-cache' },
      { 'http-equiv': 'expires', content: '0' },
      { content: 'telephone=no', name: 'format-detection' }
    ],
    // html head 中创建 script 标签
    script: [
      { innerHTML: require('./assets/js/flexible_nuxt'), type: 'text/javascript', charset: 'utf-8'}
    ],
    // 不对<script>标签中内容做转义处理
    __dangerouslyDisableSanitizers: ['script']
  },
  // Global CSS
  css: ['~/assets/css/reset.css', '~/assets/css/main.less'],
  // Global env
  env: {
    __ENV: process.env.__ENV
  },
  build: {
    vendor: ['axios'],
    postcss: [
      require('postcss-px2rem')({
        remUnit: 75
      })
    ],
    extend (config, ctx) {
      if (ctx.isClient) {
        // 拓展 webpack 配置
        config.entry['polyfill'] = ['babel-polyfill']
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
        // 添加 alias 配置
        Object.assign(config.resolve.alias, {
          'utils': path.resolve(__dirname, 'utils')
        })
      }
    }
  },
  plugins: [{src: '~plugins/toast', ssr: false}, {src: '~plugins/dialog', ssr: false}]
}

头部标签配置

Nuxt.js 通过 vue-meta 实现头部标签管理,在 nuxt.config.js 中的 head 配置。所有的页面都会走这个配置,如果想要修改某一页面的title,可以在 pages/yourpage.vue 文件下,添加如下配置,这时该页面的标题就变成了“This is Miller”,其余页面还保持原有标题不变。

<script>
    export default {
        head () {
            return {
                title:'This is Miller'
            }
        }
    }
    //do something
</script>

在config header配置中, __dangerouslyDisableSanitizers: ['script'] 主要是为了不对innerHTML中内容做转义处理

路由

Nuxt.js 依据 pages 目录结构,自动生成 vue-router 模块的路由配置。

详情可查看Nuxt.js官网路由板块

布局

Nuxt.js布局方式如下图所示: Nuxt.js布局方式

布局配置文件存储在layouts文件夹下,默认布局为default.vue:

<template>
  <div id="milleros">
    <AppHeader />
    <nuxt/>
    <AppFooter />
    <AppLoading />
  </div>
</template>

这里<nuxt/>是一个页面的主体,所有/pages/*.vue中的内容都会插入到此标签中;

<AppHeader />、<AppFooter />、<AppLoading />为conponents组件,是所有页面共用的组件。

所以layouts布局就是配置不同组件和页面主体的结构。

我们可以根据不同的页面定制不同的layouts布局:

<script>
export default {
  layout: 'demo_layout',
  //do something
}
</script>

vuex

在根目录创建 store 目录,就会默认引用 vuex 模块,除此之外,还进行了以下的操作: 1)将 vuex 模块 加到 vendors 构建配置中去; 2)设置 Vue 根实例的 store 配置项。

Nuxt.js 支持两种使用 store 的方式:

  • 普通方式:store/index.js 返回一个 Vuex.Store 实例
  • 模块方式:store 目录下的每个 .js 文件会被转换成为状态树指定命名的子模块 (当然,index 是根模块,相当于设置了namespaced: true)

Nuxt.js提供了模块方式的简单写法:使用状态树模块化的方式,store/index.js 不需要返回 Vuex.Store 实例,直接将 state、mutations 和 actions 暴露出来即可。示例如下:

export const state = () => ({
  accesstoken: ''
})

export const mutations = {
  setAccesstoken (state, accesstoken) {
    state.accesstoken = accesstoken
  }
}

asyncData

asyncData方法是服务端渲染的重点!!!

因为asyncData可以在组件(仅限于页面组件)每次加载之前被调用。而且可以再服务端或路由调用之前被调用

Nuxt.js 会将 asyncData 返回的数据融合组件 data 方法返回的数据一并返回给当前组件。

详细查看异步数据
继续阅读

记一次服务器文件被恶意篡改的解决过程

MILLEROS 发布于 2018-05-02

前言

本人也是个linux小白,下面的内容是记录一次服务器找出源文件被篡改的原因的过程。

最近遇到公司服务器文件被篡改的情况,主要表现就是公司官网主页的SEO信息被替换成了广告,一开始以为只是普通的劫持,但是后来上到服务器发现是源文件直接被篡改了,然后找了一圈服务器登录记录以及命令执行记录都没发现有可以目标存在,后来又怀疑是设置了定时任务,然而也并没有发现。在没有明确发现可以目标的情况下,而且下午就放五一小长假了就没去深究,直接把文件改回来了。

刚放完假的第一天回来发现文件再次被篡改,篡改时间为5月1日下午两点十几分,这就让我有点头疼了,但是一圈检查下来还是没有发现任何记录,怀疑是被删掉了或者干脆就是自动某个自动脚本修改的,因为修改之后的源文件编码不对了,原先是utf-8,现在直接就是GBK了。

本来想查看文件修改记录的,但是发现服务器上并没有发现任何监控程序启动,所以只能将文件再次改回去,然后使用最基础的auditd官网来监控整个被篡改过文件的目录。

auditd

使用auditd监控文件变化记录。

到了这里我们其实已经可以先设定一个设计规则了,然后等待上述说到的文件夹被篡改后查看审计报告后在做计划了。

等待

等待所监控的文件再次被修改

文件再次被篡改

服务器首页文件在修正后并且添加了监控的情况下再次被篡改,使用命令

$ sudo ausearch -f /var/www/html

并未发现存在文件修改记录,猜测文件并非从服务器端直接被修改,且注入的文字为HTML实体编码,修改后的文件编码被改变。

猜测为注入攻击

查看网站访问记录

发现可疑访问

查看apache日志发现每次文件被篡改都显示有http://www.link114.cn/ 的访问

whois反查

通过whois反查查到以下注册人信息

  • 名称:陈镇威
  • 邮箱:520vigo@163.com
  • 域名列表:hackerjs.cn、link114.cn、link114.com.cn、netloss.cn、showdownload.org

其中link114.cn为买链帮手,判断是此人所为

查到个人博客

通过google查到此邮箱所关联的网易博客,但最近的博客更新时间为2008年,无法找到更多有用信息

返回梳理发生过程

通过对文件从修改正确到再次被篡改的过程中梳理得出了我从一开始就忽略的问题——文件夹权限问题

通过ls查看/var/www/文件夹权限为777,其下所有文件夹几乎均为权限777,这就会导致一个问题,那就是任何人都可以使用工具向我们的服务器内HTTP PUT文件,而且脚本为最高权限。

调整文件、文件夹权限

/var/www/下的所有文件/文件夹所属用户和组修改为apache,修改权限为755

后来测试发现项目上传文件夹无法上传,将权限修改为drwxrwx---后可以上传了。

总结

通过这次的教训,深刻认识到了服务器文件夹权限的重要性,往严重了说,这次对方修改meta信息算是轻得了,严重了都可以造成整个服务器被格盘或者干脆被当成肉机还不知情。

继续阅读

使用auditd监控文件/文件夹修改记录

MILLEROS 发布于 2018-05-02

什么是auditd

auditd(或 auditd 守护进程)是Linux审计系统中用户空间的一个组件,其负责将审计记录写入磁盘。说白了就是开启的一个守护进程根据指定的规则来监控系统的某些情况变化。

安装audited

我当前使用的操作系统是Centos 6,发现audited默认安装了,要查看audited是否安装了只需输入以下代码

$ sudo auditctl -v

显示了版本信息说明已安装

auditctl version 2.4.5

否则就使用apt-get或者yum、rpm等包管理工具安装

$ sudo yum install auditd

安装完毕后默认以下的工具也会存在

  • auditctl : 即时控制审计守护进程的行为的工具,比如如添加规则等等。
  • /etc/audit/audit.rules : 记录审计规则的文件。
  • aureport : 查看和生成审计报告的工具。
  • ausearch : 查找审计事件的工具
  • auditspd : 转发事件通知给其他应用程序,而不是写入到审计日志文件中。
  • autrace : 一个用于跟踪进程的命令。
  • /etc/audit/auditd.conf : auditd工具的配置文件。

安装完后记得查看auditd是否启动

$ sudo service auditd status

否则使用以下命令启动

$ sudo service auditd start

使用以下命令停止

$ sudo service auditd stop

首次安装 auditd 后, 审计规则是空的:

$ sudo auditctl -l
No rules

如何使用auditd

Audit 文件和目录访问审计

我们使用审计工具的一个基本的需求是监控文件和目录的更改。使用auditd工具,我们可通过如下命令来配置(注意,以下命令需要root权限)。

文件审计
$ sudo auditctl -w /var/www/html/index.html -p rwxa

选项 :

  • -w path : 指定要监控的路径,上面的命令指定了监控的文件路径 /etc/passwd,必须是以‘/’开头的绝对路径
  • -p : 指定触发审计的文件/目录的访问权限
  • rwxa : 指定的触发条件,r 读取权限,w 写入权限,x 执行权限,a 属性(attr)
目录审计

使用类似的命令来对目录进行审计,如下:

$ sudo auditctl -w /var/www/html/

以上命令将监控对 /var/www/html/ 目录 的所有访问。 现在,可以运行 auditctl -l 命令即可查看所有已配置的规则。

$ auditctl -l
-w /var/www/html/index.html -p rwxa
-w /var/www/html/ -p rwxa

查看审计日志

添加规则后,我们可以查看 auditd 的日志。使用 ausearch 工具可以查看auditd日志。

我们已经添加规则监控 /var/www/html/ 文件。现在可以使用 ausearch 工具的以下命令来查看审计日志了。

$ sudo ausearch -f /var/www/html/
----
time->Wed May  2 11:20:31 2018
type=PATH msg=audit(1525231231.453:528): item=1 name="/var/www/html/.index.html.swp" inode=929483 dev=fc:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=DELETE
type=PATH msg=audit(1525231231.453:528): item=0 name="/var/www/html/" inode=921826 dev=fc:01 mode=040777 ouid=0 ogid=0 rdev=00:00 nametype=PARENT
type=CWD msg=audit(1525231231.453:528):  cwd="/var/www/html/"
type=SYSCALL msg=audit(1525231231.453:528): arch=c000003e syscall=87 success=yes exit=0 a0=ea54a0 a1=1 a2=1 a3=0 items=2 ppid=15009 pid=15413 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts4 ses=54129 comm="vim" exe="/usr/bin/vim" key=(null)

配置项:

  • -f 设定ausearch 调出 /var/www/html

解读输出结果:

  • time : 审计时间
  • name : 审计对象
  • cwd : 当前路径
  • syscall : 相关的系统调用
  • auid : 审计用户ID
  • uid 和 gid : 访问文件的用户ID和用户组ID
  • comm : 用户访问文件的命令
  • exe : 上面命令的可执行文件路径

以上审计日志显示文件曾被vim(/usr/bin/vim)改动过。

查看审计报告

一旦定义审计规则后,它会自动运行。过一段时间后,我们可以看看auditd是如何帮我们跟踪审计的。

Auditd提供了另一个工具叫 aureport 。从名字上可以猜到, aureport 是使用系统审计日志生成简要报告的工具。

我们已经配置auditd去跟踪/var/www/html。auditd参数设置后一段时间后,audit.log 文件就创建出来了。

生成审计报告,我们可以使用aureport工具。不带参数运行的话,可以生成审计活动的概述。

$ sudo aureport

Summary Report
======================
Range of time in logs: 05/02/2018 11:18:36.200 - 05/02/2018 12:31:43.069
Selected time for report: 05/02/2018 11:18:36 - 05/02/2018 12:31:43.069
Number of changes in configuration: 4
Number of changes to accounts, groups, or roles: 0
Number of logins: 2
Number of failed logins: 1
Number of authentications: 4
Number of failed authentications: 6
Number of users: 1
Number of terminals: 8
Number of host names: 3
Number of executables: 4
Number of commands: 2
Number of files: 123
Number of AVC's: 0
Number of MAC events: 0
Number of failed syscalls: 790
Number of anomaly events: 0
Number of responses to anomaly events: 0
Number of crypto events: 33
Number of integrity events: 0
Number of virt events: 0
Number of keys: 0
Number of process IDs: 176
Number of events: 1878

如上,报告包含了大多数重要区域的信息。

上图可以看出有 1 次授权失败。 使用aureport,我们可以深入查看这些信息。

使用以下命令查看授权失败的详细信息:

$ sudo aureport -au

Authentication Report
============================================
# date time acct host term exe success event
============================================
1. 05/02/2018 11:42:11 root 14.*.79.* ssh /usr/sbin/sshd no 1161
2. 05/02/2018 11:42:17 root 14.*.79.* ssh /usr/sbin/sshd no 1162
3. 05/02/2018 11:42:17 root 14.*.79.* ssh /usr/sbin/sshd no 1163
4. 05/02/2018 11:42:22 root 14.*.79.* ssh /usr/sbin/sshd yes 1164
5. 05/02/2018 11:42:22 root 14.*.79.* ssh /usr/sbin/sshd yes 1167
6. 05/02/2018 12:31:30 root 14.*.79.* ssh /usr/sbin/sshd no 1866
7. 05/02/2018 12:31:38 root 14.*.79.* ssh /usr/sbin/sshd no 1867
8. 05/02/2018 12:31:38 root 14.*.79.* ssh /usr/sbin/sshd no 1868
9. 05/02/2018 12:31:42 root 14.*.79.* ssh /usr/sbin/sshd yes 1869
10. 05/02/2018 12:31:42 root 14.*.79.* ssh /usr/sbin/sshd yes 1872

从上图可以看出,由一个用户在特定的时间(从上一次登录成功的时间开始计算)授权失败。

如果我们想看所有账户修改相关的事件,可以使用-m参数。

$ sudo aureport -m

Account Modifications Report
=================================================
# date time auid addr term exe acct success event
=================================================
<no events of interest were found>

我这里因为刚开启auditd服务所以并未有记录存在。

Auditd 配置文件

我们已经添加如下规则:

  • $ sudo auditctl -w /var/www/html/index.html -p rwxa
  • $ sudo auditctl -w /var/www/html/

现在,如果确信这些规则可以正常工作,我们可以将其添加到 /etc/audit/audit.rules 中使得规则永久有效。以下介绍如何将他们添加到/etc/audit/audit.rules中去。

$ sudo vim /etc/audit/audit.rules

# This file contains the auditctl rules that are loaded
# whenever the audit daemon is started via the initscripts.
# The rules are simply the parameters that would be passed
# to auditctl.

# First rule - delete all
-D

# Increase the buffers to survive stress events.
# Make this bigger for busy systems
-b 320

# Feel free to add below this line. See auditctl man page

-w /var/www/html/index.html -p rwxa
-w /var/www/html/

编辑完后保存,然后重启auditd守护进程

$ sudo service auditd restart

参考文档:

继续阅读

linux NAT端口转发实现nodejs网站https访问

MILLEROS 发布于 2018-04-30

什么是certbot?

在开始这个教程前我们必须要了解的就是什么是certbot。

certbot是Let's Encrypt为了用户方便生成https证书而开发的自动化程序,通过certbot我们可以生成四个文件分别用于为https加密所用。

安装certbot

首先最保险的方法是你需要上Certbot的官网来选择您的服务器对应的操作系统版本

cerbot

我这里使用的语言是nodejs,操作系统是CentOS7,接着下面会出现教程教你如何安装certbot:

certbot安装方法

这里官网提供的方法是使用yum来安装Certbot

$ sudo yum install certbot

但是在CentOS7上,使用yum安装的certbot很可能会出现所安装的certbot和pyOpenSSL版本不对的情况(什么都别说,我就遇到了,可是把我折腾苦了),那么什么是版本不对呢?出现以下错误就说明你的yum安装的pyOpenSSL不对:

Traceback (most recent call last):
  File "/usr/bin/certbot", line 9, in <module>
    load_entry_point('certbot==0.13.0', 'console_scripts', 'certbot')()
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 378, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 2566, in load_entry_point
    return ep.load()
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 2260, in load    entry = __import__(self.module_name, globals(),globals(), ['__name__'])
  File "/usr/lib/python2.7/site-packages/certbot/main.py", line 17, in <module>
    from certbot import client  File "/usr/lib/python2.7/site-packages/certbot/client.py", line 10, in <module>
    from acme import client as acme_client
  File "/usr/lib/python2.7/site-packages/acme/client.py", line 31, in <module>
    requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3()  # type: ignore  File "/usr/lib/python2.7/site-packages/requests/packages/urllib3/contrib/pyopenssl.py", line 112, in inject_into_urllib3
    _validate_dependencies_met()
  File "/usr/lib/python2.7/site-packages/requests/packages/urllib3/contrib/pyopenssl.py", line 147, in _v
alidate_dependencies_met
    raise ImportError("'pyOpenSSL' module missing required functionality. "ImportError: 'pyOpenSSL' module missing required functionality. Try upgrading to v0.14 or newer.

使用pip安装绕过yum安装的版本不对问题

错误提示使用v0.14或者更新版本,先看看pip中的版本:

$ pip search certbot

显示版本为0.14.0;

卸载yum安装的certbot和pyOpenSSL:

$ yum remove certbot pyOpenSSL

升级pip(如果不是最新版本,pip会提示你升级后再使用):

$ pip install –upgrade pip
pip安装certbot: pip install certbot。 碰到了Python.h或者pyconfig.h找不到的错误,于是安装Python的devel包: yum install -y python-devel;再次运行命令,提示找不到opensslv.h头文件,于是安装OpenSSL的devel包: yum install -y openssl-devel;再次运行pip install certbot命令,成功安装certbot;pip安装certbot: pip install certbot。 碰到了Python.h或者pyconfig.h找不到的错误,于是安装Python的devel包: yum install -y python-devel;再次运行命令,提示找不到opensslv.h头文件,于是安装OpenSSL的devel包: yum install -y openssl-devel;再次运行pip install certbot命令,成功安装certbot;

测试certbot是否可用: certbot certificates。输出正常,说明pip安装了最新版的certbot,并且能正确运行。

生成https证书

前面安装完certbot之后就到了最重要的地方了,就是生成https证书。

首先运行下列命令

$ certbot certonly

会出现两种选择,我们选择第二种

How would you like to authenticate with the ACME CA?
-------------------------------------------------------------------------------
1: Spin up a temporary webserver (standalone)
2: Place files in webroot directory (webroot)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

接着这里我们需要输入自己的域名:www.mydomain.com (注意这里有一个空格) mydomain.com,我们这里绑定的是两个域名,一个是带www的,一个是没有www的,如果你只需要其中一个的话这里可以只写一个

Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'to cancel):

这里要求我们输入网站的根目录

Select the webroot for milleros.com:
-------------------------------------------------------------------------------
1: Enter a new webroot
-------------------------------------------------------------------------------
Press 1 [enter] to confirm the selection (press 'c' to cancel):

如果前面输入域名的时候您输入的是两个则这里会再次提示输入网站根目录:

Select the webroot for yourdomain:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /root/www/static(你前面输入过的网站根目录)
-------------------------------------------------------------------------------

如果根目录一致的话我们就选择2.

输入完根目录之后certbot会自动为我们生成证书,生成成功会有以下提示:

Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/xxx.ml/fullchain.pem. Your cert will
   expire on 2017-06-07. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot again. To
   non-interactively renew *all* of your certificates, run "certbot
   renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
注意:这里我遇到过一个很坑爹的问题,就是输入了网站根目录之后certbot提示我生成证书时认证失败:
Failed authorization procedure. example.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://example.com/.well-known/acme-ch    <head><title>404 Not Found</title></head>
    <body bgcolor="white">    <center><h1>404 Not Found</h1></center>    <hr><center>", www.example.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://www.example.com/.well-known/acme-challenge/k
    <head><title>404 Not Found</title></head>
    <body bgcolor="white">    <center><h1>404 Not Found</h1></center>    <hr><center>"

    IMPORTANT NOTES:
     - If you lose your account credentials, you can recover through       e-mails sent to example@example.com.
     - The following errors were reported by the server:

       Domain: example.com
       Type:   unauthorized
       Detail: Invalid response from
       http://example.com/.well-known/acme-challenge/wGNv57IGJjHQ9wyzzALktpNaPzfnTtN3m7u3QuO4p40:       "<html>
       <head><title>404 Not Found</title></head>
       <body bgcolor="white">       <center><h1>404 Not Found</h1></center>       <hr><center>"

       Domain: www.example.com
       Type:   unauthorized
       Detail: Invalid response from
       http://www.example.com/.well-known/acme-challenge/kFJ0CSuKOdgcT2xmciB4GGNCcnUPoIbpQmA9jOII_Bk:       "<html>
       <head><title>404 Not Found</title></head>
       <body bgcolor="white">       <center><h1>404 Not Found</h1></center>       <hr><center>"

       To fix these errors, please make sure that your domain name was
       entered correctly and the DNS A record(s) for that domain
       contain(s) the right IP address.
     - Your account credentials have been saved in your Certbot
       configuration directory at /etc/letsencrypt. You should make a
       secure backup of this folder now. This configuration directory will
       also contain certificates and private keys obtained by Certbot so
       making regular backups of this folder is ideal.

这里我研究了很久,网上很多人说可能是网站根目录下生成的文件夹.well-known 权限不够,但是我去根目录下看了,连那个文件夹都没看到,所以确定肯定不是权限问题,于是我在分析了一下错误信息里面包含404 not found的错误信息,可能是路由的问题,于是我在重复certbot生成https证书的过程中到了输入根目录的时候在结尾对加了一级/public(我用来存放客户端代码的文件夹),发现果然可以用了,顺利生成了https证书;

创建https服务器

经过刚刚的certbot操作,我们已经生成了如下四个文件:

  • cert.pem
  • chain.pem
  • fullchain.pem
  • privkey.pem

首先我们需要在原先的http服务器上做如下改动:

var express = require('express');
var fs = require('fs');
var http = require('http');
var https = require('https');

//引入刚刚生成的https文件
var privateKey  = fs.readFileSync('/path/to/privkey.pem', 'utf-8');
var certificate = fs.readFileSync('/path/to/cert.pem', 'utf-8');
var ca = fs.readFileSync('/path/to/fullchain.pem', 'utf-8');
var credentials = {key: privateKey, cert: certificate ,ca:ca};

//实例化
expressvar app = express();

//创建http以及https服务
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);

//监听端口
httpServer.listen(8321);
console.log('http server start on port 8321');
httpsServer.listen(8322);
console.log('http server start on port 8322');
//默认的http协议端口是80端口,https端口是443端口。

大功告成 然后我们可以直接使用app来进行各种路由操作,现在就大功告成啦:

只有https访问

如果您不想要http访问,只想要https访问,只需要修改我们之前的两条规则:

//var httpServer = http.createServer(app); //之前的创建http服务器
var httpServer = http.createServer(function(req,res){
    res.writeHead(301, {'Location': 'https://milleros.com/'});
    res.end();
}); //将http请求重定向至https
var httpsServer = https.createServer(credentials, app);

上面这段代码的重定向存在一点问题,就是当我们访问的地址不是主页时,而且请求http时则会跳转回https的主页,这显然不是我们想要的结果,所以我们把原先的代码改成这样

var express = require('express');
var fs = require('fs');
var http = require('http');
var https = require('https');

//引入刚刚生成的https文件
var privateKey  = fs.readFileSync('/path/to/privkey.pem', 'utf-8');
var certificate = fs.readFileSync('/path/to/cert.pem', 'utf-8');
var ca = fs.readFileSync('/path/to/fullchain.pem', 'utf-8');
var credentials = {key: privateKey, cert: certificate ,ca:ca};

//实例化
expressvar app = express();

//创建http以及https服务
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);

//监听端口
httpServer.listen(8321);
console.log('http server start on port 8321');
httpsServer.listen(8322);
console.log('http server start on port 8322');
//默认的http协议端口是80端口,https端口是443端口。

//新增一个路由来拦截请求
app.use(function(req,res,next){
    console.log(req.protocol);
    if(req.protocol != "https"){
    res.writeHead(301, {'Location': 'https://milleros.com' + req.path});
    res.end();
    }
    next();//保证请求不被阻塞
});

这样就可以保证,网站中的每一个http请求都可以被重定向至https了。

结语

linux是一个很复杂的操作系统(至少我这么认为),在我们平时进行的操作中经常会遇到各种各样的问题,有些问题在网络上我们可以很轻易的找到解决问题的方法,但是往往很多时候我们遇到的问题是很特殊的,网上几乎很难找到解决的办法,这个时候我们不应该着急,而应该静下心来仔细捋一捋思路,找找可能会导致问题出现的地方。

正是因为linux的复杂性,很多时候我们误操作会导致很多不可预料的问题,像这次我就是因为pyOpenSSL版本不对的问题在寻求很多方法未果之后选择了对系统自带的Python进行升级,正是因为我这一疯狂的想法从而让我误删除了site-packages,这一误操作直接导致的就是yum和pip无法使用,从而让我不得不重置了整个linux系统。

虽然对linux的误操作很容易造成不可估量的错误,但我还是鼓励大家大胆尝试,毕竟只有不停地跌倒才会有更多爬起来的经验,才能让我们更加的强大!

继续阅读

Ueditor + HighLightJs实现代码块高亮

MILLEROS 发布于 2018-04-30

百度Ueditor

Ueditor是百度的一款富文本编辑器,目前在国内大多数网站都会使用到它,而今天我们就来看看,像我们这种三句不离代码的码农对于代码高亮来说是再基本不过的功能了,那么何为代码高亮呢?

普通代码显示 举例一段html代码

<!DOCTYPE html>

<html>

    <head>

        <meta charset="UTF-8">

        <title>Milleros的官方网站</title>

    </head>

    <body>

        <p>欢迎来到milleros的官方网站</p>

    </body>

</html>



这是一段普通的未加任何修饰的html代码段,像这样的代码存在于网页上,我相信如果是我,我连看的勇气都没有。

高亮代码显示 还是上面那段代码,但是现在加上高亮功能

<!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>Milleros的官方网站</title>
        </head>
        <body>
            <p>欢迎来到milleros的官方网站</p>
        </body>
    </html>

可以看到上面的代码的可读性变高了许多。

如何使用 使用方法很简单,当我们使用Ueditor编辑文章时可以在插入代码时点击Ueditor工具栏上的

snipaste_20170528_123657.png

来选择对应的语言,如果工具栏没有这个选项的话就说明您的编辑器没有默认配置这个功能,这个时候您需要对Ueditor的配置文件ueditor.config.js进行相应功能的配置。选择对应语言之后Ueditor编辑器内会出现专门用于放置代码块的pre标签

snipaste_20170528_124333.png



接着需要在我们需要渲染代码文章的页面引入两个文件

样式文件:

<link rel="stylesheet" type="text/css" href="/ueditor/third-party/SyntaxHighlighter/shCoreDefault.css">

脚本文件:

<script type="text/javascript" src="/ueditor/third-party/SyntaxHighlighter/shCore.js"></script>

激活高亮:

当上述文件均被引入之后,需要在页面js内写入下面的代码来执行代码高亮

SyntaxHighlighter.all();
            提示:代码高亮的执行需要在DOM加载完之后再执行,也就是需要执行代码高亮的代码已经被渲染到页面上之后再执行代码高亮。

代码高亮主题 官方主题分类 对于这种视觉上优化的功能,没有主题切换肯定是不“人道”的,百度Ueditor使用的代码高亮工具是第三方的SyntaxHighlighter高亮工具,从它的官网可以看到官方提供了自己的主题

    snipaste_20170528_124928.png

官方主题使用
    官方对于主题的应用,显然也考虑到了简单易用的原则;对于上述的主题样式我们可以使用
<link type="text/css" rel="Stylesheet" href="PATH/Styles/shThemeDjango.css"/>
    来更换对应的主题文件。

主题文件
    对于我们使用的Ueditor,因为是使用第三方库的原因,可能会存在主题文件不完整的问题存在,像楼主使用的是nodejs版的Ueditor,而内置的主题文件只有默认样式,所以我们需要把缺失的主题文件放到对应的文件夹内。具体的位置需要大家自己去看下 Ueditor的目录结构。
继续阅读

有些人25岁就死了,75岁才埋下

MILLEROS 发布于 2018-04-30

在很小很小的时候,我并不懂什么是理想,什么是梦想。

  • “你的理想是什么”

  • “我长大后想当警察,为民除害“

相信这个情景在我们是孩童时代经常会遇到的,那时候的我们总是带着小小的天真坚定说出自己可能并不了解的理想。

初中,依稀记得每次写到有关理想的作文,我总是写着自己要当警察的理想,但是随着我每次对梦想的探讨,都会让我加深对自己理想的动摇,因为我发现,每一次写一篇文章下来,总是绞劲脑汁都无法深入到这个理想的精髓。

高中是我动摇最大的时代,那三年让我彻底的摒弃了自己坚持了从小学到初中结束的理想,因为此刻的我发现,理想就是理想,是理想的生活,是理想中的理想。

我的大学南门有一排写字间,是学校划分给即将毕业的学生创业孵化的摇篮。记得我第一次来到这里的时候是得知自己被录取后,第二天就和妈妈来到了学校,那时候第一眼见到这一排的创业公司,觉得特别吃惊,因为在这时候的我看来,这些把自己的梦想亲自塑造出来的人该是多么的厉害。

某一个晚上,和舍友的闲聊

  • “我刚刚去创意园了“

  • “你去创意园干啥“

  • “我有一个师兄在那边开了自己的公司,我就过去坐一坐了“

  • “那你师兄是做啥的“

  • “他开的是买衣服的“

  • “卖衣服!?“

  • “对,实体店加淘宝店,他以前是玩乐队的,他说以前深职的乐队特别多,玩摇滚的也很多,听他说当时还专门出了一个深职的专辑,不过没有发行,他卖的衣服都是自己设计的“

昨天晚上

  • “那位师兄不做了,他打算自己去找工作了”;

我当时很吃惊(还是这个词),吃惊之后就是满满的无奈。

每个人活着都有自己想做的事,可能对于我来说,大学就是提供给了我最好的时机做自己最喜欢的事,当然对于大多数的大学生来说同样也是如此,但是每每在我们努力朝着自己的方向前进的时候,深深的无力感总会压迫我们脆弱得一塌糊涂的坚持,也许某一天就会垮下去,接受社会的冲刷,让我们认清楚,不管理想有多么的伟大,最后终究会沦为生活的玩偶。这个时候我们或许是25岁了吧…


有些人25岁就死了,75岁才埋下。

继续阅读

我终于知道我为什么喜欢翻唱了

MILLEROS 发布于 2018-04-30

8月2号到8月10号,仔细一想已经过去了八天了。

在过去的八天里,努力的想回到曾经的生活,做任何自己曾经想做的事。

每天9:20准时到达公司,花一分钟打开电脑打开网易云音乐,打开私人FM,又开始了与网易云一整天的日子。偶然看到一个歌评:无法想象一个人在网易云上听的歌超过8000首是有多么的孤独;我心里一惊,立马看了一下自己听过的曲目数。

-“哈,还好只有4291首”

嗯,再打开听歌排行,映入眼帘,一周内最高播放量的是哪吒乐队的闹海;所有时间排行内Love yourself依然是最高的那个。

这几天一直在收集各种翻唱歌曲,就像此刻的心情,既然无法回到原来的日子,那就干脆寻找有以前生活的影子的生活呗。

世界上痛苦的事情莫过于明明很不开心却依然要坚持微笑。

为什么会喜欢翻唱?

对于翻唱的歌曲,一般都会加入自己的改编,而这种改变似乎更能唤起处在某个特殊时期的人的情感,注意这里说的是“处在某个特殊时期”,原版可能是对于大部分人都是很有感染力的,但是可能不会像翻唱那般深的情感。所以现在很多的翻唱都是比较小众化的,直接反映出来的情况就是翻唱的乐评一般都会比原唱少很多;当然这里我也不会否认某些翻唱会比原版还更加火。

当然喜欢翻唱可能也不仅仅是因为翻唱更能够表现自己的心情,也有可能是对比于原唱,翻唱的风格更加不一样了,也就是常提到的“新鲜感”。

最近还看到一句很扎心得乐评:

-“当私人FM推荐的歌都是乐评少于999的时候说明网易云开始和你玩真的了”

这句话的意思可能就是说:

   -“现在没人真正明白你了,也就只有我了”

今天真是瞎比比了一堆没用的废话,总之就是想说,任何时候都只能依靠自己吧,需要在未来的日子里继续加油~

最后再推荐一首扎心得歌:

其实说了半天,也就是只有这里才能让我心里有点归属,说自己想说的,记录下来,未来的日子还长,谁不比谁高贵,扎心也好,无痛呻吟也罢,一切的一切都是自己的心里所想,做自己想做的,顺其自然,一切都是开心就好哈哈哈~~~

继续阅读

其实我也喜欢哆啦A梦

MILLEROS 发布于 2018-04-30

其实写这篇文章最初的想法是源于一个人对于哆啦A梦的喜爱,所以正好唤醒了那个念头吧。

  我们喜欢过的东西太多了,可能喜欢哆啦A梦只是你所有爱好中的其中一个吧。



“不过怎样都是经典我都会接受”

你是觉得幼稚吗?

   那是15年的高考季,在没有任何的期待中我看到了关于《哆啦A梦:伴我同行》在大陆上映的消息,因为从小就比较喜欢蓝胖子的缘故,我毫不犹豫的和身边的朋友提议在周日下午去电影院看。



   ——“不会吧,这些不是小孩子看的吗?”



   那天的我一直沉浸在对这句话的思考中。是啊,可能我就是一个成熟外表装着年幼的心的人吧。

   从那时候开始我就再也不敢和人说我喜欢哆啦A梦了。

我觉得我又有了新的喜好

几年之后我上了大学,从小就被人灌输大学很自由的思想,于是很多藏在心里的喜好都被勇敢的表现出来了,当时正是因为我喜欢Taylor Swift这件事,让我和他渐渐熟络了起来。

他是当时我们班我认为和我最像的一个人,在那个下午我们两个就一直坐在电脑面前,互相分享自己对于自己喜欢的歌手的热爱。在那个过程中他重新定义了我心中对于热爱的定义,他对于欧美歌手的了解远远胜过于我,而正是那时我才开始接触摇滚。

——“我才发现中国摇滚很牛逼”

   后来一段时间他和我说了上面一句话,我觉得很奇怪,一向喜欢欧美摇滚极少关注中国摇滚的他居然开始将喜爱转向中国摇滚了。



   ——“下个月XX号有个音乐节,里面有很多中国摇滚的乐队,你要去吗?”



   那时候的我对于摇滚没有多大的兴趣,所以就没有和他去。



   在后来的一段时间里他经常去各种音乐节现场,而在那段时间里我开始迷上了前端技术,说的这么好听其实就是打代码的。也就在那段时间里我开始朝着自己喜欢的而且觉得对自己有用的方向发展,所以我变得和他越来越不同了。

你以为就你喜欢的东西有用?

——“其实我觉得你经常去那种音乐节现场听摇滚并没有什么用,以后又不能帮助你找工作。”

   在一个平常的午后我对他说了上面那句话。



   我承认这是我说过的最傻逼的一句话。

   在后面很长一段时间内我都会想起当时他平静的表情,从不敢揣摩他当时的想法。

喜欢一样东西有错吗?

   我特别喜欢用网易云边听歌边看下面的评论,因为好像只有这里才能找到和我有一样爱好的人。

我这个人真的是像大雄一样,又冒失又迟钝还不爱学习,又弱势又懒惰还犹豫不决,运动差劲还胆小又健忘,毫不可靠又怕麻烦 一点主见都没有学东西又慢 ,老好人,一得意就忘形,就会撒娇 啊咧!这可怎么办,真是头疼 。而且我没有多啦A梦。那么,就靠自己吧。(来自网易云音乐《ひまわりの約束》222fool的评论)

其实今天的目的就是为了告诉大家,喜欢就是喜欢,不要因为别人的不理解而不愉快,每个人都有自己的想法,因为你喜欢的东西只有你懂得它给你带来的那种心悸。

继续阅读

你认为他真的优秀吗?

MILLEROS 发布于 2018-04-29

就这样 随便吧

嘲笑我们的孤独吧

我们生来不属于什么地方

前言 我一直在想自己是否优秀,这不是自恋,我对自己成长的每一次回顾不能说都很彻底,但是也总是能让我自己得到很多收获。当然今天我们要讨论的是“你认为他真的优秀吗?”,这个题目看起来似乎有点不正经,有点背后说别人短处的嫌疑,但是我要告诉你,今天讨论的话题虽然和TA有关,但是方向却不是TA,而是自己。

那是我的一个算是比较熟的朋友吧,还记得当初认识他的时候是因为部门活动。

   -“你真厉害,好羡慕你们能通过自己的手画出自己的想法的人”

   -“哈哈,我只不过是一个刚刚入门的新手而已,哪里厉害了”

而后来我又得知他在学院的某个比赛上那个第二名,更是觉得这是一个让我学习的楷模,在那一段时间里我总希望能通过多与他接触来提升自己,虽然我根本不知道能学习到什么,但就是抱着这种没有方向的念想,也许我就是被他人的优秀蒙蔽了双眼吧,又或者是我太急于优秀的念想让我没有看清努力的方向。

而在后来的一段时间内接触,发现他其实真的就是很普通的人,而且每次在他被人夸赞的时候已经会有意无意的绕过这个问题了,我知道这个时候这已经要成为他的烦恼了。

是优秀的烦恼还是强加优秀的烦恼? 可能就是当初首次见面的印象太好,才会让我给他强加上了优秀的称号,而这个强加的称号已经渐渐让他变得有些反感了;

在后来很长一段时间内我都没有再说过他优秀了。

-“你觉得你自己真的优秀吗?“

-“优不优秀取决于你对于优秀的定义“

你觉得他真的优秀吗? 大概是我就是生活在一个任何人都比我强的环境里面吧,我从小到大都习惯了平庸,对于同伴对他人的称赞从来都是不绝于耳的。

   -“好厉害啊,连这个你都会”

   -“还好啦,就是平时接触到的比较多”


   -“和你比起来我觉得自己特别普通”

平庸并没有什么错 不知什么时候,我也被某些人冠上了优秀的称号,起初的我觉得这些优秀的称号根本就是无稽之谈,我觉得我有太多的东西需要学习了。但是随着时间的推移,我似乎已经有点无法抵挡这种类似于渗透的“优秀”了,经常性的发现自己会偶尔迷失在关于自己优秀的幻想里面了。这个时候我真的好害怕自己会失去自己最初的本质,我想保持自己的平庸,我觉得只有这样我才能走得更远。

后记 今晚又注定是一个不眠夜,最近太多的东西夹杂在一起,感觉压力有点大了。就在今天才进行完的模拟竞选上,这次我坐的位置是前排了;一年下来,我从坐后几排坐到了第一排,这不得不说是我整个大学生涯里面比较大的进步吧。我觉得我很幸运我能拥有很多优秀的同事以及很多优秀的干事,但这次我不会再说他们优秀了。

   这是我第一次开通个人博客,文章写的也很水,对于这个个人博客,起初的想法仅仅只是想把自己刚刚学会的新技术拿来练练手,但是后来又发现在生活中我有太多值得思考的东西了,我需要的是一个能记录我这些琐碎思绪的工具,现在这个博客系统就已经承载了这个责任了。
继续阅读