Flask博客实战 - 实现登录注册功能

注意:免费节点订阅链接已更新至 2026-02-08点击查看详情

上一节我们已经创建了一个用户应用,并创建了用户模型,那么我们这节就开始实现一个简单的用户登录注册功能!

登录注册功能Flask有一个非常优秀的扩展Flask-login,我们可以选择使用这个扩展来实现,但为了学习我们暂时不使用这个第三方扩展,而是选择使用session来实现!

实现用户的登录功能

首先,我们需要完善登录的html页面, 路径为:app/auth/templates/login.html

{% extends 'base.html' %}  {% block title %} 登录页 {% endblock title %}  {% block hero %}{% endblock hero %}  {% block main %} <div class="box is-radiusless is-marginless" style="height: 80%;">     <div class="columns is-centered">         <div class="column is-5-fullhd">             {% block auth_form %}             <form action="" method="post" style="margin-top: 40%;" class="box">                 <div class=" has-text-centered mb-3">                     <p class=" subtitle">登录</p>                     <h1 class="title">FlaskBlog</h1>                 </div>                  <!-- 消息闪现 -->                 {% with messages = get_flashed_messages() %}                 <b-message type="is-danger">                   {% if messages %}                     <ul class=flashes>                     {% for message in messages %}                         <li>{{ message }}</li>                     {% endfor %}                     </ul>                 {% endif %}                 </b-message>                 {% endwith %}                  <div class="field">                     <p class="control has-icons-left has-icons-right">                       <input class="input" type="text" name="username" id="id_username" maxlength="128" placeholder="Username">                       <span class="icon is-small is-left">                         <i class="fas fa-envelope"></i>                       </span>                       <span class="icon is-small is-right">                         <i class="fas fa-check"></i>                       </span>                     </p>                   </div>                   <div class="field">                     <p class="control has-icons-left">                       <input class="input" type="password" name="password" id="id_password" maxlength="320" minlength="6" placeholder="Password">                       <span class="icon is-small is-left">                         <i class="fas fa-lock"></i>                       </span>                     </p>                   </div>                   <div class="field">                     <p class="control">                       <input class="button is-success is-fullwidth" type="submit" value="Login">                     </p>                 </div>             </form>             {% endblock auth_form %}         </div>      </div>  </div> {% endblock main %}

代码详解:

这个登陆模板继承了base.html的样式,这个base.html中的模块及代码其实就是我们之前实现的首页,只是我们把他作为一个模板基类来继承他!

这段代码中其实就是写了一个输入账号密码的表单,其他多余的代码都是为了实现表单的样式而存在的!

<input class="input" type="text" name="username" id="id_username" maxlength="128" placeholder="Username"> <input class="input" type="password" name="password" id="id_password" maxlength="320" minlength="6" placeholder="Password"> 

这里要特别说明的是这个input表单必须设置name属性,因为后端要根据此name属性来获取用户输入的值!其他属性则需要大家自行去了解学习!

登录功能的后端逻辑视图, 路径为:app/auth/views/auth.py

@bp.route('/login', methods=['GET', 'POST']) def login():     # 登录视图     if request.method == 'POST':         username = request.form['username']         password = request.form['password']          error = None         user = auth.User.query.filter_by(username=username).first()         if user is None:             error = '该用户不存在!'         elif not check_password_hash(user.password, password):             error = '密码不正确.'          if error is None:             session.clear()             session['user_id'] = user.id             return redirect(url_for('index'))         flash(error)     return render_template('login.html')

代码详解: - request.method == 'POST'判断当前请求是否为post请求方式 - error = None 来初始化一个错误变量,如果未通过登录验证,把错误信息通过消息传送到页面提示用户

user = auth.User.query.filter_by(username=username).first() if user is None:     error = '该用户不存在!' elif not check_password_hash(user.password, password):     error = '密码不正确.'

这段代码首先在数据库通过用户提交的用户名去查询该用户,用户不存在就会返回None返回错误提示,用户存在则判断密码是否正确,这里用到了一个check_password_hash()的方法,这是用来将密文密码解密后与用户输入密码比对方法,与之对应的有一个generate_password_hash()的方法用来加密明文密码保存到数据库!

if error is None:     session.clear()     session['user_id'] = user.id     return redirect(url_for('index')) flash(error)

这段代码则是如果没有返回任何错误提示,说明该提交的表单符合我们的要求,并且数据库也存在该用户信息,那么我们只需要清空session,重新将session中的user_id设置为当前登录的id即可!

因此在实现登录注册逻辑之前就必须引入这两个方法:

from werkzeug.security import check_password_hash, generate_password_hash

登录功能虽然实现了,但我们数据库目前还没有任何一个用户,所以此时就应该要去实现用户的注册功能,向数据库新增用户,大概的逻辑是,用户输入用户名及两次密码,先判断该用户是否已经存在,存在则提示更换用户名,不存在则向数据库创建该用户信息,并清空session,重新设置user_id的值为注册用户的id,以达到注册成功后自动登录的目的!

实现用户的注册功能

首先,我们需要完善注册的html页面, 路径为:app/auth/templates/register.html

{% extends 'login.html' %}  {% block title %}注册{% endblock title %}  {% block auth_form %} <form action="" method="post" style="margin-top: 40%;" class="box">     <div class=" has-text-centered mb-3">         <p class=" subtitle">注册</p>         <h1 class="title">FlaskBlog</h1>     </div>      <!-- 消息闪现 -->      {% with messages = get_flashed_messages() %}      <b-message type="is-danger">        {% if messages %}          <ul class=flashes>          {% for message in messages %}              <li>{{ message }}</li>          {% endfor %}          </ul>      {% endif %}      </b-message>      {% endwith %}      <div class="field">         <p class="control has-icons-left has-icons-right">             <input class="input" type="text" name="username" id="id_username" maxlength="128" placeholder="Username">             <span class="icon is-small is-left">                 <i class="fas fa-envelope"></i>             </span>             <span class="icon is-small is-right">                 <i class="fas fa-check"></i>             </span>         </p>     </div>     <div class="field">         <p class="control has-icons-left">             <input class="input" type="password" name="password" id="id_password" maxlength="320" minlength="6" placeholder="Password">             <span class="icon is-small is-left">                 <i class="fas fa-lock"></i>             </span>         </p>     </div>     <div class="field">         <p class="control has-icons-left">             <input class="input" type="password" name="password1" id="id_password1" maxlength="320" minlength="6" placeholder="Password1">             <span class="icon is-small is-left">                 <i class="fas fa-lock"></i>             </span>         </p>     </div>     <div class="field">         <p class="control">             <input class="button is-success is-fullwidth" type="submit" value="Register">         </p>     </div> </form> {% endblock auth_form %}

这是注册页面的html,大家自行理解下,这里着重说一个我们在视图中通过flash()传递出来的消息,在模板中由以下代码接收!

<!-- 消息闪现 --> {% with messages = get_flashed_messages() %}     <b-message type="is-danger">     {% if messages %}         <ul class=flashes>             {% for message in messages %}             <li>{{ message }}</li>             {% endfor %}         </ul>     {% endif %}     </b-message> {% endwith %}

注册功能的后端逻辑视图, 路径为:app/auth/views/auth.py

@bp.route('/register', methods=['GET', 'POST']) def register():     # 注册视图     if request.method == 'POST':         username = request.form['username']         password = request.form['password']         password1 = request.form['password1']          if password != password1:             flash('两次密码输入不一致!')             return redirect(url_for('auth.register'))          exists_user = auth.User.query.filter_by(username=username).first()         if exists_user:             flash('该用户名已经存在,请更换其他用户名!')             return redirect(url_for('auth.register'))         else:                 user = auth.User(username=username, password=generate_password_hash(password))             db.session.add(user)             db.session.commit()             session.clear()             session['user_id'] = user.id         return redirect(url_for('index'))     return render_template('register.html')

这个注册的逻辑基本上涵盖了我们之前所有章节学到的知识点,这里就不再过多地去一一解释代码,大家可自行理解并完善注释!

实现用户退出登录功能

通过登录和注册功能的实现,我们已经清楚地知道,用户是否登录其实是判断session会话中是否存在用户的id来决定,那么推出登录,我们只需要清除session会话中的用户id即可,这里我们直接选择清空session的方式实现推出功能!

@bp.route('/logout') def logout():     # 注销     session.clear()     return redirect(url_for('index'))

在模板中获取用户信息

@bp.before_app_request def load_logged_in_user():     # 每个请求之前都回去session中查看user_id来获取用户     user_id = session.get('user_id')     if user_id is None:         g.user = None     else:         g.user = auth.User.query.get(int(user_id))

bp.before_app_request()注册一个在视图函数之前运行的函数,无论请求什么 URL。 都会先检查用户 ID 是否存储在会话中,并从数据库获取该用户的数据,将其存储在 g.user 上,该数据在请求期间持续。

注册完这个函数之后,我们就可以在base.html中的导航的最右侧通过g.user的返回值,判断用户是否已经登录,显示不同的信息!

<!-- 导航 --> {% block navbar %}      <template>     <b-navbar spaced shadow>         <template #brand>             <b-navbar-item>                 <img src="{{ url_for('blog.static', filename='img/logo.png') }}" alt="FlaskBlog">             </b-navbar-item>         </template>         <template #start>             <b-navbar-item href="#">                 Home             </b-navbar-item>          ... 省略部分代码         </template>           <template #end>             <b-navbar-item tag="div">                 <!-- 判断用户是否已登录 -->                 {% if g.user %}                               <div class=" buttons">                     <!-- 获取用户信心 -->                     <a class="button is-primary">欢迎您 {{ g.user['username'] }}</a>                     <a class="button is-success">个人中心</a>                     <!-- 显示推出按钮 -->                     <a class="button is-danger" href="{{ url_for('auth.logout') }}">退出</a>                 </div>                 {% else %}                 <!-- 用户未登录,显示登录注册按钮 -->                 <div class="buttons">                     <a class="button is-primary" href="{{ url_for('auth.register') }}">                         <strong>Sign up</strong>                     </a>                     <a class="button is-light" href="{{ url_for('auth.login') }}">Log in</a>                 </div>                 {% endif %}             </b-navbar-item>         </template>     </b-navbar> </template> {% endblock navbar %} <!-- 导航 end -->             

实现login_required装饰器

对于像下一章节我们要实现的用户中心以及管理后台,则必须是带有权限的访问,最基本的权限应该是必须是登录用户,那么所以说对于那些未登录的用户我们需要拒绝访问的功能!

这个其实思路也非常简单,既然在实现模板中调用用户信息的时候,我们把当前登录的用户信息添加到了g对象,那么我们只需要判断g.user的返回值是否为None即可判断用户是否登陆!

def login_required(view):     # 限制必须登录才能访问的页面装饰器     @functools.wraps(view)     def wrapped_view(**kwargs):         if g.user is None:             return redirect(url_for('auth.login'))         return view(**kwargs)     return wrapped_view

到这里关于用户登录注册相关的基本权限问题我们就完成了,注意这些视图函数都在app/auth/views/auth.py文件中!

科学上网全攻略:VPN、代理、SSH深度对比与最佳选择指南

在当今数字时代,互联网已成为人们获取信息、交流思想的重要平台。然而,由于各种原因,全球仍有不少国家和地区对网络访问施加限制,这使得"科学上网"成为许多网民必备的技能。面对VPN、代理和SSH这三种主流科学上网方式,究竟哪种更适合您?本文将为您全面解析这三种技术的原理、优劣势及适用场景,助您做出明智选择。

一、科学上网的本质与需求

所谓"科学上网",是指通过技术手段绕过地理限制或网络审查,访问被屏蔽的网站与服务。这种现象在全球范围内普遍存在,从中国的"防火长城"到中东某些国家的网络管制,再到欧美地区因版权问题对特定流媒体服务的区域限制,都催生了科学上网技术的蓬勃发展。

选择科学上网方式时,用户通常需要考虑以下关键因素:
- 安全性:数据传输是否加密,能否保护隐私
- 速度:是否影响正常网络使用体验
- 稳定性:连接是否可靠,是否容易被封锁
- 易用性:配置和使用是否简便
- 成本:是否需要付费及价格合理性

二、VPN:安全与隐私的首选

2.1 技术原理

VPN(Virtual Private Network,虚拟专用网络)通过在公共网络上建立加密隧道,将用户设备与远程服务器安全连接。所有网络流量都经过这条加密通道传输,使得外部无法窥探用户的实际访问内容。

2.2 显著优势

军事级加密保护:优质VPN服务采用AES-256等强加密算法,即使数据被截获也难以破解。这种安全级别甚至被政府机构和军队采用,足见其可靠性。

完美隐藏数字足迹:VPN不仅隐藏用户真实IP地址,还能防止ISP(网络服务提供商)监控网络活动。对于记者、活动人士等需要高度隐私保护的人群尤为重要。

全球服务器网络:主流VPN提供商在全球部署大量服务器,用户可轻松"切换"到其他国家,解锁Netflix、HBO等流媒体的区域限制内容。

2.3 潜在不足

速度瓶颈:加密过程和远程服务器中转必然带来延迟。根据测试,使用VPN后网速平均下降20%-50%,对在线游戏和4K视频流媒体影响明显。

成本考量:虽然存在免费VPN,但往往限制流量、速度慢且安全性存疑。可靠的付费VPN月费通常在5-15美元之间,长期使用是一笔不小开支。

法律风险:在某些严格管控网络的国家,使用VPN本身可能违法,用户需自行评估风险。

三、代理服务:轻量级解决方案

3.1 工作原理

代理服务器充当用户与目标网站之间的"中间人"。用户连接代理后,所有请求先发送至代理服务器,再由代理代为访问目标网站,最后将结果返回给用户。

3.2 突出优点

即装即用:大多数代理服务只需在浏览器设置中输入地址和端口即可使用,无需安装额外软件,对技术小白极为友好。

经济实惠:大量免费代理可供选择,付费代理价格也远低于VPN。对于偶尔需要访问被屏蔽网站的用户,代理是性价比之选。

特定场景高效:若仅需临时访问某个被屏蔽网页(如查看维基百科),代理的轻量化特性使其反应迅速,避免VPN的繁琐连接过程。

3.3 明显缺陷

安全隐患:普通HTTP代理不加密流量,意味着代理提供商可查看用户所有网络活动。曾有免费代理窃取用户银行账号的案例发生。

稳定性堪忧:代理服务器容易被识别和封锁,尤其在中国等网络审查严格的国家,免费代理往往"朝生暮死"。

功能局限:多数代理仅支持网页浏览,无法像VPN那样保护所有网络流量(如游戏、APP等)。

四、SSH隧道:技术达人的利器

4.1 技术解析

SSH(Secure Shell)本是用于远程管理服务器的安全协议,但通过建立SSH隧道(SSH Tunneling),可将本地网络流量通过加密通道转发至境外服务器,实现科学上网。

4.2 独特优势

极致安全:基于RSA等非对称加密算法,安全性甚至优于多数商业VPN,特别适合处理敏感信息的专业人士。

高度定制:技术用户可自主选择服务器、调整加密方式等参数,打造个性化解决方案。

资源占用低:相比VPN,SSH隧道更加轻量,对网络速度影响较小,适合需要稳定低延迟的场景。

4.3 使用门槛

配置复杂:需要命令行操作,设置端口转发等参数,非技术用户容易出错。有用户戏称:"配置SSH隧道的过程本身就是一种科学上网的障碍。"

功能单一:主要用于网页浏览和基础网络服务,不支持P2P下载等大流量应用,观看高清视频体验较差。

服务器依赖:用户需自行准备境外SSH服务器,对大多数人而言又是一道门槛。

五、三维度对比与选择建议

5.1 安全隐私对比

  • VPN:★★★★☆(商业VPN可能存在日志记录风险)
  • SSH:★★★★★(自主控制服务器,无第三方介入)
  • 代理:★☆☆☆☆(多数无加密,隐私风险高)

5.2 速度体验对比

  • 代理:★★★★☆(轻量级架构响应快)
  • SSH:★★★☆☆(加密开销小于VPN)
  • VPN:★★☆☆☆(双重加密显著降低速度)

5.3 易用性对比

  • 代理:★★★★★(浏览器设置即可)
  • VPN:★★★★☆(一键连接应用)
  • SSH:★☆☆☆☆(需技术背景)

5.4 终极选择指南

  • 隐私至上者:选择无日志记录的付费VPN或自建SSH隧道
  • 临时轻量用户:使用HTTPS代理满足基本浏览需求
  • 技术爱好者:SSH隧道提供最大化控制权
  • 流媒体爱好者:需要专为流媒体优化的VPN服务
  • 预算有限学生:可尝试信誉良好的免费代理或学生优惠VPN

六、未来趋势与风险警示

随着深度学习技术在网络审查中的应用,传统科学上网手段面临越来越大的挑战。有迹象表明,某些国家已开始部署能够识别和阻断VPN流量的系统。与此同时,新兴技术如WireGuard协议VPN、Tor网络等也在不断发展。

必须提醒的是:
1. 在某些地区,使用科学上网工具可能违反法律
2. 免费服务往往通过出售用户数据获利
3. 没有任何工具能保证100%匿名性

七、笔者点评

在这个信息即权力的时代,网络访问权已成为基本人权的重要组成部分。科学上网工具的百花齐放,既是对数字鸿沟的抗争,也折射出现实世界的地缘政治博弈。

从技术角度看,这三种方式各具特色:VPN如同装甲车,安全但笨重;代理好比自行车,灵活却脆弱;SSH则像特种装备,强大但需要专业技能。

值得深思的是,当人们需要动用各种技术手段才能获取本应自由流通的信息时,这本身就是一个时代的悖论。或许比选择工具更重要的是,我们始终保持着对知识自由的渴望和对真相的不懈追求。

无论选择哪种方式,请记住:技术只是工具,真正的"科学"精神在于独立思考与判断。愿每位读者都能在数字海洋中安全航行,抵达真理的彼岸。

版权声明:

作者: freeclashnode

链接: https://www.freeclashnode.com/news/article-2645.htm

来源: FreeClashNode

文章版权归作者所有,未经允许请勿转载。

免费节点实时更新

热门文章

最新文章

归档