Python, HTTPS GET dengan otentikasi dasar

90

Saya mencoba melakukan HTTPS GET dengan otentikasi dasar menggunakan python. Saya sangat baru mengenal python dan panduan tampaknya menggunakan perpustakaan yang berbeda untuk melakukan sesuatu. (http.client, httplib, dan urllib). Adakah yang bisa menunjukkan kepada saya bagaimana ini dilakukan? Bagaimana Anda bisa memberitahu perpustakaan standar untuk digunakan?

Tom Squires
sumber
2
Apakah Anda ingin memastikan bahwa sertifikat tersebut valid?
Andrew Cox
1
Lihat stackoverflow.com/questions/635113/… . Tampaknya menutupi dengan tepat apa yang Anda cari.
Geo

Jawaban:

122

Dengan Python 3 berikut ini akan bekerja. Saya menggunakan http.client tingkat yang lebih rendah dari perpustakaan standar. Lihat juga bagian 2 dari rfc2617 untuk detail otorisasi dasar. Kode ini tidak akan memeriksa apakah sertifikat valid, tetapi akan menyiapkan koneksi https. Lihat dokumentasi http.client tentang cara melakukannya.

from http.client import HTTPSConnection
from base64 import b64encode
#This sets up the https connection
c = HTTPSConnection("www.google.com")
#we need to base 64 encode it 
#and then decode it to acsii as python 3 stores it as a byte string
userAndPass = b64encode(b"username:password").decode("ascii")
headers = { 'Authorization' : 'Basic %s' %  userAndPass }
#then connect
c.request('GET', '/', headers=headers)
#get the response back
res = c.getresponse()
# at this point you could check the status etc
# this gets the page text
data = res.read()  
Andrew Cox
sumber
5
The requestmetode dokumentasi [1] menyebutkan bahwa "String dikodekan sebagai "ISO-8859-1", charset default untuk HTTP". Jadi saya sarankan untuk memecahkan kode dengan "ISO-8859-1" daripada "ASCII". [1] docs.python.org/3/library/…
jgomo3
22
Untuk menggunakan variabel bukan b"username:password", gunakan: bytes(username + ':' + password, "utf-8").
kenorb
1
@ jgomo3: .decode("ascii")Hanya untuk bytes-> strkonversi. Hasilnya b64encodeadalah hanya ASCII.
Torsten Bronger
1
Penyelamat ku. Setelah 4 jam berjuang dan beban salah arah.
Conrad B
Bagaimana cara menggunakan kredensial default ?, ini tidak akan berfungsi jika saya menjalankan kode di sistem lain, bukan?
anandhu
93

Gunakan kekuatan Python dan bersandar pada salah satu perpustakaan terbaik di sekitar: permintaan

import requests

r = requests.get('https://my.website.com/rest/path', auth=('myusername', 'mybasicpass'))
print(r.text)

Variabel r (respon permintaan) memiliki lebih banyak parameter yang dapat Anda gunakan. Hal terbaik adalah masuk ke interpreter interaktif dan bermain-main dengannya, dan / atau membaca dokumen permintaan .

ubuntu@hostname:/home/ubuntu$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> r = requests.get('https://my.website.com/rest/path', auth=('myusername', 'mybasicpass'))
>>> dir(r)
['__attrs__', '__bool__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_content', '_content_consumed', 'apparent_encoding', 'close', 'connection', 'content', 'cookies', 'elapsed', 'encoding', 'headers', 'history', 'iter_content', 'iter_lines', 'json', 'links', 'ok', 'raise_for_status', 'raw', 'reason', 'request', 'status_code', 'text', 'url']
>>> r.content
b'{"battery_status":0,"margin_status":0,"timestamp_status":null,"req_status":0}'
>>> r.text
'{"battery_status":0,"margin_status":0,"timestamp_status":null,"req_status":0}'
>>> r.status_code
200
>>> r.headers
CaseInsensitiveDict({'x-powered-by': 'Express', 'content-length': '77', 'date': 'Fri, 20 May 2016 02:06:18 GMT', 'server': 'nginx/1.6.3', 'connection': 'keep-alive', 'content-type': 'application/json; charset=utf-8'})
IvanD
sumber
23

Pembaruan: OP menggunakan Python 3. Jadi menambahkan contoh menggunakan httplib2

import httplib2

h = httplib2.Http(".cache")

h.add_credentials('name', 'password') # Basic authentication

resp, content = h.request("https://host/path/to/resource", "POST", body="foobar")

Di bawah ini berfungsi untuk python 2.6:

Saya menggunakan pycurlbanyak hal dalam produksi untuk proses yang menghasilkan lebih dari 10 juta permintaan per hari.

Anda harus mengimpor yang berikut ini terlebih dahulu.

import pycurl
import cStringIO
import base64

Bagian dari header otentikasi dasar terdiri dari nama pengguna dan kata sandi yang dikodekan sebagai Base64.

headers = { 'Authorization' : 'Basic %s' % base64.b64encode("username:password") }

Di header HTTP Anda akan melihat baris ini Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=. Perubahan string yang disandikan tergantung pada nama pengguna dan kata sandi Anda.

Kami sekarang membutuhkan tempat untuk menulis tanggapan HTTP kami dan pegangan koneksi curl.

response = cStringIO.StringIO()
conn = pycurl.Curl()

Kami dapat mengatur berbagai opsi curl. Untuk daftar lengkap opsi, lihat ini . Dokumentasi terkait adalah untuk libcurl API, tetapi opsinya tidak berubah untuk binding bahasa lain.

conn.setopt(pycurl.VERBOSE, 1)
conn.setopt(pycurlHTTPHEADER, ["%s: %s" % t for t in headers.items()])

conn.setopt(pycurl.URL, "https://host/path/to/resource")
conn.setopt(pycurl.POST, 1)

Jika Anda tidak perlu memverifikasi sertifikat. Peringatan: Ini tidak aman. Mirip dengan lari curl -katau curl --insecure.

conn.setopt(pycurl.SSL_VERIFYPEER, False)
conn.setopt(pycurl.SSL_VERIFYHOST, False)

Panggilan cStringIO.writeuntuk menyimpan respons HTTP.

conn.setopt(pycurl.WRITEFUNCTION, response.write)

Saat Anda membuat permintaan POST.

post_body = "foobar"
conn.setopt(pycurl.POSTFIELDS, post_body)

Buat permintaan sebenarnya sekarang.

conn.perform()

Lakukan sesuatu berdasarkan kode respons HTTP.

http_code = conn.getinfo(pycurl.HTTP_CODE)
if http_code is 200:
   print response.getvalue()
Ocaj Nires
sumber
Itu tampaknya untuk pyhthon 2.5 im menggunakan 3
Tom Squires
Apakah Anda menggunakan instalasi mudah atau pip? Apakah paket pycurl tidak tersedia untuk python 3?
Ocaj Nires
Diperbarui dengan sebuah httplib2. Ini tersedia untuk python 3.
Ocaj Nires
Bagi mereka yang baru: contoh di atas tidak memiliki titik: "pycurl.HTTPHEADER" (Saya akan mengedit tetapi itu 1 karakter dan minimum 6).
Graeme Wicksted
OP mengatakan GET, bukan POST
Joe C
17

Berikut cara yang benar untuk melakukan autentikasi dasarurllib.request dengan Python3 dengan validasi sertifikat.

Perhatikan bahwa certifiitu tidak wajib. Anda dapat menggunakan bundel OS Anda (kemungkinan hanya * nix) atau mendistribusikan sendiri Bundel CA Mozilla . Atau jika host yang Anda ajak berkomunikasi hanya sedikit, gabungkan file CA Anda dari CA host, yang dapat mengurangi risiko serangan MitM yang disebabkan oleh CA lain yang korup.

#!/usr/bin/env python3


import urllib.request
import ssl

import certifi


context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations(certifi.where())
httpsHandler = urllib.request.HTTPSHandler(context = context)

manager = urllib.request.HTTPPasswordMgrWithDefaultRealm()
manager.add_password(None, 'https://domain.com/', 'username', 'password')
authHandler = urllib.request.HTTPBasicAuthHandler(manager)

opener = urllib.request.build_opener(httpsHandler, authHandler)

# Used globally for all urllib.request requests.
# If it doesn't fit your design, use opener directly.
urllib.request.install_opener(opener)

response = urllib.request.urlopen('https://domain.com/some/path')
print(response.read())
saaj
sumber
Ini bagus. Verifikasi sertifikat penting saat mengirim kredensial teks biasa (HTTP Basic Auth). Anda perlu memastikan lapisan TLS (HTTPS) Anda aman karena Anda mengandalkan lapisan tersebut untuk mengamankan.
empat43
Tampak benar, tetapi tidak berhasil dalam kasus saya, ini memunculkan kesalahan seperti ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] verifikasi sertifikat gagal: tidak bisa mendapatkan sertifikat penerbit lokal (_ssl.c: 1056)
neelmeg
Saya mengetahuinya dengan memberikan sertifikat pem yang valid ke parameter verifikasi dan cookie.
neelmeg
1

hanya menggunakan modul standar dan tidak ada pengkodean header manual

... yang tampaknya merupakan cara yang paling diinginkan dan paling portabel

konsep python urllib adalah mengelompokkan berbagai atribut permintaan ke dalam berbagai manajer / direktur / konteks ... yang kemudian memproses bagian-bagiannya:

import urllib.request, ssl

# to avoid verifying ssl certificates
httpsHa = urllib.request.HTTPSHandler(context= ssl._create_unverified_context())

# setting up realm+urls+user-password auth
# (top_level_url may be sequence, also the complete url, realm None is default)
top_level_url = 'https://ip:port_or_domain'
# of the std managers, this can send user+passwd in one go,
# not after HTTP req->401 sequence
password_mgr = urllib.request.HTTPPasswordMgrWithPriorAuth()
password_mgr.add_password(None, top_level_url, "user", "password", is_authenticated=True)

handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
# create OpenerDirector
opener = urllib.request.build_opener(handler, httpsHa)

url = top_level_url + '/some_url?some_query...'
response = opener.open(url)

print(response.read())
alexey
sumber
0

Berdasarkan jawaban @AndrewCox dengan beberapa perbaikan kecil:

from http.client import HTTPSConnection
from base64 import b64encode


client = HTTPSConnection("www.google.com")
user = "user_name"
password = "password"
headers = {
    "Authorization": "Basic {}".format(
        b64encode(bytes(f"{user}:{password}", "utf-8")).decode("ascii")
    )
}
client.request('GET', '/', headers=headers)
res = client.getresponse()
data = res.read()

Catatan, Anda harus menyetel encoding jika Anda menggunakan bytesfungsi, bukan b"".

I159
sumber
-1
requests.get(url, auth=requests.auth.HTTPBasicAuth(username=token, password=''))

Jika dengan token, kata sandi harus ''.

Ini bekerja untuk saya.

yidong li
sumber