node 09 sockect.io 即时聊天工具
<!DOCTYPE html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* {
margin: 0
padding: 0
box-sizing: border-box
}
body {
font: 13px Helvetica, Arial
}
form {
background:
padding: 3px
position: fixed
bottom: 0
width: 100%
}
form input {
border: 0
padding : 10px
width: 90%
margin-right: 0.5%
}
form button {
width: 9%
background: rgb(130, 224, 255)
border: none
padding: 10px
}
list-style-type: none
margin: 0
padding: 0
}
padding: 5px 10px
}
background:
}
</style>
</head>
<body>
<ul id = 'messages'></ul>
<form action = ''>
<input id = 'm' autocomplete = 'off' />
<button>Send</button>
</form>
<script src = 'https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.0/socket.io.js'></script>
<script src = 'https://code.jquery.com/jquery-2.2.4.js'></script>
<script>
/*
Socket.IO支持及时、双向与基于事件的交流。它可以在每个平台、
每个浏览器和每个设备上工作,可靠性和速度同样稳定。
Socket.io是一个WebSocket库,包括了客户端的js和服务器端的nodejs
,它的目标是构建可以在不同浏览器和移动设备上使用的实时应用。
它会自动根据浏览器从WebSocket、AJAX长轮询、
Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,
非常方便和人性化,而且支持的浏览器最低达IE5.5
*/
$(function(){
var socket = io()
// 表单的提交事件
$('form').submit(function(e) {
e.preventDefault()
// 给服务器发送信息
socket.emit('chat message', $('
$('
return false
})
// 接收到的信息
socket.on('chat message', function(msg) {
$('
})
})
</script>
</body>
</html>
index.html
// 使用express 服务器
var app = require('express')()
var http = require('http').Server(app)
var io = require('socket.io')(http)
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html')
})
http.listen(3000, function() {
console.log('listen on * :' + 3000)
})
io.on('connection', function(socket) {
// 用户已经来连接了
console.log('a user connected')
// 响应用户发送的信息
socket.on('chat message', function(msg) { // 给
console.log('chat message ' + msg)
// 广播给所有人
io.emit('chat message', msg)
})
socket.on('disconnect', function() {
console.log('user disconnected')
})
})
index.js
node10 电影天堂爬虫实战
const originRequest = require('request')
// request 对原生的http 进行封装
const iconv = require('iconv-lite')
// 电影天堂使用gb2132 进行编码,无法在js 中的 utf-8 中进行处理
const cheerio = require('cheerio')
// 对内容的提取
function request(url, callback) {
const option = {
encoding: null
}
originRequest(url, option, callback)
}
for (let i = 100553
const url = `https://www.dy2018.com/i/${i}.html`
request(url, function(err, res, body) {
//
const html = iconv.decode(body, 'gb2312')
// console.log(html)
const $ = cheerio.load(html)
console.log($('.title_all h1').text())
})
}
node10 阿里ECS 服务器
node 12 sql 基础
/*
数据库是什么?
数据库是一种专门管理数据的软件
1 可以处理大量数据 2 有统一的程序读写接口比如sql ,3可以通过统一的语法处理关联查询和统计
数据库基础
目前常用的数据库, 就是关系型数据库, mysql, oracle sqlserver
表, 字段 记录
二位表的形式来存储数据的
学号 姓名 成绩
1 李云龙 60
表的每一行成为记录(record) , 记录是一个逻辑意义的数据
表的每一行成为字段, 同一个表的每一行记录都拥有相同的若干字段,
每一行成为一条记录
数据类型
每一个字段都需要定义数据类型
名称 类型 说明
INT 整型 4字节整型数据,
BIGINT 长整型 8字节整型数据,
REAL 浮点型 4字节浮点数据,
DOUBLE 浮点型 8字节浮点数据,
DECIMAL(M,N) 高精度小数 由用户指定精度的小数,DECIMAL(20, 10)
表示一共20位, 其中小数为10
CHAR(N) 定长字符型 存储指定长度的字符串, CHAR(100),
可以存储100个字符的字符串
VARCHAR 变长的字符串 存储可变长度的字符串
BOOLEAN 布尔类型 存储true , false
DATE 日期类型 存储日期: 2019-06-22
TIME 时间类型 存储时间: 12:20:50
DATETIME 日期和时间类型 存储日期+时间类型 例如 2018-06-22 12:20:59
主键 PrimaryKey
对于关系表, 有个重要的约束, 任意两条记录不能重复, 不能重复不是两条
记录不完全相同, 而是指能够通过某一个字段唯一区分出不同的记录
自然主键: 姓名和学号, 基本不靠谱, (名字和可以同名, 学号可以后补)
自增主键: 数据库会在插入数据时,自动为每一条数据分配一个自增的整数,
这样不用担心主键重复, 也不用自己预先生成主键
UUID 局唯一GUID类型, 两个表
使用一种全局唯一的字符串作为主键, 类型 8f55d896b, GUID算法通过网卡MAC地址
时间戳和随机数保证任意计算机在任意时间生成的字符串是不同的, 大部分语言都
内置了GUID算法, 可以自己预算出主键
一个表中唯一的字段
SQL struct query language : 结构化查询语言
启动mysql
(1) start mysql sqlserver (系统偏好设置)
(2) 任意的终端输入: mysql -u root -p liyang226097
登陆:mysql -h localhost -u root -pliayang226097
-h 跟上数据库的ip地址(localhost 表示本地地址: 127.0.0.1)
-u mysql数据库的登陆名称 (root 超级管理员)
-p 跟上数据路的端口号: 默认3306 所以 -P 可以省略
-p 跟上mysql 的数据库密码: (这里不推荐直接跟在-p后面
回车之后再书写)
退出: exit
(2) show databases
三个系统库 information_scaema mysql
performance_schema sys
(1) 创建数据库,: 数据库名称test, 字符符合utf8 的字符集
create database test character set utf8 collate utf8_general_ci
(2) 切换数据库, 切换到test 数据库
use test
(3) 创建表 , 进入test 后, 再创建表
mysql> CREATE TABLE IF NOT EXISTS TBL_RESULT (
-> id INT UNSIGNED AUTO_INCREMENT,
-> name VARCHAR(100) NOT NULL,
-> score INT UNSIGNED NOT NULL,
-> PRIMARY KEY (id)
-> )
Query OK, 0 rows affected (0.10 sec)
如果表TBL_RESULT 不存在, 则创建表,
具有三个字段 id, name, score 对应类型为 无符号整型, 字符串型, 和无符号整型
primary id 为id, id 为自增的整数数据
(4) 展示表, 在test 库中展示数据
show tables
(5)desc TBL_RESULT
展示表 TBL_RESULT 创建时的配置,各个字段的类型和primary key
(6) 将TBL_RESULT表中 socre 字段修改为 20 长度的无符号整型
ALTER TABLE TBL_RESULT MODIFY COLUMN socre INT(29) unsigned
(7) INSERT INTO TBL_RESULT (name, score) VALUES('李云龙', 20)
给表TBL_RESULT 增加数据, name: 李云龙 score: 20, idw为自增的数据,不需要处理
(8) SELECT * from TBL_RELULT
查询所有的数据在 TBL_RESULT
(9) 修改数据
UPDATE TBL_RESULT SET score = 80 WHERE name = '李云龙';
将name 为李云龙的score 修改为80
(10) 删除数据
DELETE FROM TBL_RESULT WHERE name = '李云龙'
删除name 为李云龙的数据,在TBL_RESULT 中的数据
(11) 表中的数据全部查询
SELECT * FROM TBL_RESULT
(12) 条件查询, 在TBL_RESULT表中 ,分数大于60的,现实名字和分数, 按照分数的降序排列
SELECT name, score FROM TBL_RESULT WHERE score >60 ORDER BY score DESC
(13) 分组查询, 按照名字分组 , 查询name, 和 score 的平均值, 在TBL_RESULT 表中
select name AVG(score) FROM TBL_RESULT BY name
(14) 连接查询
连接查询, 有两种连接方法
1 内连接: 必须满足连接条件的数据才会展示在结果集,
如果一个数据没有对应的字段,则不会展示
2 外连接, 即使不满足连接条件也会展示, 外连接,需要设置一个主表
以主表的数据为核心, 副表的数据添加到主表,副表中缺少数据,则设置
null
使用外链接时:
SELECT a.name, a.score, b.position FROM TBL_RESULT a LEFT JOIN
TBL_POSITION b ON a.name = b.name
使用左联, 展示a表的名字, 分数 b表的职位 , 根据a ,b 表的名字进行连接
(15) DROP TABLE TBL_POSITION
删除表TBL_POSITION
(16) ysql> CREATE TABLE IF NOT EXISTS TBL_POSITION(
-> name VARCHAR(100) NOT NULL,
-> position VARCHAR(100) NOT NULL,
-> PRIMARY KEY (name)
-> )
TBL_POSITION 表的建立
*/
node13 oauth 认证
const Koa = require('koa')
const router = require('koa-router')()
const static = require('koa-static')
const app = new Koa()
const axios = require('axios')
const querystring = require('querystring')
app.use(static(__dirname + '/'))
const config = {
client_id: 'a9665f2cb0edd5becd8e',
client_secret: 'b5c2033911c36375300a190707907300f9aff3ce',
}
// 第一步进行服务器认证
router.get('/github/login', async ctx => {
// 重定向github 服务器,进行认证
let path = `https://github.com/login/oauth/authorize`
path += `?client_id=${config.client_id}`
// 跳转到hithub 服务亲进行认证
ctx.redirect(path)
})
// github 服务器 认证后, 将对服务器进行回调, 带有code
router.get('/auth/github/callback', async (ctx) => {
console.log('callbakc .....')
const { code} = ctx.query
console.log('code:' + code)
// 获取github 对应的cod 信息
const params = {
client_id: config.client_id,
client_secret: config.client_secret,
code
}
// 通过code , client_id clicent_secret 获取对应的token
let ret = await axios.post('https://github.com/login/oauth/access_token', params)
const {access_token} = querystring.parse(ret.data)
// 获取github 中用户的token 的信息
console.log('access_token', access_token)
// g
ret = await axios.get(`https://api/github.com/user?access_token=${access_token}`)
console.log('user', ret.data)
const { login, avatar_url} = ret.data
ctx.body = `
<h1>hello ${login}</h1>
<img src = '${avatar_url}' alt = '' />
`
})
app.use(router.routes())
app.listen(7000)
/*
OAUTH (开放授权)
概述: 三方登陆主要基于OAUTH2.0, OAUTH 协议为用户资源的授权
提供了一个安全的开发的而有简易的标准, 与以往的授权方式不同指出是
OAUTH 的授权不会使第三方触及到用户的账号信息(如用户名和密码)
即第三方无需使用用户的用户名和密码就可以申请获取该用户资源的授权
因此OAUTH 是安全的
让其他的服务器告诉第三方登陆的账户的信息
游览器 服务器 github 服务器
1 用户向服务器发起认证
2 服务器向游览器重定向
github 认证, 给游览器
3 github 的认证页面
进行认证, 输入用户名和密码
4 github 第三方服务器的进行认证
回调带认证的code , 对服务器进行回调
设置了回调服务器的地址 从而可以正确回调
5 服务器获取到认证的code,
用code 向github 申请令牌、
6 收到code 返回正确的令牌
7 服务器收到令牌,刷新游览器
对应的页面
*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=<device-width>, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<a href ='/github/login'>Login with github</a>
</div>
</body>
</html>
node 14 单点登录
/*
现在有两个应用 , 应用A, 应用B,
单点登录,三个服务器在不同的域
应用A 认证服务器 应用B
1 应用A进行认证时
跳转到 认证页, 进入认证服务器
认证页面在认证服务器
2 认证服务器认证完成(输入用户和密码)
认证服务器保存用户的登录状态
Session
跳回到应用A, 带回token ,
3 接收到token, 到认证
服务器,使用token 进行核实
核实正确后将产生自身的Session
用户登录状态
完成整个登录过程
``4 在应用B 进行登录时
也会跳转到认证页信息
5 认证服务器已经有用户的认证信息
认证服务器,不需要认证,直接返回
token 信息
6 在应用B中利用获取的token
信息, 进行核实
*/
/*
应用A 认证服务器 应用B,、、
Session 用户登录态 Session 用户登录态 Session 用户登录态
*/
const request = require("request")
/*
1 服务器A登录, 获取当前req.session.user 中是否有user 的信息, 如果有user 信息
正常存在, 正常显示
2 不具有req.session.user 信息时, 会获取 req.query 中是否具有token 信息时,
初次登录时, 没有req.query.token 信息
没有token 时, 直接重定向到认证服务器, 并设置回调的地址 , req.header.host + req.originalUrl
就是初次进入时的url 地址, 通过hash 字段设置
system A 或System B
*/
router.get('/', function(req, res, next) {
let system = process.env.SERVER_NAME
let user = req.session.user
// session 中已经具有 user 信息
if (user) {
// 如果session 中有用户的信息, 则说明已经登录过, 这几返回请求的资源
res.render('index', {
user: user,
system: system
})
} else {
/*
如果session 中没有用户的信息, 则需要到passport 系统中进行认证, 这里区分两种情况
1 如果url 中带有token 信息, 则去passport 中认证token 的有效性,
2 如果url 中没有token 信息, 则去passport进行登录, 如果登录成功,
因为token 很容易伪造, 所以需要去验证token的真伪, 否则任何一个带有token ,都通过验证吧
*/
let token = req.query.token
if (!token) {
// 带上回调地址
res.redirect(`http://localhost:8080/login?redirect=${req.header.host + req.originalUrl}`)
} else {
// 确认token
request(`http://localhost:8080/check_token?token=${token}$t=${new Date().toUTCString()}`,
function(error, response, data) {
if (!error, response.statusCode === 200) {
data = JSON.parse(data)
if (data.error == 0) {
// 这里的userId 信息是加密的, 加密算法内嵌
let userId = data.userId
if (!userId) {
res.redirect(`http://localhost:8080/login?redirectUrl${req.header.host + req.originalUrl}`)
return
} else {
/*
获取userId 后, 可以操作数据库获取用户的详细信息, 用户名和全县
为了放拜年, 直接操作useId,
*/
req.session.user = userId
res.render('index', {
userId: userId,
system: system
})
}
}
} else {
// token 验证失败, 冲去去passport 登录
res.redirect(`http://localhost:8080/login?redirectUrl${req.header.host + req.originalUrl}`)
}
})
}
}
})
/*
3 , 在认证服务器, 确认是否存在req,cookies.token 是否存在, 如果token 存在
则说明登录过, 检查token 是否合法,
(1)获取req.cooki.token , 并判断token 是否合法, 不存在或者不合法 则返回登录页
如果token存在 且存在回调的地址, 重定向到回调地址, 如果不存在回调地址, 则回到首页
在登录页面登录时, 进行验证, 验证通过时, 设置cookie, 当设置了回调地址时,
token = 'passport'
重定向到回调地址, 并通过hash 带上token
*/
// login.js 部分
router.get('/', function(req, res, next) {
let cookies = req.cookies
let token = cookies.token
/*
如果token 存在, 说明登录过, 检查token 是否合法,合法将重定向到原来的页面,并将token 传递过去
原页面对应的系统在收到带有token 的请求后, 应该向passport 系统发起请求检查token
如果cookie 中的token 不存在, 或者不合法, 则返回登录页面, 这里的登录页面是由
passport 系统生成
*/
if (token && service.isTokenValid(token)) {
let redirect = req.query.redirectUrl
if (redirect) {
res.redirect(`http://${redirect}?token=${token}`)
} else {
// 如果不含有重定向的页面, 可以返回系统的首页
res.send('<h1>登录成功!</h1>')
}
} else {
res.render('login') // 登录页
}
})
router.post('/', function(req, res, next) {
let body = req.body
let name = body.name
let password = body.password
// FIXME 密码验证
if (name === 'test' && password === '123456') {
// token 应该按照一定的规则生成, 并持久化
let token = 'password'
res.cookie('token', token, { // 设置cookie
maxAge: 1000 * 60 * 60 * 24 * 30,
httpOnly: true
})
// 回调地址存在
if (req.query.redirectUrl) {
// 回到system A或 sysetm B
res.redirect(`http://${req.query.redirectUrl}?token=${token}`)
} else {
res.send('<h1>登录成功!</h1>')
}
} else {
res.send({
error: 1,
msg: '用户名或密码错误'
})
}
})
// check-token.js
router.get('/', function(req, res, next){
let token = req.query.token
var result = {
error : 1 // 登录失败
}
if (service.isTokenValid(token)) {
result.error = 0
result.userId = 'test'
}
res.json(result)
// 返回验证的结果
} )
/*
1SystemA 首次时, 不具有session 和token 内容, 将重定向到认证服务器,并带上
回调url, 进入认证服务器时, 此时不具有token 信息, 将显示登录页面
登录页面填写登录信息后, 进行提交, 验证通过后, 在cookie 中设置token 信息
如果存在回调url 地址, 则重定向到回调地址,并带上token, 不存在url地址
回到主页。用户和密码验证不通过,显示错误
2 再次回到systemA 时, 仍不具有sesssion 但有token , 发送请求 check_token
验证token 时, 验证通过时, 返回用户信息userId, 验证不通过,返回错误
当接收到返回, 没有错误,且具有用户信息时, 设置session 信息userId, 正常显示页面
当收到正常返回但没有userId 信息或者错误信息时, 重定向到登录页面
system B,在System A 已经登录时,首次登录时, 不具有session, 也不具有token
跳转到认证服务器, 在认证服务器,具有用户的 cookie 和token 信息时,
将直接重定向System B 页面, 并带上token 信息, 将依靠token 信息
发起check_token 请求, 流程如上
*/