当前位置:网站首页>03. Login of development blog project
03. Login of development blog project
2022-07-06 05:22:00 【John is orange】
Login of developing blog project
1. Cookie
1.1 cookie Introduce
What is? cookie
- A string stored in the browser ( Maximum 5 kb)
- Cross domain not shared
- The format is as follows k1=v1; k2=v2; k3=v3; So you can store structured data
- Send each time http request , The cookie Send to server
- server You can modify cookie And return to browser
- You can also use the JavaScript modify cookie( Limited )
client JavaScript operation cookie
Client view cookie, Three ways
Browser developer tools Network You can see in the request header Cookie, And respond to the Set-Cookie
Developer tools Application You can see the stored in the browser Cookie
Enter in the console
document.cookie
see . Support cookie Accumulation ,server The end can be set httpOnly To make cookie Cannot be modified .// js Additional cookie document.cookie = "k1=100;"
1.2 server End nodejs operation cookie
- see cookie
- modify cookie
- Implement login authentication
The basic process is , After entering the account and password successfully , The back-end set cookie To mark that you have logged in , At the same time, set an expiration time . If you need to do something later , We need to see whether it exists cookie, without cookie Description has not been logged in , You can't do related operations .
// router/user.js
const {
login } = require("../controller/user");
const {
SuccessModel, ErrorModel } = require("../model/resModel");
const getCookieExpires = () => {
const d = new Date();
// Set one day later cookie Be overdue
d.setTime(d.getTime() + 24 * 60 * 60 * 1000);
return d.toUTCString();
};
const handleUserRouter = (req, res) => {
const method = req.method;
// Sign in
if (method === "POST" && req.path === "/api/user/login") {
const {
username, password } = req.body;
const result = login(username, password);
return result.then((data) => {
if (data.username) {
// operation cookie
res.setHeader(
"Set-Cookie",
`username=${
data.username }; path=/; httpOnly; expires=${
getCookieExpires()}`
);
return new SuccessModel();
}
return new ErrorModel(" Login failed ");
});
}
// Test of login verification
if (method === "GET" && req.path === `/api/user/login-test`) {
if (req.cookie.username) {
return Promise.resolve(new SuccessModel());
}
return Promise.resolve(new ErrorModel(" Not signed in yet "));
}
};
module.exports = {
handleUserRouter,
};
Because it involves cookie Acquisition , therefore App.js Need to implement parsing cookie Functionality of content .
const qs = require("qs");
const {
handleBlogRouter } = require("./src/router/blog");
const {
handleUserRouter } = require("./src/router/user");
const getPostData = (req) => {
return new Promise((resolve, reject) => {
// Not POST The request does not exist POST data The problem of
if (req.method !== "POST") {
resolve({
});
return;
}
// If POST data No JSON Formatted data , Direct to ignore ( This project POST data All are JSON Format )
if (req.headers["content-type"] !== "application/json") {
resolve({
});
return;
}
let postData = "";
req.on("data", (chunk) => {
postData += chunk.toString();
});
req.on("end", () => {
if (!postData) {
resolve({
});
return;
}
resolve(JSON.parse(postData));
});
});
};
const serverHandle = async (req, res) => {
// Set the return format JSON
res.setHeader("Content-type", "application/json");
// obtain path
const url = req.url;
req.path = url.split("?")[0];
// analysis query
req.query = qs.parse(url.split("?")[1]);
// analysis cookie
req.cookie = {
};
const cookieStr = req.headers.cookie || "";
cookieStr.split(";").forEach((element) => {
if (!element) {
return;
}
const [key, val] = element.split("=");
req.cookie[key] = val;
});
// analysis POST data Put it after req.body Inside
const postData = await getPostData(req);
req.body = postData;
// Handle blog route
const blogData = await handleBlogRouter(req, res);
if (blogData) {
res.end(JSON.stringify(blogData));
// Need to be return, Otherwise, it will continue to execute
return;
}
// Handle user route
const userData = await handleUserRouter(req, res);
if (userData) {
res.end(JSON.stringify(userData));
return;
}
// Missed route : Plain text returns 404 Information
res.writeHead(404, {
"content-type": "text/plain" });
res.write("404 not found");
res.end();
};
module.exports = {
serverHandle,
};
2. session
- cookie The problem of : Will expose user information , It's dangerous
- How to solve :cookie Storage in userid,server End correspondence username
- Solution :session, namely server The client stores user information
session Usage flow
Add storage in the global session Variables of data SESSION_DATA:
const SESSION_DATA = {
};
Resolve each request session. Of this project user Of session by userid. without userid, Then generate a userid , And in SESSION_DATA Create a storage in userid The address of , And assign the address to req.session, Convenient for responding to requests SESSION_DATA modify .
// analysis session
let needSetCookie = false;
let userId = req.cookie.userid;
if (userId) {
if (!SESSION_DATA[userId]) {
SESSION_DATA[userId] = {
};
}
} else {
needSetCookie = true;
// First, replace with a pseudo-random number
userId = `${
Date.now()}_${
Math.random()}`;
SESSION_DATA[userId] = {
};
}
req.session = SESSION_DATA[userId];
Determine whether there is... Before receiving the request and returning the result every time in the route userid, Add if not key by userid Of cookie.
// Handle blog route
const blogData = await handleBlogRouter(req, res);
if (blogData) {
if (needSetCookie) {
res.setHeader(
"Set-Cookie",
`userid=${
userId}; path=/; httpOnly; expires=${
getCookieExpires()}`
);
}
res.end(JSON.stringify(blogData));
// Need to be return, Otherwise, it will continue to execute
return;
}
// Handle user route
const userData = await handleUserRouter(req, res);
if (userData) {
if (needSetCookie) {
res.setHeader(
"Set-Cookie",
`userid=${
userId}; path=/; httpOnly; expires=${
getCookieExpires()}`
);
}
res.end(JSON.stringify(userData));
return;
}
The above code is app.js Finish in . The code is as follows :
const qs = require("qs");
const {
handleBlogRouter } = require("./src/router/blog");
const {
handleUserRouter, getCookieExpires } = require("./src/router/user");
// session data
const SESSION_DATA = {
};
// obtain post data
const getPostData = (req) => {
return new Promise((resolve, reject) => {
// Not POST The request does not exist POST data The problem of
if (req.method !== "POST") {
resolve({
});
return;
}
// If POST data No JSON Formatted data , Direct to ignore ( This project POST data All are JSON Format )
if (req.headers["content-type"] !== "application/json") {
resolve({
});
return;
}
let postData = "";
req.on("data", (chunk) => {
postData += chunk.toString();
});
req.on("end", () => {
if (!postData) {
resolve({
});
return;
}
resolve(JSON.parse(postData));
});
});
};
const serverHandle = async (req, res) => {
// Set the return format JSON
res.setHeader("Content-type", "application/json");
// obtain path
const url = req.url;
req.path = url.split("?")[0];
// analysis query
req.query = qs.parse(url.split("?")[1]);
// analysis cookie
req.cookie = {
};
const cookieStr = req.headers.cookie || "";
cookieStr.split(";").forEach((element) => {
if (!element) {
return;
}
const [key, val] = element.split("=");
req.cookie[key] = val;
});
// analysis session
let needSetCookie = false;
let userId = req.cookie.userid;
if (userId) {
if (!SESSION_DATA[userId]) {
SESSION_DATA[userId] = {
};
}
} else {
needSetCookie = true;
// First, replace with a pseudo-random number
userId = `${
Date.now()}_${
Math.random()}`;
SESSION_DATA[userId] = {
};
}
req.session = SESSION_DATA[userId];
// analysis POST data Put it after req.body Inside
const postData = await getPostData(req);
req.body = postData;
// Handle blog route
const blogData = await handleBlogRouter(req, res);
if (blogData) {
if (needSetCookie) {
res.setHeader(
"Set-Cookie",
`userid=${
userId}; path=/; httpOnly; expires=${
getCookieExpires()}`
);
}
res.end(JSON.stringify(blogData));
// Need to be return, Otherwise, it will continue to execute
return;
}
// Handle user route
const userData = await handleUserRouter(req, res);
if (userData) {
if (needSetCookie) {
res.setHeader(
"Set-Cookie",
`userid=${
userId}; path=/; httpOnly; expires=${
getCookieExpires()}`
);
}
res.end(JSON.stringify(userData));
return;
}
// Missed route : Plain text returns 404 Information
res.writeHead(404, {
"content-type": "text/plain" });
res.write("404 not found");
res.end();
};
module.exports = {
serverHandle,
};
Next , Login and login verification tests are required . If the login is successful , As to the SESSION_DATA[userId]
Placed in username and realName.
// router/user.js
// Sign in
if (method === "POST" && req.path === "/api/user/login") {
const {
username, password } = req.body;
const result = login(username, password);
return result.then((data) => {
if (data.username) {
// Set up session
req.session.username = data.username;
req.session.realName = data.realname;
return new SuccessModel();
}
return new ErrorModel(" Login failed ");
});
}
session Put username after , Later, if you need to judge whether the user logs in , Just get session See if there is username.
// router/user.js
// Test of login verification
if (method === "GET" && req.path === `/api/user/login-test`) {
if (req.session.username) {
return Promise.resolve(
new SuccessModel({
username: req.session.realName })
);
}
return Promise.resolve(new ErrorModel(" Not signed in yet "));
}
Current limitations
- at present session Is directly js Variable , Put it in nodejs In process memory
- First of all , Process memory is limited , Too many visits , What about memory explosion ?
- second , The official online operation is multi process , Memory cannot be shared between processes .
3. redis
3.1 from session To redis
Based on the upper handle session Put it in nodejs Disadvantages in process memory ,redis It's a good solution .
redis characteristic
- web server The most commonly used cache database , Data is stored in memory
- Compared with MySQL, Fast access ( Memory and hard disk are not of the same order of magnitude )
- But the cost is higher , The amount of data that can be stored is smaller
- take web server and redis Split into two separate services
- Both sides are independent , It's all scalable ( For example, they are all expanded into clusters )
- Be similar to MySQL, It is also a separate service , It can also be extended
why session Suitable for use redis
- session Frequent visits , High performance requirements
- session The problem of data loss due to power failure can not be considered ( Hard injury of memory )
- session The amount of data will not be too large ( Compared with MySQL Data stored in )
Why website data is not suitable redis
- The operation frequency is not too high ( Compared with session operation )
- Power failure cannot be lost , Must keep
- Too much data , Memory costs are too high
3.2 redis Basic use
After reading the online installation tutorial , Input redis-server
and redis-cli
. One is to start redis service ( The server must be turned on before it can be used !), The other is to open the client , Used for operation redis.
redis The format of storage is key value pair , It can be imagined as a big map.
Place key value pairs and get key value pairs :
set myname sjh
get myname # return "sjh"
Get all keys :
keys *
Delete a key value pair
del myname
3.3 nodejs Connect redis - Encapsulating utility functions
After understanding the relevant use , Write a tool , Convenient in node Inside operation redis.
Be careful ,nodejs in redis All operations are asynchronous , Therefore, it is necessary to use await To receive .
const redis = require("redis");
const {
REDIS_CONF } = require("../conf/db");
// establish redis client
const redisClient = redis.createClient(REDIS_CONF);
redisClient.connect();
const set = async (key, val) => {
// Key value pairs must be in string form
if (typeof val === "object") {
val = JSON.stringify(val);
}
await redisClient.set(key, val);
return true;
};
const get = async (key) => {
const value = await redisClient.get(key);
if (!value) {
return null;
}
// Try to parse redis The string becomes an object
try {
return JSON.parse(value);
} catch (error) {
return value;
}
};
module.exports = {
set,
get,
};
among set Method is used to set key value pairs ,get Used to get key Corresponding value .
3.4 session Deposit in redis
stay app.js Inside for analysis session.
First get cookie Inside userid, If it doesn't exist , Then mark it as required cookie, And initialize redis Medium session value .session Of key Is uniquely identified userId.
// analysis session( Use redis)
let needSetCookie = false;
let userId = req.cookie.userid;
if (!userId) {
needSetCookie = true;
userId = `${
Date.now()}_${
Math.random()}`;
// initialization redis Medium session value
set(userId, {
});
}
And then userId Put it in req in , Convenient route acquisition sessionId. At the same time through sessionId , from redis get sessionId Corresponding sessionData. If sessionData non-existent , It also needs to be initialized sessionData and redis Medium session value . Last , take sessionData The address is in req in , It is convenient to modify and obtain routes sessionData.
// obtain session
req.sessionId = userId
const sessionData = await get(req.sessionId);
if (sessionData == null) {
// initialization redis Medium session value
set(req.sessionId, {
})
// Set up session
req.session = {
}
} else {
// Set up session
req.session = sessionData
}
app.js All of the code :
// app.js
const qs = require("qs");
const {
handleBlogRouter } = require("./src/router/blog");
const {
handleUserRouter } = require("./src/router/user");
const {
get, set } = require("./src/db/redis");
const getCookieExpires = () => {
const d = new Date();
// Set one day later cookie Be overdue
d.setTime(d.getTime() + 24 * 60 * 60 * 1000);
return d.toUTCString();
};
// obtain post data
const getPostData = (req) => {
return new Promise((resolve, reject) => {
// Not POST The request does not exist POST data The problem of
if (req.method !== "POST") {
resolve({
});
return;
}
// If POST data No JSON Formatted data , Direct to ignore ( This project POST data All are JSON Format )
if (req.headers["content-type"] !== "application/json") {
resolve({
});
return;
}
let postData = "";
req.on("data", (chunk) => {
postData += chunk.toString();
});
req.on("end", () => {
if (!postData) {
resolve({
});
return;
}
resolve(JSON.parse(postData));
});
});
};
const serverHandle = async (req, res) => {
// Set the return format JSON
res.setHeader("Content-type", "application/json");
// obtain path
const url = req.url;
req.path = url.split("?")[0];
// analysis query
req.query = qs.parse(url.split("?")[1]);
// analysis cookie
req.cookie = {
};
const cookieStr = req.headers.cookie || "";
cookieStr.split(";").forEach((element) => {
if (!element) {
return;
}
const [key, val] = element.split("=");
req.cookie[key] = val;
});
// analysis session( Use redis)
let needSetCookie = false;
let userId = req.cookie.userid;
if (!userId) {
needSetCookie = true;
userId = `${
Date.now()}_${
Math.random()}`;
// initialization redis Medium session value
set(userId, {
});
}
// obtain session
req.sessionId = userId
const sessionData = await get(req.sessionId);
if (sessionData == null) {
// initialization redis Medium session value
set(req.sessionId, {
})
// Set up session
req.session = {
}
} else {
// Set up session
req.session = sessionData
}
// analysis POST data Put it after req.body Inside
const postData = await getPostData(req);
req.body = postData;
// Handle blog route
const blogData = await handleBlogRouter(req, res);
if (blogData) {
if (needSetCookie) {
res.setHeader(
"Set-Cookie",
`userid=${
userId}; path=/; httpOnly; expires=${
getCookieExpires()}`
);
}
res.end(JSON.stringify(blogData));
// Need to be return, Otherwise, it will continue to execute
return;
}
// Handle user route
const userData = await handleUserRouter(req, res);
if (userData) {
if (needSetCookie) {
res.setHeader(
"Set-Cookie",
`userid=${
userId}; path=/; httpOnly; expires=${
getCookieExpires()}`
);
}
res.end(JSON.stringify(userData));
return;
}
// Missed route : Plain text returns 404 Information
res.writeHead(404, {
"content-type": "text/plain" });
res.write("404 not found");
res.end();
};
module.exports = {
serverHandle,
};
Now let's look at login and login verification functions . If the login is successful , Will username and realname Set to req.session in , Synchronize to redis in . In login verification , adopt req.session get username, If username There is , Indicates that the user has logged in .
// router/user.js
const {
login } = require("../controller/user");
const {
set } = require("../db/redis");
const {
SuccessModel, ErrorModel } = require("../model/resModel");
const handleUserRouter = (req, res) => {
const method = req.method;
// Sign in
if (method === "POST" && req.path === "/api/user/login") {
const {
username, password } = req.body;
const result = login(username, password);
return result.then((data) => {
if (data.username) {
// Set up session
req.session.username = data.username;
req.session.realName = data.realname;
// Synchronize to redis in
set(req.sessionId, req.session);
return new SuccessModel();
}
return new ErrorModel(" Login failed ");
});
}
// Test of login verification
if (method === "GET" && req.path === `/api/user/login-test`) {
if (req.session.username) {
return Promise.resolve(
new SuccessModel({
username: req.session })
);
}
return Promise.resolve(new ErrorModel(" Not signed in yet "));
}
};
module.exports = {
handleUserRouter,
};
3.5 Login authentication middleware
In the new blog , Update blog , When deleting a blog , Login verification is required , Therefore, it is reasonable to write login verification as middleware .
// router/blog.js
// Unified login verification function
const loginCheck = (req) => {
if (!req.session.username) {
return Promise.resolve(new ErrorModel(" Not signed in yet "));
}
};
The above function is the middleware for login verification , If session Not in it username, Return relevant information that has not been logged in .
Adding blog 、 modify 、 Before deleting logic , Take a look at the middleware logic first , If the middleware returns data , Description: not logged in , directly return Interrupt subsequent operations .
blog All relevant codes :
// router/blog.js
const {
getList,
getDetail,
newBlog,
updateBlog,
delBlog,
} = require("../controller/blog");
const {
SuccessModel, ErrorModel } = require("./../model/resModel");
// Unified login verification function
const loginCheck = (req) => {
if (!req.session.username) {
return Promise.resolve(new ErrorModel(" Not signed in yet "));
}
};
const handleBlogRouter = (req, res) => {
const method = req.method;
const id = req.query.id;
// Get a list of blogs
if (method === "GET" && req.path === "/api/blog/list") {
const author = req.query.author || "";
const keyword = req.query.keyword || "";
const result = getList(author, keyword);
// return promise
return result.then((listData) => {
return new SuccessModel(listData);
});
}
// Get blog details
if (method === "GET" && req.path === "/api/blog/detail") {
const result = getDetail(id);
return result.then((data) => {
return new SuccessModel(data);
});
}
// Create a new blog
if (method === "POST" && req.path === "/api/blog/new") {
const loginCheckResult = loginCheck(req);
// If there is a return value , Description: not logged in
if (loginCheckResult) {
return loginCheckResult;
}
// Get user information
req.body.author = req.session.username;
const result = newBlog(req.body);
return result.then((data) => {
return new SuccessModel(data);
});
}
// Update a blog
if (method === "POST" && req.path === "/api/blog/update") {
const loginCheckResult = loginCheck(req);
// If there is a return value , Description: not logged in , Direct return information
if (loginCheckResult) {
return loginCheckResult;
}
req.body.author = req.session.username;
const result = updateBlog(id, req.body);
return result.then((val) => {
if (val) {
return new SuccessModel();
} else {
return new ErrorModel(" Failed to update blog ");
}
});
}
// Delete a blog
if (method === "POST" && req.path === "/api/blog/del") {
const loginCheckResult = loginCheck(req);
// If there is a return value , Description: not logged in
if (loginCheckResult) {
return loginCheckResult;
}
const author = req.session.username;
const result = delBlog(id, author);
return result.then((val) => {
if (val) {
return new SuccessModel();
} else {
return new ErrorModel(" Failed to delete blog ");
}
});
}
};
module.exports = {
handleBlogRouter,
};
4. Joint commissioning with the front end
4.1 Cross-domain problem
- Login function depends on cookie, You need to use a browser to debug
- cookie Cross domain not shared , Front end and server The end must be in the same domain
- Need to use nginx Do the agent , Let the front and back ends be in the same domain
The back-end and front-end start the project at the same time , Because of the different ports , There will be cross domain problems .
Front end startup service :
Install first http-server Used to start the server
yarn global add http-server
Then set a port in the folder where the front end is located to start :
http-server -p 8001
The effect is as follows :
But you can't request the blog list , Because the ports are different ( My backend port is 8000), therefore cookie You can't take it with you .
4.2 NGINX To solve the cross domain
High performance web The server , Free open source
Generally used for static services 、 Load balancing
Reverse proxy
A reverse proxy is a proxy that is invisible to the client . The opposite of reverse proxy is forward proxy , That is, the agent that the client can control , for example , The company's intranet needs to download relevant agents , Browser to access .
nginx Depending on the request , Agent to different places .
nginx install
- windows Users install on the official website
- mac The user to use homebrew:
brew install nginx
Details after installation :
Docroot is: /usr/local/var/www
The default port has been set in /usr/local/etc/nginx/nginx.conf to 8080 so that
nginx can run without sudo.
nginx will load all files in /usr/local/etc/nginx/servers/.
To restart nginx after an upgrade:
brew services restart nginx
Or, if you don't want/need a background service you can just run:
/usr/local/opt/nginx/bin/nginx -g daemon off;
nginx Default profile
- Windows:
C:\nginx\conf\nginx.conf
- Mac:
/usr/local/etc/nginx/nginx.conf
nginx command
- Test if the configuration file format is correct :
nginx -t
- start-up nginx:
nginx
- restart nginx:
nginx -s reload
- stop it nginx:
nginx -s stop
Agent configuration
In profile nginx.conf
Make the following changes in :
server {
listen 8080;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
# Static file port
location / {
proxy_pass http://localhost:8001;
}
# Server port
location /api/ {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
}
It's easy to understand the content directly .nginx The port accessed by the server is 8080. When receiving the root directory , Agent to 8001 Under port , Receive /api/ route , Then agent to 8000 port .
After the agent is configured, you can see the effect , Cross domain successfully resolved :
边栏推荐
- The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
- Using stopwatch to count code time
- The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
- Drive development - the first helloddk
- 關於Unity Inspector上的一些常用技巧,一般用於編輯器擴展或者其他
- Nacos TC setup of highly available Seata (02)
- EditorUtility. The role and application of setdirty in untiy
- HAC集群修改管理员用户密码
- 【torch】|torch. nn. utils. clip_ grad_ norm_
- Steady, 35K, byte business data analysis post
猜你喜欢
Using stopwatch to count code time
Postman Association
Can the feelings of Xi'an version of "Coca Cola" and Bingfeng beverage rush for IPO continue?
Figure database ongdb release v-1.0.3
Compilation et connexion de shader dans games202 - webgl (comprendre la direction)
Golang -- TCP implements concurrency (server and client)
无代码六月大事件|2022无代码探索者大会即将召开;AI增强型无代码工具推出...
The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
RT thread analysis - object container implementation and function
The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
随机推荐
Drive development - the first helloddk
Talking about the type and function of lens filter
【华为机试真题详解】检查是否存在满足条件的数字组合
Knowledge points of circular structure
Steady, 35K, byte business data analysis post
Vulhub vulnerability recurrence 68_ ThinkPHP
改善Jpopup以实现动态控制disable
SQLite queries the maximum value and returns the whole row of data
Nacos TC setup of highly available Seata (02)
Ora-01779: the column corresponding to the non key value saving table cannot be modified
Compilation et connexion de shader dans games202 - webgl (comprendre la direction)
Mongodb basic knowledge summary
Cuda11.1 online installation
[leetcode] 18. Sum of four numbers
SQLite add index
Collection + interview questions
UCF(暑期团队赛二)
The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
02. 开发博客项目之数据存储
Solution of QT TCP packet sticking