1、session认证原理
流程大致如下:
- 进入某个平台的页面后会发送一些请求,第一次请求时没有携带cookie,自动条状到SSO进行登录
- 登陆后,SSO会生成sessionID并保存到cookie中,然后重定向到应用平台
- 接着发送的请求会携带cookie,如果session过期,那么跳转到SSO再一次登录
2、window下redis安装与使用
session会话保存到redis集群中,实现服务器共享session。
2.1、安装redis
- 使用wget或者直接到github下载redis压缩包,将压缩包解压到任何文件夹中,比如
D:\redis
- 设置密码,打开redis.windows.conf文件,添加
requirepass ****
,如下图:
-
redis-server.exe
使用本地配置启动rediscd D:\redis redis-server.exe redis.windows.conf
-
运行
redis-cli.exe
以连接到您的redis实例cd D:\redis redis-cli.exe
- 开始使用redis
PS:如果报了(error) NOAUTH Authentication required.是因为设置了认证密码,需要输入密码(就是上面设置的密码)进行认证登入
2.2、使用图形化工具连接redis
Redis React是一个简单易用的用户界面,用于浏览Redis服务器中的数据,该数据由React桌面模板构建,可在Windows,OSX,Linux等多种平台上使用,也可以部署为自托管控制台或ASP.NET Web应用程序。
Redis React充分利用了基于Web的UI的导航和深层链接优势,React框架的生产力和响应能力 以及本机桌面应用程序提供的丰富的本机体验和OS集成。
-
安装Redis React图形化工具
下载连接:https://github.com/ServiceStackApps/RedisReact/raw/master/dist/RedisReact-winforms.exe,下载完直接执行即可
- 链接redis
3、模拟redis集群
在本地启动redis就可以轻松的模拟redis集群,所有其他服务器直接连接redis就可以了
本文后台使用nodeJs实现,生成session和操作redis依赖express-session
、connect-redis
、redis
三个库。
express-session
:中间件,用于在后端生成sessionID和cookie,会在request流对象中生成一个Session对象,用于操作session。connect-redis
:这是一个关于session的持久化插件, 配合express-session
使用。此模块基于redis,将session相关信息持久化(意思就是将session存入redis)。redis
:用于连接和操作redis数据库。
使用方式大致如下:
const express = require("express")
const session = require('express-session')
const RedisStrore = require('connect-redis')(session)
const redis = require('redis')
const config={
"cookie" : {
"path": "/",
"maxAge" : 1800000,
"httpOnly": true
},
"sessionStore" : {
"host": "127.0.0.1", // redis主机
"port": "6379", // redis默认端口号
"pass": "****",
"auth_pass": "****", // 设置了密码时需要该字段
}
}
const app = express()
const client = redis.createClient(6379, '127.0.0.1', config.sessionStore) // 连接redis,创建实例
app.use(session({
name : "sessionId", // sessionID的属性名
secret : 'Asecret123-', // 生成sessionID的密钥
resave : false,
rolling: false, //在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false)
saveUninitialized : false, // 强制将未初始化的 session 存储。当新建了一个 session 且未设定属性或值时,它就处于未初始化状态。在设定一个 cookie 前,这对于登陆验证,减轻服务端存储压力,权限控制是有帮助的。(默 认:true)。建议手动添加。
cookie: config.cookie,
store: new RedisStrore({ client })
}));
使用后request流对象会新增sessionID字段和session对象
3、应用平台实现
前端部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
</head>
<body>
<div id="app">home</div>
<button onclick="logout()">退出</button>
</body>
</html>
<script>
$.ajax({
url: "http://127.0.0.1:1001/getData",
method: 'GET',
dataType: 'json',
xhrFields: {
withCredentials: true //允许携带Cookie
},
success: function(res) {
console.log(res)
if (res.code === 401) {
window.location.href = "http://127.0.0.1:2000/?redirect_url=" + encodeURIComponent(window.location.href)
} else if (res.code === 200) {
$("#app").text(res.data.msg)
}
}
})
function logout() {
$.ajax({
url: "http://127.0.0.1:1001/logout",
method: 'GET',
dataType: 'json',
xhrFields: {
withCredentials: true //允许携带Cookie
},
success: function(res) {
if (res.code === 200) {
// 不能再这里操作document.cookie来删除sessionId,因为设置了httpOnly
window.location.reload()
}
}
})
}
</script>
后端部分:
// 拦截器
app.all('*', function(req, res, next) {
if (req.cookies.sessionId) {
var sessionId = req.cookies.sessionId.split('.')[0].replace("s:", "sess:")
console.log('sessionId', sessionId)
client.get(sessionId, function(err, reply) {
console.log('reply', reply)
if (reply) {
next();
}
})
} else {
console.log("=============重定向到SSO=============")
res.json({
code: 401
})
}
})
app.get('/getData', (req, res) => {
res.json({
code: 200,
data: {
msg: 'hello, welcome you!'
}
})
})
app.get('/logout', (req, res) => {
req.session.destroy(err => {
if (err) throw new ErrorEvent("注销失败")
res.clearCookie("sessionId").json({
code: 200
})
})
})
初始进入页面发送http://127.0.0.1:1001/getData请求,如果没有携带cookie,后台拦截器就会拦截,返回401,前台判断是401就重定向到SSO
4、SSO实现
前端部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>login</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
</head>
<body>
<div>
<label>账户:</label>
<input type="text" class="account" name="account" value="">
</div>
<div>
<label>密码:</label>
<input type="text" class="password" name="password" value="">
</div>
<div>
<input type="button" onclick="submit()" value="登录">
<input type="reset" value="重置">
</div>
</body>
</html>
<script>
// 获取url参数
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
function submit() {
var account = $(".account").val().trim()
var password = $(".password").val().trim()
if (!account || !password) {
alert("账户密码不能为空")
}
$.ajax({
url: 'http://127.0.0.1:2001/login',
method: 'POST',
data: { account, password },
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
xhrFields: {
withCredentials: true //允许携带Cookie
},
success: function(res) {
if (res.code === 200) {
setTimeout(() => {
window.location.href = decodeURIComponent(getQueryVariable("redirect_url"))
}, 1000);
}
}
})
}
</script>
后台部分:
app.post('/login', (req, res) => {
var account = req.body.account
var pwd = req.body.password
if (!account || !pwd) {
console.log("账户密码不能为空")
return
}
nosql.one().make(builder => {
builder.where('account', '=', account);
builder.where('password', '=', pwd);
builder.callback((err, userInfo) => {
if (userInfo) {
req.session.regenerate(err => {
if (err) {
console.log('生成sessionID失败: ' + err)
} else {
console.log(req)
req.session.userInfo = userInfo
req.session.save()
// res.cookie("sessionId", req.sessionID, req.session.cookie)
res.json({
code: 200
})
}
})
} else {
console.log("该账户不存在")
}
})
})
})
这里数据库使用的是nosql文档数据库,依赖nosql
模块。登录成功后regenerate方法会生成session和cookie,然后将session保存到redis中
PS:存在cookie中的sessionID和存在redis中的sessionID不一样,这是因为express-session底层实现的原因,有兴趣的同学可以深入一下
项目地址:https://github.com/Revelation...
5、参考
https://segmentfault.com/a/11...
https://github.com/ServiceStackApps/RedisReact#download
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。