如何在 Django 中强制用户注销?
在我的 Django 应用程序中,在某些条件下,我希望能够强制用户通过用户名注销。 不一定是当前登录的用户,而是另一个用户。 因此,我认为请求方法没有任何有关我要注销的用户的会话信息。
我熟悉 django.auth 和 auth。 logout 方法,但它接受 request 作为参数。 如果我只有用户名,是否有“Django 方式”来注销用户? 或者我必须推出自己的注销 SQL?
In my Django app under certain conditions I want to be able to force users to log out by a username. Not necessarily the current user who is logged in, but another user. So, the request method in my view doesn't have any session information about the user that I want to logout.
I am familiar with django.auth and with auth. logout method, but it takes request as an argument. Is there a "Django-way" to log the user out if all I have is the username? Or do I have to roll my own logout SQL?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
更新:
从 Django 1.7 开始,用户在密码更改时会自动注销。 对于每个请求,会将当前密码哈希值与会话中保存的值进行比较,如果不匹配,则用户将被注销。
因此,简单的密码更新就会导致用户注销。 然后,您可以禁用该帐户登录,或建议他们使用密码重置功能设置新密码并重新登录。
原文:
我认为 Django 中还没有一种认可的方法可以做到这一点。
用户 ID 存储在会话对象中,但它是经过编码的。 不幸的是,这意味着您必须遍历所有会话,解码并比较...
两个步骤:
首先删除目标用户的会话对象。 如果他们从多台计算机登录,他们将拥有多个会话对象。
然后,如果需要的话,将它们锁在外面......
Update:
Since Django 1.7, users are automatically logged-out when their password changes. On each request, the current password hash is compared to the value saved in their session and if doesn't match, the user is logged-out.
So, a simple password update has the effect of logging the user out. You can then disable the account for login, or advise them to use the password reset feature to set a new password and log in again.
Original:
I don't think there is a sanctioned way to do this in Django yet.
The user id is stored in the session object, but it is encoded. Unfortunately, that means you'll have to iterate through all sessions, decode and compare...
Two steps:
First delete the session objects for your target user. If they log in from multiple computers they will have multiple session objects.
Then, if you need to, lock them out....
尽管哈罗德的答案在这种特定情况下有效,但我至少可以看到两个重要问题:
Session
模型。为了解决这些问题,我建议您采取另一种方法来解决问题。 这个想法是将用户登录给定会话的日期以及上次请求用户注销的日期存储在某处。
然后,每当有人访问您的网站时,如果登录日期低于注销日期,您可以强制注销该用户。 正如 dan 所说,立即注销用户或在用户下次请求您的网站时注销并没有实际区别。
现在,让我们看看这个解决方案在 django 1.3b1 中的可能实现。 分三个步骤:
1. 在会话中存储上次登录日期
幸运的是,Django auth 系统公开了 信号称为
user_logged_in
。 您只需注册该信号,并将当前日期保存在会话中。 在models.py
的底部:2. 请求用户强制注销
我们只需向
User
模型添加一个字段和一个方法。 有多种方法可以实现这一点(用户配置文件、模型继承等.) 各有利弊。为了简单起见,我将在这里使用模型继承,如果您选择这个解决方案,请不要忘记 编写自定义身份验证后端。
然后,如果您想强制用户
johndoe
注销,您只需:3. 实施访问检查
最好的方法是使用 中间件 正如丹建议的那样。 此中间件将访问
request.user
,因此您需要将其放在'django.contrib.auth.middleware.AuthenticationMiddleware'
之后代码> MIDDLEWARE_CLASSES 设置。应该可以做到这一点。
注释
请注意为用户存储额外字段对性能的影响。 使用模型继承将添加额外的
JOIN
。 使用用户配置文件将添加额外的查询。 直接修改User
是性能方面的最佳方法,但它仍然是一个毛茸茸的东西主题。如果您在现有网站上部署该解决方案,则现有会话可能会遇到一些问题,因为现有会话没有
'LAST_LOGIN_DATE'
键。 您可以稍微调整一下中间件代码来处理这种情况:在 django 1.2.x 中,没有
user_logged_in
信号。 回退到覆盖login
函数:Although Harold's answer works in this specific case, I can see at least two important issues with it:
Session
model would not be used.To solve those issues, I suggest you take another approach at the problem. The idea is to store somewhere the date when the user was logged in for a given session, and the last time you requested a user to be logged out.
Then whenever someone access your site, if the logged in date is lower than the log out date, you can force-logout the user. As dan said, there's no practical difference between logging out a user immediately or on his next request to your site.
Now, let's see a possible implementation of this solution, for django 1.3b1. In three steps:
1. store in the session the last login date
Fortunately, Django auth system exposes a signal called
user_logged_in
. You just have to register that signals, and save the current date in the session. At the bottom of yourmodels.py
:2. request a force logout for a user
We just need to add a field and a method to the
User
model. There's multiple ways to achieve that (user profiles, model inheritance, etc.) each with pros and cons.For the sake of simplicity, I'm gonna use model inheritance here, if you go for this solution, don't forget to write a custom authentication backend.
Then, if you want to force logout for user
johndoe
, you just have to:3. implement the check on access
Best way here is to use a middleware as dan suggested. This middleware will access
request.user
, so you need to put it after'django.contrib.auth.middleware.AuthenticationMiddleware'
in yourMIDDLEWARE_CLASSES
setting.That should do it.
Notes
Be aware of the performance implication of storing an extra field for your users. Using model inheritance will add an extra
JOIN
. Using user profiles will add an extra query. Modifying directly theUser
is the best way performance wise, but it is still a hairy topic.If you deploy that solution on an existing site, you will probably have some trouble with existing sessions, which won't have the
'LAST_LOGIN_DATE'
key. You can adapt a bit the middleware code to deal with that case :In django 1.2.x, there is no
user_logged_in
signal. Fall back to overriding thelogin
function:我的应用程序中需要类似的东西。 就我而言,如果用户设置为非活动状态,我想确保用户是否已经登录,他们将被注销并且无法继续使用该网站。 读完这篇文章后,我得出了以下解决方案:
只需在您的设置中添加此中间件即可。 在更改密码的情况下,您可以在用户配置文件模型中引入一个新字段,强制用户注销,检查该字段的值而不是上面的 is_active,并在用户登录时取消设置该字段。后者可以使用 Django 的 user_logged_in 信号来完成。
I needed something similar in my app. In my case, if a user was set to inactive, I wanted to make sure if the user was already logged in that they will be logged out and not able to continue to use the site. After reading this post, I came to the following solution:
Just add this middleware in your settings and off you go. In the case of changing passwords, you could introduce a new field in the userprofile model that forces a user to logout, check for the value of the field instead of is_active above, and also unset the field when a user logs in. The latter can be done with Django's user_logged_in signal.
也许是一个引用被迫注销的用户列表的中间件。 下次用户尝试执行任何操作时,将其注销,重定向它们等。
当然,除非他们需要立即注销。 但话又说回来,直到他们下次尝试提出请求时他们才会注意到,所以上述解决方案可能会起作用。
Perhaps, a bit of middleware that references a list of users who have been forced to log out. Next time the user tries to do anything, log them out then, redirects them, etc.
Unless of course, they need to be logged out immediately. But then again, they wouldn't notice until they next tried to make a request anyway, so the above solution may just work.
这是对 Balon 查询的回应:
是的,需要迭代大约 140k 个会话,我可以明白为什么 Harold 的答案可能没有您希望的那么快!
我建议的方法是添加一个模型,其仅有两个属性是
User
和Session
对象的外键。 然后添加一些中间件,使该模型与当前用户会话保持同步。 我以前使用过这种设置; 就我而言,我从这个单点登录系统借用了sessionprofile
模块对于 phpBB (请参阅“django/sessionprofile”文件夹中的源代码),这(我认为)会满足您的需求。您最终会得到的是代码中某处的一些管理功能,如下所示(假设与上面链接的
sessionprofile
模块中的代码名称和布局相同):(我认为这也会删除
SessionProfile
对象,据我所知,当删除ForeignKey
引用的对象时,Django 的默认行为是级联它并删除包含ForeignKey
的对象code>,但如果没有,那么完成后删除sessionProfiles
的内容就足够了。)This is in response to Balon's query:
Yes, with around 140k sessions to iterate through I can see why Harold's answer may not be as fast as you may like!
The way I would recommend is to add a model whose only two properties are foreign keys to
User
andSession
objects. Then add some middleware that keeps this model up-to-date with current user sessions. I have used this sort of setup before; in my case, I borrowed thesessionprofile
module from this Single Sign-On system for phpBB (see the source code in the "django/sessionprofile" folder) and this (I think) would suit your needs.What you would end up with is some management function somewhere in your code like this (assuming the same code names and layout as in the
sessionprofile
module linked above):(I think this will also delete the
SessionProfile
objects, as from what I recall, Django's default behaviour when an object referenced by aForeignKey
is deleted is to cascade it and also delete the object containing theForeignKey
, but if not then it is trivial enough to delete the contents ofsessionProfiles
when you are done.)您还可以使用直接 django 函数来执行此操作,它将更新并注销用户的所有其他会话(当前会话除外)。
update_session_auth_hash 的文档此处。
You can also use direct django function to do that, it will update and logs out all other sessions for the user, except the current one.
Docs for update_session_auth_hash here.
作为 Tony Abou-Assaleh,我还需要注销设置为非活动状态的用户,因此我首先实施他的解决方案。 一段时间后,我发现中间件强制对所有请求进行数据库查询(以检查用户是否被阻止),从而损害了不需要登录的页面的性能。
我有一个自定义用户对象和 Django >= 1.7,所以我最终做的是覆盖它的
get_session_auth_hash
函数在用户不活动时使会话无效。 一个可能的实现是:为此,
django.contrib.auth.middleware.SessionAuthenticationMiddleware
应该位于settings.MIDDLEWARE_CLASSES
中As Tony Abou-Assaleh, I also needed to log out users who were set to inactive, so I started by implementing his solution. After some time I found out that the middleware is forcing a DB query on all requests (to check if the user was blocked), and thus hurts performance on pages that doesn't require login.
I have a custom user object and Django >= 1.7, so what I ended up doing is overriding its
get_session_auth_hash
function to invalidate the session when the user is inactive. A possible implementation is:For this to work,
django.contrib.auth.middleware.SessionAuthenticationMiddleware
should be insettings.MIDDLEWARE_CLASSES
正如其他人所说,您可以迭代数据库中的所有会话,对所有会话进行解码,并删除属于该用户的会话。 但它很慢,特别是当您的网站流量很高并且有很多会话时。
如果您需要更快的解决方案,可以使用会话后端来查询和获取特定用户的会话。 在这些会话后端中,Session 有一个 User 的外键,因此您不需要迭代所有会话对象:
db
、cached_db
会话后端)db
会话后端)使用这些后端,删除 的所有会话用户可以通过一行代码完成:
免责声明:我是
django-qsessions
的作者。As others stated, you can iterate over all sessions in DB, decode all of them, and delete those belonging to that user. But it's slow, particularly if your site has high traffic and there are lots of sessions.
If you need a faster solution, you can use a session backend that lets you query and get the sessions of a specific user. In these session backends, Session has a foreign key to User, so you don't need to iterate over all session objects:
db
,cached_db
session backends)db
session backend)Using these backends, deleting all sessions of a user can be done in a single line of code:
Disclaimer: I am the author of
django-qsessions
.from django.contrib.sessions.models import Session
删除用户会话
from django.contrib.sessions.models import Session
deleting user session
即使我也遇到过这个问题。 来自印度的垃圾邮件发送者很少会继续发布关于 Baba 和 Molvi 的爱情解决方案。
我所做的是在发布时插入以下代码:
Even I faced this issue. Few spammers from India keep posting about those Baba and Molvi for love solutions.
What I did is at the time of posting just inserted this code: