session 修改密码python_django修改密码强制退出机制

起因

BUG出现

系统升级django版本后经常出现自动退出登录

问题复现

系统升级django(大版本,如1.8、1.11和2.0)后,旧版与新版同时运行,同一各User用旧版authenticate验证后会导致新版中已登录User被退出。

正常使用中的登陆、退出和会话保持

登录

from django.contrib.auth import authenticate, login , logout

def login(request):

if request.method == "POST":

username = request.POST.get('username', '')

password = request.POST.get('password', '')

user = authenticate(username=username, password=password) # 验证用户帐号密码是否正确,通过后返回User对象

if not user:

login(request, user) # 登录,response添加cookie并将session_key记录到django_session

request.session.set_expiry(0)# 设置session失效时间,0表示关闭浏览器失效

return HttpResponseRedirect('/')

c1615cadef703486e822f278603c4b39.png

退出

from django.contrib.auth import authenticate, login , logout

def logout(request):

logout(request)# 清除response的cookie和django_session中记录

return HttpResponseRedirect('/login')

3d060e944d9c68b03d6a0dcbae410c58.png

保持登陆状态

成功登录后浏览器将在每次请求时Header中附带cookie

7ab45ff56f996d6f461d4141cd53804b.png

settings.py配置:

SECRET_KEY = '%iai=j+uyd4s0t3qm$3w-w6^b0lf!df%hh-@5f!0&4enc(o7#5'# key变化密码的hash也会变

MIDDLEWARE = (

# ... 无关部分省略

'django.contrib.sessions.middleware.SessionMiddleware', # 获取cookie中sessionid对应的django_session,有效的时候会把session对象增加到request.session

'django.contrib.auth.middleware.AuthenticationMiddleware',# 根据request.session获取对应User并校验是否有效

# ... 无关部分省略

)

寻根溯源

具体过程比较曲折,一言难尽。

会话保持依赖两个中间件SessionMiddleware和AuthenticationMiddleware,其中AuthenticationMiddleware通过request.session获取用户对象,并比对此时用户认证和session中存储的认证是否相同

HASH_SESSION_KEY = '_auth_user_hash'

# auth.get_user

def get_user(request):

"""

Return the user model instance associated with the given request session.

If no user is retrieved, return an instance of `AnonymousUser`.

"""

from .models import AnonymousUser

user = None

try:

user_id = _get_user_session_key(request)

backend_path = request.session[BACKEND_SESSION_KEY]

except KeyError:

pass

else:

if backend_path in settings.AUTHENTICATION_BACKENDS:

backend = load_backend(backend_path)

user = backend.get_user(user_id)

# Verify the session

if hasattr(user, 'get_session_auth_hash'):

session_hash = request.session.get(HASH_SESSION_KEY)

session_hash_verified = session_hash and constant_time_compare(

session_hash,

user.get_session_auth_hash()

)

if not session_hash_verified:

request.session.flush()

user = None

return user or AnonymousUser()

也就是session_hash_verified = session_hash and constant_time_compare(session_hash,user.get_session_auth_hash()),对比session._auth_user_hash和user.get_session_auth_hash()是否相同 django_session中session_data默认存储的如下信息:

{

'_auth_user_id': '25',

'_auth_user_backend': 'django.contrib.auth.backends.ModelBackend',

'_auth_user_hash': '54332f14831144a4022e7601c6dd3fcc531a5454',

'_session_expiry': 0

}

user.get_session_auth_hash()如下(User的父类):

class AbstractBaseUser(models.Model):

def get_session_auth_hash(self):

"""

Return an HMAC of the password field.

"""

key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash"

return salted_hmac(key_salt, self.password).hexdigest()

那么影响hash的就是user.password,django这么做原本是要实现修改密码强制用户自动退出。django文档

修改密码强制用户自动退出的特性在django 2.0之前版本需要添加'django.contrib.auth.middleware.SessionAuthenticationMiddleware'到 MIDDLEWARE_CLASSES才能启用此特性,2.0开始移除此中间件并强制启用此特性

当用户密码被修改后,hash变化,判断为session失效

当不同版本django中authenticate()时会自动重新生成密码的密文,导致数据库中密码密文变化,hash也变化,判断为session失效,这就是文章开始bug的原因

升级django密码密文变化原因d

绕过修改密码强制退出的方法

两种方法

方法1: 每次请求不严重hash(仅适用于自定义User的) 屏蔽user.get_session_auth_hash()方法,使跳过hash比对

class User(AbstractBaseUser):

# ... 无关的省略

def __getattribute__(self, item):

if item == 'get_session_auth_hash':

raise AttributeError

return super().__getattribute__(item)

方法2: 保证不同版本密码密文一致 settings.py

PASSWORD_HASHERS = [

'core.password.CustomPBKDF2PasswordHasher',

]

core.password.py

from django.contrib.auth.hashers import PBKDF2PasswordHasher

class CustomPBKDF2PasswordHasher(PBKDF2PasswordHasher):

iterations = 20000# 所有系统指定此值为一样,能使密码密文不变

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值