[技术博客] 使用邮箱验证并激活账户
为什么需要使用邮箱验证账户?
我们的项目的特殊之处在于,是通过向用户邮箱发送邮件的方式来实现DDL的提醒的,因此需要确保用户的邮箱是正确的,如果注册时使用的邮箱是错误的,不仅会导致用户收不到DDL提醒邮件,也会打扰到别人。
此外,我们选择使用北航邮箱进行验证,这样做还有一个好处在于,北航邮箱是绑定学号的,即可以直接使用“学号@buaa.edu.cn”的格式来发送邮件。这样在创建账户时,可以确保用户输入的是自己的学号,而不能冒名顶替使用他人的学号注册。
技术原理
1. 在数据库中为User增加is_active字段
为了实现没有完成邮箱验证的用户不能登陆,我们在数据库中的User实体中添加了is_active字段,以标识用户好是否激活,并在用户登录时检查账户是否激活,如果未激活就不能登陆。在用户完成邮箱验证后,将该用户的is_active字段设置为True(已激活)。
class User(models.Model):
'''
Other attributes...
'''
is_active = models.BooleanField(default=False)
2. 生成激活链接并发送激活邮件
为了给每个用户生成其独立的激活链接,我们新建了一个Token类,通过itsdangerous中的URLSafeTimedSerializer类来生成和验证Token。调用Token类,使用用户的uid+SECRET_KEY结合生成token,并将这个token加入激活链接的url部分。
class Token():
def __init__(self, security_key):
self.security_key = security_key
self.salt = base64.b64encode(security_key.encode(encoding='utf-8'))
def generate_validate_token(self, username):
serializer = utsr(self.security_key)
return serializer.dumps(username, self.salt)
def confirm_validate_token(self, token, expiration=3600):
serializer = utsr(self.security_key)
return serializer.loads(token, salt=self.salt, max_age=expiration)
def create_user(request): #用户注册
'''
Other codes...
'''
token_confirm = Token(settings.SECRET_KEY)
token = token_confirm.generate_validate_token(data["uid"])
message = "\n".join([
u'亲爱的 {0} {1}, 欢迎使用ddl_killer'.format(data["uid"], data['username']),
u'请访问该链接,完成用户验证: <a href="http://123.57.67.161:8000/api/activate/? token={0}">ddl_killer 注册链接</a>'.format(token),
u'ddl_killer 团队至上.'])
在发送邮件部分,我们使用了yagmail来发送邮件。
settings.YAG.send([data['email']], u'ddl_killer 注册用户验证信息', message, None)
3. 响应激活链接并激活用户
在Django后端收到激活链接格式的url请求时,将进入active_user视图,完成用户的激活。
def active_user(request):
token = request.GET['token']
token_confirm = Token(settings.SECRET_KEY)
try:
uid = token_confirm.confirm_validate_token(token)
print(uid)
except:
return HttpResponse(u'对不起,验证链接已经过期')
try:
user = User.objects.get(uid=uid)
except User.DoesNotExist:
return HttpResponse(u'对不起,您所验证的用户不存在,请重新注册')
user.is_active = True
user.save()
confirm = u'验证成功,请进行登录操作。'
return HttpResponseRedirect('/', {'msg':confirm})