标签归档:authentication

在Python中加盐并哈希密码

问题:在Python中加盐并哈希密码

该代码应该用盐来散列密码。盐和哈希密码将保存在数据库中。密码本身不是。

鉴于该操作的敏感性,我想确保所有内容都是洁净的。

import hashlib
import base64
import uuid

password = 'test_password'
salt     = base64.urlsafe_b64encode(uuid.uuid4().bytes)


t_sha = hashlib.sha512()
t_sha.update(password+salt)
hashed_password =  base64.urlsafe_b64encode(t_sha.digest())

This code is supposed to hash a password with a salt. The salt and hashed password are being saved in the database. The password itself is not.

Given the sensitive nature of the operation, I wanted to make sure everything was kosher.

import hashlib
import base64
import uuid

password = 'test_password'
salt     = base64.urlsafe_b64encode(uuid.uuid4().bytes)


t_sha = hashlib.sha512()
t_sha.update(password+salt)
hashed_password =  base64.urlsafe_b64encode(t_sha.digest())

回答 0

编辑:这个答案是错误的。SHA512的单次迭代速度很快,这使其不适合用作密码哈希函数。请在此处使用其他答案之一。


我看起来不错。但是,我敢肯定您实际上并不需要base64。您可以这样做:

import hashlib, uuid
salt = uuid.uuid4().hex
hashed_password = hashlib.sha512(password + salt).hexdigest()

如果这不会造成麻烦,则可以通过将salt和哈希密码存储为原始字节而不是十六进制字符串,从而在数据库中获得稍微更有效的存储。要做到这一点,更换hexbyteshexdigestdigest

EDIT: This answer is wrong. A single iteration of SHA512 is fast, which makes it inappropriate for use as a password hashing function. Use one of the other answers here instead.


Looks fine by me. However, I’m pretty sure you don’t actually need base64. You could just do this:

import hashlib, uuid
salt = uuid.uuid4().hex
hashed_password = hashlib.sha512(password + salt).hexdigest()

If it doesn’t create difficulties, you can get slightly more efficient storage in your database by storing the salt and hashed password as raw bytes rather than hex strings. To do so, replace hex with bytes and hexdigest with digest.


回答 1

基于此问题的其他答案,我使用bcrypt实现了一种新方法。

为什么要使用bcrypt

如果我理解正确,使用的说法bcryptSHA512bcrypt被设计成缓慢。bcrypt还提供了一个选项,用于调整首次生成哈希密码时的速度:

# The '12' is the number that dictates the 'slowness'
bcrypt.hashpw(password, bcrypt.gensalt( 12 ))

缓慢是可取的,因为如果恶意方将他们的手放到包含哈希密码的表上,那么蛮力地将它们强行加起来就困难得多。

实作

def get_hashed_password(plain_text_password):
    # Hash a password for the first time
    #   (Using bcrypt, the salt is saved into the hash itself)
    return bcrypt.hashpw(plain_text_password, bcrypt.gensalt())

def check_password(plain_text_password, hashed_password):
    # Check hashed password. Using bcrypt, the salt is saved into the hash itself
    return bcrypt.checkpw(plain_text_password, hashed_password)

笔记

我能够使用以下命令在Linux系统中轻松安装该库:

pip install py-bcrypt

但是,我在Windows系统上安装它时遇到了更多麻烦。它似乎需要一个补丁。看到这个Stack Overflow问题:在Win 7 64位python上安装py-bcrypt

Based on the other answers to this question, I’ve implemented a new approach using bcrypt.

Why use bcrypt

If I understand correctly, the argument to use bcrypt over SHA512 is that bcrypt is designed to be slow. bcrypt also has an option to adjust how slow you want it to be when generating the hashed password for the first time:

# The '12' is the number that dictates the 'slowness'
bcrypt.hashpw(password, bcrypt.gensalt( 12 ))

Slow is desirable because if a malicious party gets their hands on the table containing hashed passwords, then it is much more difficult to brute force them.

Implementation

def get_hashed_password(plain_text_password):
    # Hash a password for the first time
    #   (Using bcrypt, the salt is saved into the hash itself)
    return bcrypt.hashpw(plain_text_password, bcrypt.gensalt())

def check_password(plain_text_password, hashed_password):
    # Check hashed password. Using bcrypt, the salt is saved into the hash itself
    return bcrypt.checkpw(plain_text_password, hashed_password)

Notes

I was able to install the library pretty easily in a linux system using:

pip install py-bcrypt

However, I had more trouble installing it on my windows systems. It appears to need a patch. See this Stack Overflow question: py-bcrypt installing on win 7 64bit python


回答 2

聪明的事不是自己写加密货币,而是使用类似passlib的东西:https ://bitbucket.org/ecollins/passlib/wiki/Home

以安全的方式编写密码很容易造成混乱。令人讨厌的是,使用非加密代码时,由于程序崩溃,当它不起作用时,您经常会立即注意到它。使用密码时,您通常只会发现到很晚才发现您的数据已遭到破坏。因此,我认为最好使用由其他人编写的软件包,该软件包基于经过战斗力测试的协议,对此问题有一定的了解。

passlib还具有一些不错的功能,可以使它易于使用,并且如果原来的协议被破坏,还可以轻松升级到更新的密码哈希协议。

同样,只有一轮sha512更容易受到字典攻击。sha512的设计速度很快,而在尝试安全存储密码时,这实际上是一件坏事。其他人已经对所有此类问题进行了漫长而艰难的思考,因此您最好利用这一点。

The smart thing is not to write the crypto yourself but to use something like passlib: https://bitbucket.org/ecollins/passlib/wiki/Home

It is easy to mess up writing your crypto code in a secure way. The nasty thing is that with non crypto code you often immediately notice it when it is not working since your program crashes. While with crypto code you often only find out after it is to late and your data has been compromised. Therefor I think it is better to use a package written by someone else who is knowledgable about the subject and which is based on battle tested protocols.

Also passlib has some nice features which make it easy to use and also easy to upgrade to a newer password hashing protocol if an old protocol turns out to be broken.

Also just a single round of sha512 is more vulnerable to dictionary attacks. sha512 is designed to be fast and this is actually a bad thing when trying to store passwords securely. Other people have thought long and hard about all this sort issues so you better take advantage of this.


回答 3

为了使它在Python 3中工作,您需要使用UTF-8编码,例如:

hashed_password = hashlib.sha512(password.encode('utf-8') + salt.encode('utf-8')).hexdigest()

否则,您将获得:

追溯(最近一次通话最近):
文件“”,第1行,在
hashed_pa​​ssword = hashlib.sha512(password + salt).hexdigest()
TypeError:Unicode对象必须在散列之前编码

For this to work in Python 3 you’ll need to UTF-8 encode for example:

hashed_password = hashlib.sha512(password.encode('utf-8') + salt.encode('utf-8')).hexdigest()

Otherwise you’ll get:

Traceback (most recent call last):
File “”, line 1, in
hashed_password = hashlib.sha512(password + salt).hexdigest()
TypeError: Unicode-objects must be encoded before hashing


回答 4

从Python 3.4开始,hashlib标准库中的模块包含“被设计用于安全密码散列”的密钥派生函数。

因此,请使用其中一种,例如hashlib.pbkdf2_hmac,使用以下方法生成的盐os.urandom

from typing import Tuple
import os
import hashlib
import hmac

def hash_new_password(password: str) -> Tuple[bytes, bytes]:
    """
    Hash the provided password with a randomly-generated salt and return the
    salt and hash to store in the database.
    """
    salt = os.urandom(16)
    pw_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    return salt, pw_hash

def is_correct_password(salt: bytes, pw_hash: bytes, password: str) -> bool:
    """
    Given a previously-stored salt and hash, and a password provided by a user
    trying to log in, check whether the password is correct.
    """
    return hmac.compare_digest(
        pw_hash,
        hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    )

# Example usage:
salt, pw_hash = hash_new_password('correct horse battery staple')
assert is_correct_password(salt, pw_hash, 'correct horse battery staple')
assert not is_correct_password(salt, pw_hash, 'Tr0ub4dor&3')
assert not is_correct_password(salt, pw_hash, 'rosebud')

注意:

  • 使用16字节盐和PBKDF2的100000迭代与Python文档中建议的最小数目相匹配。进一步增加迭代次数将使散列的计算速度变慢,因此更加安全。
  • os.urandom 始终使用加密安全的随机源
  • hmac.compare_digest在中使用的is_correct_password,基本上只是==字符串的运算符,但没有短路能力,这使其不受定时攻击的影响。那可能实际上并没有提供任何额外的安全性价值,但是也没有什么坏处,所以我继续使用它。

有关如何进行良好的密码哈希处理的理论以及适用于对密码进行哈希处理的其他功能的列表,请参见https://security.stackexchange.com/q/211/29805

As of Python 3.4, the hashlib module in the standard library contains key derivation functions which are “designed for secure password hashing”.

So use one of those, like hashlib.pbkdf2_hmac, with a salt generated using os.urandom:

from typing import Tuple
import os
import hashlib
import hmac

def hash_new_password(password: str) -> Tuple[bytes, bytes]:
    """
    Hash the provided password with a randomly-generated salt and return the
    salt and hash to store in the database.
    """
    salt = os.urandom(16)
    pw_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    return salt, pw_hash

def is_correct_password(salt: bytes, pw_hash: bytes, password: str) -> bool:
    """
    Given a previously-stored salt and hash, and a password provided by a user
    trying to log in, check whether the password is correct.
    """
    return hmac.compare_digest(
        pw_hash,
        hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    )

# Example usage:
salt, pw_hash = hash_new_password('correct horse battery staple')
assert is_correct_password(salt, pw_hash, 'correct horse battery staple')
assert not is_correct_password(salt, pw_hash, 'Tr0ub4dor&3')
assert not is_correct_password(salt, pw_hash, 'rosebud')

Note that:

  • The use of a 16-byte salt and 100000 iterations of PBKDF2 match the minimum numbers recommended in the Python docs. Further increasing the number of iterations will make your hashes slower to compute, and therefore more secure.
  • os.urandom always uses a cryptographically secure source of randomness
  • hmac.compare_digest, used in is_correct_password, is basically just the == operator for strings but without the ability to short-circuit, which makes it immune to timing attacks. That probably doesn’t really provide any extra security value, but it doesn’t hurt, either, so I’ve gone ahead and used it.

For theory on what makes a good password hash and a list of other functions appropriate for hashing passwords with, see https://security.stackexchange.com/q/211/29805.


回答 5

如果需要使用现有系统存储的哈希,passlib似乎很有用。如果您可以控制格式,请使用现代的哈希,例如bcrypt或scrypt。目前,bcrypt在python中似乎更容易使用。

passlib支持bcrypt,建议安装py-bcrypt作为后端:http : //pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html

如果您不想安装passlib,也可以直接使用py-bcrypt。自述文件包含一些基本用法示例。

另请参阅:如何在Python中使用scrypt为密码和盐生成哈希

passlib seems to be useful if you need to use hashes stored by an existing system. If you have control of the format, use a modern hash like bcrypt or scrypt. At this time, bcrypt seems to be much easier to use from python.

passlib supports bcrypt, and it recommends installing py-bcrypt as a backend: http://pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html

You could also use py-bcrypt directly if you don’t want to install passlib. The readme has examples of basic use.

see also: How to use scrypt to generate hash for password and salt in Python


回答 6

我不想复活旧线程,但是…任何想使用现代最新安全解决方案的人都可以使用argon2。

https://pypi.python.org/pypi/argon2_cffi

它赢得了密码哈希竞赛。(https://password-hashing.net/)比bcrypt更易于使用,并且比bcrypt更安全。

I don’ want to resurrect an old thread, but… anyone who wants to use a modern up to date secure solution, use argon2.

https://pypi.python.org/pypi/argon2_cffi

It won the the password hashing competition. ( https://password-hashing.net/ ) It is easier to use than bcrypt, and it is more secure than bcrypt.


回答 7

首先导入:

import hashlib, uuid

然后根据您的方法更改此代码:

uname = request.form["uname"]
pwd=request.form["pwd"]
salt = hashlib.md5(pwd.encode())

然后在数据库sql查询中传递此salt和uname,在login下面是一个表名:

sql = "insert into login values ('"+uname+"','"+email+"','"+salt.hexdigest()+"')"

Firstly import:-

import hashlib, uuid

Then change your code according to this in your method:

uname = request.form["uname"]
pwd=request.form["pwd"]
salt = hashlib.md5(pwd.encode())

Then pass this salt and uname in your database sql query, below login is a table name:

sql = "insert into login values ('"+uname+"','"+email+"','"+salt.hexdigest()+"')"

Google Authenticator在Python中的实现

问题:Google Authenticator在Python中的实现

我正在尝试使用可以通过Google Authenticator应用程序生成的一次性密码。

Google身份验证器的功能

基本上,Google身份验证器实现两种类型的密码:

  • HOTP-基于HMAC的一次性密码,这意味着密码会在每次呼叫时更改,以符合RFC4226的要求,并且
  • TOTP-基于时间的一次性密码,每30秒更改一次(据我所知)。

Google身份验证器也可以在此处作为开源使用:code.google.com/p/google-authenticator

当前代码

我一直在寻找用于生成HOTP和TOTP密码的现有解决方案,但没有找到太多。我拥有的代码是负责生成HOTP的以下代码段:

import hmac, base64, struct, hashlib, time

def get_token(secret, digest_mode=hashlib.sha1, intervals_no=None):
    if intervals_no == None:
        intervals_no = int(time.time()) // 30
    key = base64.b32decode(secret)
    msg = struct.pack(">Q", intervals_no)
    h = hmac.new(key, msg, digest_mode).digest()
    o = ord(h[19]) & 15
    h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
    return h

我面临的问题是,使用上述代码生成的密码与使用Android的Google Authenticator应用生成的密码不同。即使我努力过多个intervals_no值(第一完全相同10000,开头intervals_no = 0),以secret等于在GA的应用程序内提供的密钥。

我有问题

我的问题是:

  1. 我究竟做错了什么?
  2. 如何在Python中生成HOTP和/或TOTP?
  3. 有没有现成的Python库?

总结一下:请给我提供一些线索,这些线索将有助于我在Python代码中实现Google Authenticator身份验证。

I am trying to use one-time passwords that can be generated using Google Authenticator application.

What Google Authenticator does

Basically, Google Authenticator implements two types of passwords:

  • HOTP – HMAC-based One-Time Password, which means the password is changed with each call, in compliance to RFC4226, and
  • TOTP – Time-based One-Time Password, which changes for every 30-seconds period (as far as I know).

Google Authenticator is also available as Open Source here: code.google.com/p/google-authenticator

Current code

I was looking for existing solutions to generate HOTP and TOTP passwords, but did not find much. The code I have is the following snippet responsible for generating HOTP:

import hmac, base64, struct, hashlib, time

def get_token(secret, digest_mode=hashlib.sha1, intervals_no=None):
    if intervals_no == None:
        intervals_no = int(time.time()) // 30
    key = base64.b32decode(secret)
    msg = struct.pack(">Q", intervals_no)
    h = hmac.new(key, msg, digest_mode).digest()
    o = ord(h[19]) & 15
    h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
    return h

The problem I am facing is that the password I generate using the above code is not the same as generated using Google Authenticator app for Android. Even though I tried multiple intervals_no values (exactly first 10000, beginning with intervals_no = 0), with secret being equal to key provided within the GA app.

Questions I have

My questions are:

  1. What am I doing wrong?
  2. How can I generate HOTP and/or TOTP in Python?
  3. Are there any existing Python libraries for this?

To sum up: please give me any clues that will help me implement Google Authenticator authentication within my Python code.


回答 0

我想悬赏我的问题,但是我成功地找到了解决方案。我的问题似乎与不正确的secret键值有关(它必须是base64.b32decode()功能的正确参数)。

下面,我发布完整的工作解决方案,并说明如何使用它。

下面的代码就足够了。我也将它作为名为onetimepass的单独模块上传到GitHub (可在此处找到:https : //github.com/tadeck/onetimepass)。

import hmac, base64, struct, hashlib, time

def get_hotp_token(secret, intervals_no):
    key = base64.b32decode(secret, True)
    msg = struct.pack(">Q", intervals_no)
    h = hmac.new(key, msg, hashlib.sha1).digest()
    o = ord(h[19]) & 15
    h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
    return h

def get_totp_token(secret):
    return get_hotp_token(secret, intervals_no=int(time.time())//30)

它具有两个功能:

  • get_hotp_token() 生成一次性令牌(单次使用后应失效),
  • get_totp_token() 根据时间生成令牌(每30秒更改一次),

参量

关于参数:

  • secret 是服务器(上述脚本)和客户端(Google身份验证器,通过在应用程序中将其作为密码提供)已知的秘密值,
  • intervals_no 是每代令牌生成后增加的数字(可能应该在服务器上通过检查过去一次成功的整数之后检查一些有限数量的整数来解决)

如何使用它

  1. 生成secret(必须是的正确参数base64.b32decode())-最好为16个字符(无=符号),因为它肯定适用于脚本和Google身份验证器。
  2. 使用get_hotp_token(),如果你想在每次使用后无效一次性密码。在Google Authenticator中,我提到了这种基于计数器的密码。为了在服务器上检查它,您将需要检查多个值intervals_no(因为您没有隔离该用户由于某种原因未在请求之间生成传递的密码),但不能小于最后一个工作intervals_no值(因此您可能应该存储它)某处)。
  3. get_totp_token()如果您希望令牌以30秒的间隔工作,请使用。您必须确保两个系统都设置了正确的时间(这意味着它们在任何给定的时间点都生成相同的Unix时间戳)。
  4. 确保保护自己免受暴力攻击。如果使用基于时间的密码,则在不到30秒的时间内尝试输入1000000个值将使您有100%的机会猜测密码。在基于HMAC的密码(HOTP)的情况下,情况甚至更糟。

当将以下代码用于基于HMAC的一次性密码时:

secret = 'MZXW633PN5XW6MZX'
for i in xrange(1, 10):
    print i, get_hotp_token(secret, intervals_no=i)

您将得到以下结果:

1 448400
2 656122
3 457125
4 35022
5 401553
6 581333
7 16329
8 529359
9 171710

对应于Google Authenticator应用生成的令牌(除非少于6个符号,应用会在开头添加零,以达到6个字符的长度)。

I wanted to set a bounty on my question, but I have succeeded in creating solution. My problem seemed to be connected with incorrect value of secret key (it must be correct parameter for base64.b32decode() function).

Below I post full working solution with explanation on how to use it.

Code

The following code is enough. I have also uploaded it to GitHub as separate module called onetimepass (available here: https://github.com/tadeck/onetimepass).

import hmac, base64, struct, hashlib, time

def get_hotp_token(secret, intervals_no):
    key = base64.b32decode(secret, True)
    msg = struct.pack(">Q", intervals_no)
    h = hmac.new(key, msg, hashlib.sha1).digest()
    o = ord(h[19]) & 15
    h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
    return h

def get_totp_token(secret):
    return get_hotp_token(secret, intervals_no=int(time.time())//30)

It has two functions:

  • get_hotp_token() generates one-time token (that should invalidate after single use),
  • get_totp_token() generates token based on time (changed in 30-second intervals),

Parameters

When it comes to parameters:

  • secret is a secret value known to server (the above script) and client (Google Authenticator, by providing it as password within application),
  • intervals_no is the number incremeneted after each generation of the token (this should be probably resolved on the server by checking some finite number of integers after last successful one checked in the past)

How to use it

  1. Generate secret (it must be correct parameter for base64.b32decode()) – preferably 16-char (no = signs), as it surely worked for both script and Google Authenticator.
  2. Use get_hotp_token() if you want one-time passwords invalidated after each use. In Google Authenticator this type of passwords i mentioned as based on the counter. For checking it on the server you will need to check several values of intervals_no (as you have no quarantee that user did not generate the pass between the requests for some reason), but not less than the last working intervals_no value (thus you should probably store it somewhere).
  3. Use get_totp_token(), if you want a token working in 30-second intervals. You have to make sure both systems have correct time set (meaning that they both generate the same Unix timestamp in any given moment in time).
  4. Make sure to protect yourself from brute-force attack. If time-based password is used, then trying 1000000 values in less than 30 seconds gives 100% chance of guessing the password. In case of HMAC-based passowrds (HOTPs) it seems to be even worse.

Example

When using the following code for one-time HMAC-based password:

secret = 'MZXW633PN5XW6MZX'
for i in xrange(1, 10):
    print i, get_hotp_token(secret, intervals_no=i)

you will get the following result:

1 448400
2 656122
3 457125
4 35022
5 401553
6 581333
7 16329
8 529359
9 171710

which is corresponding to the tokens generated by the Google Authenticator app (except if shorter than 6 signs, app adds zeros to the beginning to reach a length of 6 chars).


回答 1

我想要一个Python脚本来生成TOTP密码。因此,我编写了python脚本。这是我的实现。我在Wikipedia上有此信息,并且有一些有关HOTP和TOTP的知识可以编写此脚本。

import hmac, base64, struct, hashlib, time, array

def Truncate(hmac_sha1):
    """
    Truncate represents the function that converts an HMAC-SHA-1
    value into an HOTP value as defined in Section 5.3.

    http://tools.ietf.org/html/rfc4226#section-5.3

    """
    offset = int(hmac_sha1[-1], 16)
    binary = int(hmac_sha1[(offset * 2):((offset * 2) + 8)], 16) & 0x7fffffff
    return str(binary)

def _long_to_byte_array(long_num):
    """
    helper function to convert a long number into a byte array
    """
    byte_array = array.array('B')
    for i in reversed(range(0, 8)):
        byte_array.insert(0, long_num & 0xff)
        long_num >>= 8
    return byte_array

def HOTP(K, C, digits=6):
    """
    HOTP accepts key K and counter C
    optional digits parameter can control the response length

    returns the OATH integer code with {digits} length
    """
    C_bytes = _long_to_byte_array(C)
    hmac_sha1 = hmac.new(key=K, msg=C_bytes, digestmod=hashlib.sha1).hexdigest()
    return Truncate(hmac_sha1)[-digits:]

def TOTP(K, digits=6, window=30):
    """
    TOTP is a time-based variant of HOTP.
    It accepts only key K, since the counter is derived from the current time
    optional digits parameter can control the response length
    optional window parameter controls the time window in seconds

    returns the OATH integer code with {digits} length
    """
    C = long(time.time() / window)
    return HOTP(K, C, digits=digits)

I wanted a python script to generate TOTP password. So, I wrote the python script. This is my implementation. I have this info on wikipedia and some knowledge about HOTP and TOTP to write this script.

import hmac, base64, struct, hashlib, time, array

def Truncate(hmac_sha1):
    """
    Truncate represents the function that converts an HMAC-SHA-1
    value into an HOTP value as defined in Section 5.3.

    http://tools.ietf.org/html/rfc4226#section-5.3

    """
    offset = int(hmac_sha1[-1], 16)
    binary = int(hmac_sha1[(offset * 2):((offset * 2) + 8)], 16) & 0x7fffffff
    return str(binary)

def _long_to_byte_array(long_num):
    """
    helper function to convert a long number into a byte array
    """
    byte_array = array.array('B')
    for i in reversed(range(0, 8)):
        byte_array.insert(0, long_num & 0xff)
        long_num >>= 8
    return byte_array

def HOTP(K, C, digits=6):
    """
    HOTP accepts key K and counter C
    optional digits parameter can control the response length

    returns the OATH integer code with {digits} length
    """
    C_bytes = _long_to_byte_array(C)
    hmac_sha1 = hmac.new(key=K, msg=C_bytes, digestmod=hashlib.sha1).hexdigest()
    return Truncate(hmac_sha1)[-digits:]

def TOTP(K, digits=6, window=30):
    """
    TOTP is a time-based variant of HOTP.
    It accepts only key K, since the counter is derived from the current time
    optional digits parameter can control the response length
    optional window parameter controls the time window in seconds

    returns the OATH integer code with {digits} length
    """
    C = long(time.time() / window)
    return HOTP(K, C, digits=digits)

如何使用Python登录网页并检索Cookie以供以后使用?

问题:如何使用Python登录网页并检索Cookie以供以后使用?

我想使用python下载和解析网页,但是要访问它,我需要设置一些cookie。因此,我需要先通过https登录到网页。登录时刻需要将两个POST参数(用户名,密码)发送到/login.php。在登录请求期间,我想从响应头中检索cookie并将其存储,以便可以在请求中使用它们来下载网页/data.php。

我将如何在python(最好是2.6)中做到这一点?如果可能,我只想使用内置模块。

I want to download and parse webpage using python, but to access it I need a couple of cookies set. Therefore I need to login over https to the webpage first. The login moment involves sending two POST params (username, password) to /login.php. During the login request I want to retrieve the cookies from the response header and store them so I can use them in the request to download the webpage /data.php.

How would I do this in python (preferably 2.6)? If possible I only want to use builtin modules.


回答 0

import urllib, urllib2, cookielib

username = 'myuser'
password = 'mypassword'

cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
login_data = urllib.urlencode({'username' : username, 'j_password' : password})
opener.open('http://www.example.com/login.php', login_data)
resp = opener.open('http://www.example.com/hiddenpage.php')
print resp.read()

resp.read()是您要打开的页面的纯HTML,您可以使用opener会话cookie查看任何页面。

import urllib, urllib2, cookielib

username = 'myuser'
password = 'mypassword'

cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
login_data = urllib.urlencode({'username' : username, 'j_password' : password})
opener.open('http://www.example.com/login.php', login_data)
resp = opener.open('http://www.example.com/hiddenpage.php')
print resp.read()

resp.read() is the straight html of the page you want to open, and you can use opener to view any page using your session cookie.


回答 1

这是使用优秀请求库的版本:

from requests import session

payload = {
    'action': 'login',
    'username': USERNAME,
    'password': PASSWORD
}

with session() as c:
    c.post('http://example.com/login.php', data=payload)
    response = c.get('http://example.com/protected_page.php')
    print(response.headers)
    print(response.text)

Here’s a version using the excellent requests library:

from requests import session

payload = {
    'action': 'login',
    'username': USERNAME,
    'password': PASSWORD
}

with session() as c:
    c.post('http://example.com/login.php', data=payload)
    response = c.get('http://example.com/protected_page.php')
    print(response.headers)
    print(response.text)

如何检查用户是否已登录(如何正确使用user.is_authenticated)?

问题:如何检查用户是否已登录(如何正确使用user.is_authenticated)?

我正在看这个网站,但似乎无法弄清楚该怎么做,因为它不起作用。我需要检查当前站点用户是否已登录(已认证),并且正在尝试:

request.user.is_authenticated

尽管确定用户已登录,但它仅返回:

>

我能够执行其他请求(来自上述网址的第一部分),例如:

request.user.is_active

这将返回成功的响应。

I am looking over this website but just can’t seem to figure out how to do this as it’s not working. I need to check if the current site user is logged in (authenticated), and am trying:

request.user.is_authenticated

despite being sure that the user is logged in, it returns just:

>

I’m able to do other requests (from the first section in the url above), such as:

request.user.is_active

which returns a successful response.


回答 0

Django 1.10+更新is_authenticated现在是Django 1.10中的属性。为了向后兼容,该方法仍然存在,但在Django 2.0中将被删除。

对于Django 1.9及更早版本

is_authenticated是一个功能。你应该这样称呼它

if request.user.is_authenticated():
    # do something if the user is authenticated

正如Peter Rowell所指出的那样,可能让您感到困扰的是,在默认的Django模板语言中,您无需附加括号即可调用函数。因此,您可能已经在模板代码中看到了以下内容:

{% if user.is_authenticated %}

但是,在Python代码中,它确实是User类中的方法。

Update for Django 1.10+:

is_authenticated is now an attribute in Django 1.10.

The method was removed in Django 2.0.

For Django 1.9 and older:

is_authenticated is a function. You should call it like

if request.user.is_authenticated():
    # do something if the user is authenticated

As Peter Rowell pointed out, what may be tripping you up is that in the default Django template language, you don’t tack on parenthesis to call functions. So you may have seen something like this in template code:

{% if user.is_authenticated %}

However, in Python code, it is indeed a method in the User class.


回答 1

Django 1.10+

使用属性,而不是方法:

if request.user.is_authenticated: # <-  no parentheses any more!
    # do something if the user is authenticated

Django 2.0中已弃用了同名的方法,并且Django文档中不再提及。


请注意,对于Django 1.10和1.11,该属性的值是a CallableBool而不是布尔值,这可能会导致一些奇怪的错误。例如,我有一个返回JSON的视图

return HttpResponse(json.dumps({
    "is_authenticated": request.user.is_authenticated()
}), content_type='application/json') 

在更新到属性request.user.is_authenticated后抛出异常TypeError: Object of type 'CallableBool' is not JSON serializable。解决方案是使用JsonResponse,它可以在序列化时正确处理CallableBool对象:

return JsonResponse({
    "is_authenticated": request.user.is_authenticated
})

Django 1.10+

Use an attribute, not a method:

if request.user.is_authenticated: # <-  no parentheses any more!
    # do something if the user is authenticated

The use of the method of the same name is deprecated in Django 2.0, and is no longer mentioned in the Django documentation.


Note that for Django 1.10 and 1.11, the value of the property is a CallableBool and not a boolean, which can cause some strange bugs. For example, I had a view that returned JSON
return HttpResponse(json.dumps({
    "is_authenticated": request.user.is_authenticated()
}), content_type='application/json') 

that after updated to the property request.user.is_authenticated was throwing the exception TypeError: Object of type 'CallableBool' is not JSON serializable. The solution was to use JsonResponse, which could handle the CallableBool object properly when serializing:

return JsonResponse({
    "is_authenticated": request.user.is_authenticated
})

回答 2

以下块应该工作:

    {% if user.is_authenticated %}
        <p>Welcome {{ user.username }} !!!</p>       
    {% endif %}

Following block should work:

    {% if user.is_authenticated %}
        <p>Welcome {{ user.username }} !!!</p>       
    {% endif %}

回答 3

您认为:

{% if user.is_authenticated %}
<p>{{ user }}</p>
{% endif %}

在控制器函数中添加装饰器:

from django.contrib.auth.decorators import login_required
@login_required
def privateFunction(request):

In your view:

{% if user.is_authenticated %}
<p>{{ user }}</p>
{% endif %}

In you controller functions add decorator:

from django.contrib.auth.decorators import login_required
@login_required
def privateFunction(request):

回答 4

如果要在模板中检查经过身份验证的用户,则:

{% if user.is_authenticated %}
    <p>Authenticated user</p>
{% else %}
    <!-- Do something which you want to do with unauthenticated user -->
{% endif %}

If you want to check for authenticated users in your template then:

{% if user.is_authenticated %}
    <p>Authenticated user</p>
{% else %}
    <!-- Do something which you want to do with unauthenticated user -->
{% endif %}

回答 5

对于Django 2.0+版本,请使用:

    if request.auth:
       # Only for authenticated users.

有关更多信息,请访问https://www.django-rest-framework.org/api-guide/requests/#auth

在Django 2.0及更高版本中,request.user.is_authenticated()已被删除。

For Django 2.0+ versions use:

    if request.auth:
       # Only for authenticated users.

For more info visit https://www.django-rest-framework.org/api-guide/requests/#auth

request.user.is_authenticated() has been removed in Django 2.0+ versions.