基于Flask的通用登录注册模块,并代理跳转到目标网址
实现了用户密码的加密,代理跳转到目标网址,不会暴露目标路径,未登录的情况下访问proxy则自动跳转到登录页,使用时需要修改配置项config,登录注册页面背景快速修改,可以实现登录注册模块的快速复用。
1.app.py
from flask import Flask, render_template, request, redirect, url_for, session, Response
import sqlite3
import os
import bcrypt
import requests
app = Flask(__name__)
app.secret_key = os.urandom(24)
# 数据库连接
def get_db():
conn = sqlite3.connect('users.db')
return conn
# 创建用户表
def create_user_table():
conn = get_db()
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS users
(id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL)''')
conn.commit()
conn.close()
create_user_table()
# 配置项
config = {
"after_login_url": "http://xxxx", # 修改为目标网址
"after_register_url": "/login",
"login_background_image": "static/login_background.jpg",
"register_background_image": "static/register_background.jpg"
}
# 检查用户是否登录的装饰器
def login_required(func):
def wrapper(*args, **kwargs):
if 'username' not in session:
return redirect(url_for('login'))
return func(*args, **kwargs)
wrapper.__name__ = func.__name__
return wrapper
# 注册页面
@app.route('/register', methods=['GET', 'POST'])
def register():
error = None
success = None
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password').encode('utf-8')
hashed = bcrypt.hashpw(password, bcrypt.gensalt())
conn = get_db()
c = conn.cursor()
try:
c.execute("INSERT INTO users (username, password) VALUES (?,?)", (username, hashed))
conn.commit()
success = "注册成功!请登录。"
except sqlite3.IntegrityError as e:
print(f"IntegrityError: {e}")
error = "用户名已存在,请选择其他用户名。"
finally:
conn.close()
return render_template('register.html', error=error, success=success,
background_image=config["register_background_image"])
# 登录页面
@app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password').encode('utf-8')
remember_me = request.form.get('remember_me')
conn = get_db()
c = conn.cursor()
c.execute("SELECT password FROM users WHERE username =?", (username,))
user = c.fetchone()
conn.close()
if user:
stored_password = user[0]
if bcrypt.checkpw(password, stored_password):
session['username'] = username
if remember_me:
session.permanent = True
return redirect(url_for('proxy'))
error = "用户名或密码错误。"
return render_template('login.html', error=error, background_image=config["login_background_image"])
# 注销
@app.route('/logout')
def logout():
session.pop('username', None)
return redirect(url_for('login'))
# 代理请求
@app.route('/proxy')
@login_required
def proxy():
target_url = config["after_login_url"]
try:
resp = requests.get(target_url, headers=dict(request.headers))
excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
headers = [(name, value) for (name, value) in resp.raw.headers.items()
if name.lower() not in excluded_headers]
return Response(resp.content, resp.status_code, headers)
except requests.RequestException as e:
return f"请求出错: {str(e)}", 500
# 将根目录设置为跳转到代理路由
@app.route('/')
@login_required
def index():
return redirect(url_for('proxy'))
if __name__ == '__main__':
app.run(debug=True)
2.login.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
<!-- 使用 url_for 生成 CSS 文件的绝对路径 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
background-image: url('{{ background_image }}');
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.card {
border: none;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
background-color: rgba(255, 255, 255, 0.9);
}
.card:hover {
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}
.card-header {
background-color: #0d6efd;
color: white;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.alert {
margin-top: 15px;
}
</style>
</head>
<body>
<div class="container d-flex justify-content-center align-items-center vh-100">
<div class="card w-50">
<div class="card-header text-center">
<h2>登录</h2>
</div>
<div class="card-body">
{% if error %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endif %}
<form method="post">
<div class="mb-3">
<label for="username" class="form-label">用户名:</label>
<input type="text" id="username" name="username" class="form-control" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">密码:</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" value="1" id="remember_me" name="remember_me">
<label class="form-check-label" for="remember_me">
记住密码
</label>
</div>
<button type="submit" class="btn btn-primary w-100">登录</button>
</form>
<p class="mt-3 text-center">还没有账号?<a href="{{ url_for('register') }}">注册</a></p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
3.register.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册</title>
<!-- 使用 url_for 生成 CSS 文件的绝对路径 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
background-image: url('{{ background_image }}');
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.card {
border: none;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
background-color: rgba(255, 255, 255, 0.9);
}
.card:hover {
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}
.card-header {
background-color: #0d6efd;
color: white;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.alert {
margin-top: 15px;
}
</style>
</head>
<body>
<div class="container d-flex justify-content-center align-items-center vh-100">
<div class="card w-50">
<div class="card-header text-center">
<h2>注册</h2>
</div>
<div class="card-body">
{% if error %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endif %}
{% if success %}
<div class="alert alert-success" role="alert">
{{ success }}
</div>
{% endif %}
<form method="post">
<div class="mb-3">
<label for="username" class="form-label">用户名:</label>
<input type="text" id="username" name="username" class="form-control" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">密码:</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary w-100">注册</button>
</form>
<p class="mt-3 text-center">已有账号?<a href="{{ url_for('login') }}">登录</a></p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>