该文原创为新潮质量保障技术团队中的 “上进的中年软件测试从业者”,用于技术交流分享
上一次我们讲了测试平台的初始化和运行,这一次我们会对测试平台如何实现登录工作做讲解。
登录功能的实现包含以下组成部分:
在第一篇的测试平台选型中我们有介绍到之所以选择 flask_admin, 原因在于我个人对于前端的知识匮乏,并且不想在前端花太多的时间,把主要的精力放在平台的功能实现上。这相比我之前在 TCL 的时候的那几个技术大牛,简直差的不是一点半点,当时他们实现的登录功能,不仅有很好的前端内容渲染,并且支持邮箱验证和验证码功能。
按照我的习惯,这肯定直接网上扒一个下来就用了。最早我还天真的想用弹出框的实现登录的表单提交,调研了两个多小时,我放弃了,因为网上没找到成型的可以直接拿来抄的。最终的页面就就是如下两个(首页和登录页面):
得益于公司强大的运维团队提供的 LDAP 认证,我只需要提供用户名和密码调接口就可以拿到登录是否认证成功的信息。来到这边发现很多人对于域账号没有太多的感念,感觉就是某一个系统的账号而已。刚毕业的前几年,经历的公司基本上都是大型公司,至少从人数和跨区域性来说。这些公司的最主要的特点就是有强大的 IT 部门,有完善的入职和离职手续。可以做到所有的系统用的全部都是一个账号,包括你用的电脑,这就是域账号。而每一个系统全部接入域账号管理,通过权限控制是否具备对应系统的权限。入职的时候,根据你所在的部门一键创建分配;在你离职的时候,一键删除。再多说一点,IP 与 mac 绑定,而电脑登录又是你的域账号,这绝对不会出现前几天遇到的一个问题,行政部门让大家看一下谁的 IP 是 10.X.X.X,这台电脑一直在局域网发攻击包,我不知道该说什么,就如同一个哥们说他们公司,大老板让所有人把自己的密码改强密码策略,而不是让系统部门改登录强校验。
好了,回归正题。登录请求从前端以表单的形式提交如下:
<div class="form-bottom">
<form role="form" action="/login" method="post" class="login-form">
<div class="form-group">
<label class="sr-only" for="form-username">Username</label>
<input type="text" name="form-username" placeholder="Username..."
class="form-username form-control" id="form-username">
</div>
<div class="form-group">
<label class="sr-only" for="form-password">Password</label>
<input type="password" name="form-password" placeholder="Password..."
class="form-password form-control" id="form-password">
</div>
<button type="submit" class="btn">Sign in!</button>
</form>
</div>
@expose('/login', methods=["GET", "POST"])
def login(self):
username = request.form.get("form-username")
password = request.form.get("form-password")
cur_user = User()
cur_user.id = username
login_user(cur_user)
token = login_to_ad(username=username, password=password)
if token:
session["username"] = username
refer = request.headers.get("Referer")
if refer.endswith("login"):
return redirect("/userinfo")
if not refer.endswith("loginMainPage"): # 如果从其他页面登录的,则直接跳转到其他页面。
return redirect(refer)
if has_permission(USER):
return redirect("/userinfo")
else:
return self.render('home.html', login="Login" if not current_user.is_authenticated else "Logout",
user=True if current_user.is_authenticated else False)
else:
# flash('Wrong username or password!')
return self.render('Login/index.html', wrong_info="Wrong username/password, please try again!")
这里的 User,网上大部分给的都是 mysql 的数据模型,并且采用的都是单独的密码管理体系,自己需要做密码的 md5 加解密之类的操作。既然我们选择了域账号管理,还是需要引用框架,目的只有一个,就是装载登录过程,让系统在运行过程中知道当前的用户是谁。
下面这部分代码就是 User 的数据库表结构:
class User(UserMixin, Document):
testerName = StringField(db_field="testerName", validators=[DataRequired(message=u"姓名不能为空")])
username = StringField(db_field="testerId", validators=[DataRequired(message=u"域账号ID不能为空")])
testerDepartment = StringField(db_field="testerDepartment", validators=[DataRequired(message=u"项目测试负责人不能为空")])
meta = {
"collection": "tester"
}
def __repr__(self):
return '<User %r>' % self.username
config.app.config['SESSION_TYPE'] = 'mongodb' # session类型为mongodb
config.app.config['SESSION_MONGODB'] = MongoClient(host=mongo_host)
config.app.config['SESSION_MONGODB_DB'] = config.db_name
config.app.config['SESSION_MONGODB_COLLECT'] = 'session'
config.app.config['SESSION_PERMANENT'] = True # 如果设置为True,则关闭浏览器session就失效。
config.app.config['SESSION_USE_SIGNER'] = False # 是否对发送到浏览器上session的cookie值进行加密
config.app.config['SESSION_KEY_PREFIX'] = 'session:' # 保存到session中的值的前缀
config.app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=5) # 5个小时过期
Session(config.app)
之前将初始化的时候,有带过这部分,实际上就是程序初始化的时候,把 session 的配置加载到 app 里面,与常规不同的是,我们这里依然用了 Mongo 作为会话管理, 在测试平台里面,我们是用了 5 小时的超时机制,如上面的代码配置。
数据内容如下:
这里面的BLOB数据,在后续的文件处理中会有介绍。最近特别忙,PMP 和公司的事。这篇文章应该在周天下午就发出来,吐槽一下公司的网络,真的很烂,写到一半死活保存不了,今天只能重新写。
好了,今天就介绍到这里,感谢大家的耐心阅读,下一期我们将介绍如何实现权限和角色管理,下次见!