Python, HTTPS GET ile temel kimlik doğrulama


90

Python kullanarak temel kimlik doğrulama ile bir HTTPS GET yapmaya çalışıyorum. Python konusunda çok yeniyim ve kılavuzlar bir şeyler yapmak için farklı kitaplıkları kullanıyor gibi görünüyor. (http.client, httplib ve urllib). Biri bana nasıl yapıldığını gösterebilir mi? Standart kitaplığa kullanmasını nasıl söyleyebilirsiniz?


2
Sertifikanın geçerli olduğundan emin olmak istiyor musunuz?
Andrew Cox

1
Check stackoverflow.com/questions/635113/... . Tam olarak aradığınız şeyi kapsıyor gibi görünüyor.
Geo

Yanıtlar:


122

Python 3'te aşağıdakiler çalışacaktır. Standart kitaplıktan daha düşük düzeyli http.client kullanıyorum . Ayrıca temel yetkilendirme ayrıntıları için rfc2617'nin 2. bölümüne bakın . Bu kod, sertifikanın geçerli olup olmadığını kontrol etmez, ancak bir https bağlantısı kurar. Bunun nasıl yapılacağını öğrenmek için http.client belgelerine bakın .

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()  

5
requestYöntem belgeleri [1] ISO-8859-1 "HTTP için varsayılan karakter kümesi '' Yaylı olarak kodlanmış" bahseder. Bu yüzden "ASCII" yerine "ISO-8859-1" ile kod çözmeyi öneririm. [1] docs.python.org/3/library/…
jgomo3

22
Yerine değişkenler kullanmak için b"username:password", kullanın: bytes(username + ':' + password, "utf-8").
kenorb

1
@ jgomo3: .decode("ascii")Yalnızca bytes-> strdönüşüm içindir. Sonuç, b64encodeyalnızca ASCII'dir.
Torsten Bronger

1
Kurtarıcım. 4 saatlik mücadeleden ve bir sürü yanlış yönden sonra.
Conrad B

Varsayılan kimlik bilgilerini nasıl kullanırım? Kodu başka bir sistemde doğru çalıştırırsam bu çalışmaz?
anandhu

93

Python'un gücünü kullanın ve etraftaki en iyi kitaplıklardan birine yaslanın: istekler

import requests

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

Değişken r (istek yanıtı), kullanabileceğiniz çok daha fazla parametreye sahiptir. En iyisi, etkileşimli tercümana girip onunla oynamak ve / veya istek belgelerini okumaktır .

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'})

23

Güncelleme: OP Python 3 kullanıyor. Yani httplib2 kullanarak bir örnek eklemek

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")

Aşağıdakiler python 2.6 için çalışır:

pycurlGünde 10 milyondan fazla talep yapan bir süreç için üretimde çok kullanıyorum .

Önce aşağıdakileri içe aktarmanız gerekecek.

import pycurl
import cStringIO
import base64

Temel kimlik doğrulama başlığının bir kısmı, Base64 olarak kodlanmış kullanıcı adı ve paroladan oluşur.

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

HTTP başlığında bu satırı göreceksiniz Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=. Kodlanmış dize, kullanıcı adınıza ve şifrenize bağlı olarak değişir.

Artık HTTP yanıtımızı yazacağımız bir yere ve bir curl bağlantı tutamacına ihtiyacımız var.

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

Çeşitli kıvrılma seçenekleri ayarlayabiliriz. Seçeneklerin tam listesi için buna bakın . Bağlantılı dokümantasyon libcurl API içindir, ancak seçenekler diğer dil bağlamaları için değişmez.

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)

Sertifikayı doğrulamanıza gerek yoksa. Uyarı: Bu güvensizdir. Koşuya benzer curl -kveya curl --insecure.

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

cStringIO.writeHTTP yanıtını depolamak için çağrı .

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

POST isteğinde bulunduğunuzda.

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

Şimdi asıl istekte bulunun.

conn.perform()

HTTP yanıt koduna dayalı bir şey yapın.

http_code = conn.getinfo(pycurl.HTTP_CODE)
if http_code is 200:
   print response.getvalue()

Yani 3 kullanılarak pyhthon 2.5 im gibi görünüyor
Tom Squires

Kolay kurulum veya pip mi kullanıyorsunuz? Pycurl paketi python 3 için kullanılamıyor mu?
Ocaj Nires

Bir httplib2 ile güncellendi. Bu, python 3 için kullanılabilir.
Ocaj Nires

Yeni olanlar için: Yukarıdaki örnekte bir nokta eksik: "pycurl.HTTPHEADER" (ben düzenlerdim, ancak 1 karakter ve minimum 6'dır).
Graeme Wicksted

OP, POST değil, GET dedi
Joe C

17

Sertifika doğrulamasıyla Python3'te temel kimlik doğrulaması yapmanın doğru bir yolu urllib.requestaşağıdadır.

Bunun certifizorunlu olmadığını unutmayın . İşletim sistemi paketinizi (muhtemelen yalnızca * nix) kullanabilir veya Mozilla'nın CA Paketini kendiniz dağıtabilirsiniz . Veya iletişim kurduğunuz ana bilgisayarlar yalnızca birkaçsa , CA dosyasını ana bilgisayarların CA'larından kendiniz birleştirin, bu da başka bir bozuk CA'nın neden olduğu MitM saldırısı riskini azaltabilir .

#!/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())

Bu harika. Düz metin kimlik bilgileri (HTTP Temel Yetkilendirme) gönderirken sertifika doğrulaması önemlidir. TLS katmanınızın (HTTPS) güvenli olduğundan emin olmanız gerekir, çünkü o katmanın güvenli olması için o katmana güveniyorsunuz.
dört43

Doğru görünüyor, ancak benim durumumda işe yaramadı, ssl.SSLCertVerificationError gibi bir hata veriyor: [SSL: CERTIFICATE_VERIFY_FAILED] sertifika doğrulaması başarısız oldu: yerel yayıncı sertifikası alınamıyor (_ssl.c: 1056)
neelmeg

Doğrulama parametresi ve tanımlama bilgileri parametresine geçerli bir pem sertifikası geçirerek bunu anladım.
neelmeg

1

sadece standart modüller kullanır ve manüel başlık kodlaması gerekmez

... amaçlanan ve en taşınabilir yol gibi görünen

python urllib kavramı, isteğin çeşitli özelliklerini çeşitli yöneticiler / yöneticiler / bağlamlar halinde gruplandırmaktır ... daha sonra parçalarını işler:

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())

0

@AndrewCox'un bazı küçük iyileştirmelerle yanıtına dayanarak:

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()

bytesBunun yerine işlev kullanıyorsanız kodlamayı ayarlamanız gerektiğini unutmayın b"".


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

Belirteç varsa, şifre olmalıdır ''.

Benim için çalışıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.