[Node] Stateful Session Management for login, logout and signup
Stateful session management: Store session which associate with user, and store in the menory on server.
Sign Up:
app.route('/api/signup')
.post(createUser);
import {Request, Response} from 'express';
import {db} from './database';
import * as argon2 from 'argon2';
import {validatePassword} from './password-validation';
import {randomBytes} from './security.utils';
import {sessionStore} from './session-store';
export function createUser (req: Request, res: Response) {
const credentials = req.body;
const errors = validatePassword(credentials.password);
if (errors.length > 0) {
res.status(400).json({
errors
});
} else {
createUserAndSession(res, credentials);
}
}
async function createUserAndSession(res, credentials) {
// Create a password digest
const passwordDigest = await argon2.hash(credentials.password);
// Save into db
const user = db.createUser(credentials.email, passwordDigest);
// create random session id
const sessionId = await randomBytes(32).then(bytes => bytes.toString('hex'));
// link sessionId with user
sessionStore.createSession(sessionId, user);
// set sessionid into cookie
res.cookie('SESSIONID', sessionId, {
httpOnly: true, // js cannot access cookie
secure: true // enable https only
});
// send back to UI
res.status(200).json({id: user.id, email: user.email});
}
Password validation:
import * as passwordValidator from 'password-validator'; // Create a schema
const schema = new passwordValidator(); // Add properties to it
schema
.is().min(7) // Minimum length 7
.has().uppercase() // Must have uppercase letters
.has().lowercase() // Must have lowercase letters
.has().digits() // Must have digits
.has().not().spaces() // Should not have spaces
.is().not().oneOf(['Passw0rd', 'Password123']); // Blacklist these values export function validatePassword(password: string) {
return schema.validate(password, {list: true});
}
Random bytes generator:
const util = require('util');
const crypto = require('crypto');
// convert a callback based code to promise based
export const randomBytes = util.promisify(
crypto.randomBytes
);
Session storage:
import {Session} from './session';
import {User} from '../src/app/model/user';
class SessionStore {
private sessions: {[key: string]: Session} = {};
createSession(sessionId: string, user: User) {
this.sessions[sessionId] = new Session(sessionId, user);
}
findUserBySessionId(sessionId: string): User | undefined {
const session = this.sessions[sessionId];
return this.isSessionValid(sessionId) ? session.user : undefined;
}
isSessionValid(sessionId: string): boolean {
const session = this.sessions[sessionId];
return session && session.isValid();
}
destroySession(sessionId: string): void {
delete this.sessions[sessionId];
}
}
// We want only global singleton
export const sessionStore = new SessionStore();
In menory database:
import * as _ from 'lodash';
import {LESSONS, USERS} from './database-data';
import {DbUser} from './db-user'; class InMemoryDatabase { userCounter = 0; createUser(email, passwordDigest) {
const id = ++this.userCounter;
const user: DbUser = {
id,
email,
passwordDigest
}; USERS[id] = user; return user;
} findUserByEmail(email: string): DbUser {
const users = _.values(USERS);
return _.find(users, user => user.email === email);
}
} export const db = new InMemoryDatabase();
Login:
app.route('/api/login')
.post(login);
import {Request, Response} from 'express';
import {db} from './database';
import {DbUser} from './db-user';
import * as argon2 from 'argon2';
import {randomBytes} from './security.utils';
import {sessionStore} from './session-store';
export function login(req: Request, res: Response) {
const info = req.body;
const user = db.findUserByEmail(info.email);
if (!user) {
res.sendStatus(403);
} else {
loginAndBuildResponse(info, user, res);
}
}
async function loginAndBuildResponse(credentials: any, user: DbUser, res: Response) {
try {
const sessionId = await attemptLogin(credentials, user);
res.cookie('SESSIONID', sessionId, {httpOnly: true, secure: true});
res.status(200).json({id: user.id, email: user.email});
} catch (err) {
res.sendStatus(403);
}
}
async function attemptLogin(info: any, user: DbUser) {
const isPasswordValid = await argon2.verify(user.passwordDigest, info.password);
if (!isPasswordValid) {
throw new Error('Password Invalid');
}
const sessionId = await randomBytes(32).then(bytes => bytes.toString('hex'));
sessionStore.createSession(sessionId, user);
return sessionId;
}
Logout:
app.route('/api/logout')
.post(logout);
import {Response, Request} from 'express';
import {sessionStore} from './session-store';
export const logout = (req: Request, res: Response) => {
console.log(req.cookies['SESSIONID']);
const sessionId = req.cookies['SESSIONID'];
sessionStore.destroySession(sessionId);
res.clearCookie('SESSIONID');
res.sendStatus(200);
};
[Node] Stateful Session Management for login, logout and signup的更多相关文章
- Spring Security笔记:自定义Login/Logout Filter、AuthenticationProvider、AuthenticationToken
在前面的学习中,配置文件中的<http>...</http>都是采用的auto-config="true"这种自动配置模式,根据Spring Securit ...
- login/logout切换
1. 前端按钮 <img border="0" width="18" height="18" src="<%=base ...
- YII session存储 调用login方法
当要进行用户的session存储的时候,可以调用里面的login方法进行存储
- node express session
在express4.0版本以上,需要单独增加session模块:express-session:https://www.npmjs.com/package/express-session 具体做法是, ...
- node中session存储与销毁,及session的生命周期
1.首先在使用session之前需要先配置session的过期时间等,在入口文件app.js中 app.use(express.session({ cookie: { maxAge: config.g ...
- session management
The session does not created until the HttpServletRequest.getSession() method is called.
- Use Spring transaction to simplify Hibernate session management
Spring对Hibernate有很好的支持 DataSource ->SessionFactory-> HibernateTranscationManagerHibernate中通 ...
- node中session的管理
请看这个博客: http://spartan1.iteye.com/blog/1729148 我自己的理解 session俗称会话. 第一次访问服务器的时候由服务器创建,相当于一个cookie(就 ...
- Apache Shiro Session Management
https://shiro.apache.org/session-management.html#session-management https://shiro.apache.org/session ...
随机推荐
- js如何生成[n,m]的随机数(整理总结)
js如何生成[n,m]的随机数(整理总结) 一.总结 一句话总结: // max - 期望的最大值 // min - 期望的最小值 parseInt(Math.random()*(max-min+1) ...
- Ionic2集成ArcGIS JavaScript API.md
1. Ionic同原生ArcGIS JavaScript API结合 1.1. 安装esri-loader 在工程目录下命令行安装: npm install angular2-esri-loader ...
- cf 828 A. Restaurant Tables
A. Restaurant Tables time limit per test 1 second memory limit per test 256 megabytes input standard ...
- 昼猫笔记 JavaScript -- 闭包
本次主要内容是 闭包 阅读时间: 约 3分钟 记得点个赞支持支持我哦 初步了解 先看下代码,输出结果是多少? function fn1 () { var a = 2 function fn2 () ...
- Hexo 自动同步
灵感 最近认证阿里云学生用户,参与ESC服务器9.9元/月的活动,准备先搭建一个博客网站,写写自已的心得以及经验.之前也搭建过网站,最后由于个人没时间(没时间是假的,就是懒.哈哈)的原因导致最后服务器 ...
- android对话框显示异常报错:You need to use a Theme.AppCompat theme (or descendant) with this activity.
今天写android实验碰到到一个问题,在用AlertDialog.Builder类构建一个对话框之后,调用Builder.show()方法时抛出异常如下: - ::-/xyz.qlrr.sqlite ...
- linux 查看tomcat 实时日志
进入tomcat下logs文件夹下,若没有Catalina.out文件先去启动服务在回到logs文件夹输入 tail -f catalina.out ,可看到实时日志
- myeclipse中断点调试
在代码最左端,也就是行号位置处双击.会出现一个实心小圆点.即增加的断点.debug启动程序,就会运行到断点处: 按F5是进去方法里面. 按F6是一步一步走, 按F7是跳出方法里面(按F5后再按F7就跳 ...
- Reuse Is About People and Education, Not Just Architecture
 Reuse Is About People and Education, Not Just Architecture Jeremy Meyer you MigHT AdopT THE AppRoA ...
- SpringMVC-@RequestMapping的参数和用法
RequestMapping里面的注解包含的参数如图: RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上.用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径. ...