登录页面
(1) 登录页面填写登录表单
用户在前端登录页面填写用户名和密码。
登录页面使用HTML和JavaScript构建,表单包含用户名和密码输入框。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login Page</title>
</head>
<body>
<h2>Login</h2>
<form id="loginForm">
<!-- 用户名输入框 -->
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<br>
<!-- 密码输入框 -->
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<br>
<!-- 登录按钮 -->
<button type="button" onclick="login()">Login</button>
</form>
<script>
function login() {
// 从输入框中获取用户名和密码
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
// 使用fetch API向/login URL发送POST请求
fetch('/login', {
method: 'POST',
// mode: 'no-cors',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
// 将用户名和密码作为JSON数据发送
body: JSON.stringify({ username: username, password: password }),
})
.then(response => response.json())
.then(data => {
// 处理从服务器返回的JSON数据
if (data.error) {
// 如果登录失败,记录错误消息到控制台
console.error("Login failed:", data.error);
} else {
// 如果登录成功,记录成功消息和令牌到控制台
console.log("Login successful! Token:", data.token);
// 将令牌保存到localStorage
localStorage.setItem('jwtToken', data.token);
// 重定向到index.html
window.location.href = '/';
}
})
.catch((error) => {
// 捕捉发生的错误,并记录到控制台
console.error('Error:', error);
});
}
</script>
</body>
</html>
(2) 发送登录请求
用户点击登录按钮后,JavaScript发起POST请求到/login
URL。使用Fetch API发送请求,将用户名和密码以JSON格式发送给服务器。
#login.html
fetch('/login', {
method: 'POST',
// mode: 'no-cors',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
body: JSON.stringify({ username: username, password: password }),
})
(3) 后台进行用户名与密码验证
服务器后台通过Flask框架处理/login
URL的请求。
后台获取前端发送的用户名和密码,并与预先设定的用户名和密码进行比较验证。
# houtai.py
@app.route('/login', methods=['POST'])
def login():
# 获取请求中的用户名和密码
request_data = request.get_json()
username = request_data.get('username')
password = request_data.get('password')
# 固定密码,以后有需求再改
if USERNAME == username and PASSWORD == password:
user_id = 1 # 假设'user_id' == 1
# 如果用户名和密码匹配,则生成JWT令牌
token = generate_token(user_id)
# 返回生成的JWT令牌作为JSON响应
return jsonify({"token": token})
# 如果用户名和密码不匹配,则返回401状态码和错误消息
return jsonify({"error": "Invalid credentials"}), 401
(4) 验证通过后,生成token
如果用户名和密码验证成功,后台生成JWT令牌。
令牌中包含用户ID和过期时间,使用jwt.encode方法生成
# houtai.py
def generate_token(user_id):
# 设置令牌过期时间为当前时间加上24小时
expiration_time = datetime.datetime.utcnow() + datetime.timedelta(hours=24)
# 使用jwt库的encode函数生成JWT令牌
token = jwt.encode({"user_id": user_id, "exp": expiration_time}, secret_key, algorithm="HS256")
# 返回生成的令牌
return token
(5) 返回token
如果用户名和密码验证成功后台将生成的JWT令牌以JSON格式返回给前端。
如果用户名和密码验证失败,则返回错误信息。
# houtai.py
# ...
@app.route('/login', methods=['POST'])
def login():
# 从JSON数据中获取用户名和密码
request_data = request.get_json()
username = request_data.get('username')
password = request_data.get('password')
# 固定密码,以后有需求再改
if USERNAME == username and PASSWORD == password:
user_id = 1 # 假设'user_id' == 1
#通过调用generate_token函数生成JWT令牌,用于身份验证。
token = generate_token(user_id)
#返回包含JWT令牌的JSON响应,表示登录成功。
return jsonify({"token": token})
return jsonify({"error": "Invalid credentials"}), 401
(6) 前端处理登录结果
前端通过JavaScript处理后台返回的数据。
如果登录失败,记录错误消息到控制台
如果登录成功,记录成功消息和令牌到控制台,并将令牌保存到localStorage然后跳转到主页。
<!-- login.html -->
<!-- ... -->
<script>
function login() {
// 从输入框中获取用户名和密码
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
// 使用fetch API向/login端点发送POST请求
fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
body: JSON.stringify({ username: username, password: password }),
})
.then(response => response.json())
.then(data => {
// 处理从服务器返回的JSON数据
if (data.error) {
// 如果登录失败,记录错误消息到控制台
console.error("Login failed:", data.error);
} else {
// 如果登录成功,记录成功消息和令牌到控制台
console.log("Login successful! Token:", data.token);
// 将令牌保存到localStorage
localStorage.setItem('jwtToken', data.token);
// 重定向到index.html
window.location.href = '/';
}
})
.catch((error) => {
// 捕捉发生的错误,并记录到控制台
console.error('Error:', error);
});
}
</script>
<!-- ... -->
受保护的请求 (添加token验证)
(1) 获取日记列表
index.html包含了日记内容的显示区域、表单用于提交新的日记,以及获取日记列表的JavaScript代码。
#index.html
<body>
<div class="container">
<!-- 日记表单 -->
<form id="diary-form">
<!-- 日记内容输入框 -->
<textarea id="diary-content"></textarea>
<br>
<!-- 保存按钮 -->
<button type="submit">Save</button>
</form>
<!-- 用于显示日记列表的div -->
<div id="diary-list"></div>
<!-- 引入index.js脚本 -->
<script src="index.js"></script>
</div>
</body>
(2) 获取JWT令牌发送GET请求
获取存储在localStorage中的JWT令牌,将JWT令牌包含在请求头中,向服务器的/get_diaries发送GET请求。
#index.js
// 获取存储在localStorage中的JWT令牌
token = localStorage.getItem('jwtToken');
// 向服务器发起GET请求获取日记列表
fetch('/get_diaries', {
headers: {
'Authorization': token,
}
})
// 处理响应
.then(response => {
if (response.status === 401) {
// 如果返回状态码为401,表示未认证,将页面重定向到登录页面
window.location.href = '/login.html';
}
return response.json();
})
.then(diaries => {
// 在页面上显示日记列表
const diaryList = document.getElementById('diary-list');
diaries.forEach((diary, index) => {
// 创建日记内容显示区域
const pre = document.createElement('pre');
pre.textContent = diary.content;
// 创建删除按钮
const delButton = document.createElement('button');
delButton.textContent = '删除';
delButton.onclick = () => {
// 发送删除请求到后台
fetch(`/delete_diary/${index}`, { method: 'DELETE' })
.then(() => {
// 从页面上删除该日记
diaryList.removeChild(pre);
alert('删除成功!');
location.reload();
})
.catch(error => console.error('删除失败:', error));
};
pre.appendChild(delButton);
// 创建行号
const lineNumber = document.createElement('span');
lineNumber.textContent = `(${index + 1}) `;
lineNumber.style.color = '#999';
lineNumber.style.fontSize = '0.8em';
pre.insertBefore(lineNumber, pre.firstChild);
diaryList.appendChild(pre);
});
})
.catch(error => console.error('获取日记列表失败:', error));
(3) 认证处理
在后台,/get_diaries通过验证请求头中的token实现认证。如果认证失败,返回401状态码。
# houtai.py
# ...
@app.route('/get_diaries', methods=['GET'])
def get_diaries():
token = request.headers.get('Authorization')
if not token:
return jsonify({"error": "Token is missing"}), 401
decoded_data = verify_token(token)
if not decoded_data:
return jsonify({"error": "Token is invalid or expired"}), 401
# 从文件中读取所有日记
with open(CONF.DIARY_CSV_DIR, 'r+', newline='',encoding='utf-8') as file:
reader = csv.reader(file, delimiter=',')
diaries = list(reader)
diaries = [{'content': line[0], 'lineNumber': index+1}
for index, line in enumerate(diaries[1:])]
# 返回日记列表
return jsonify(diaries)
(4) 处理401响应
在前端,当收到401状态码时,跳转到登录页面。
.then(response => {
if (response.status === 401) {
console.error(response)
// Redirect to the login page or handle unauthorized access as needed
window.location.href = '/login.html';
}
return response.json();
})
(5) github地址
https://github.com/99fukong/web/commit/2ba7ce5fc97cd6b30bea4bceec7d785231337cf4