2021.06.01   2021.07.12

【Python】requestsのsessionとcookiesでPOST通信を検証

Python    


requestsでセッションを維持する仕組みがわからず、困ったので検証を行いました。

特にsessionとcookiesの使い方が分からなかったため、その検証を行なっています。

検証方法はDjangoの管理者画面にログイン(POST)し、ログイン状態(セッション)を保持しつつ管理者ページ内の別ページに遷移できるかを確認します。

前提知識

requestsとは

requestsとは、PythonでHTTP通信を行うことができるライブラリのことです。

このライブラリを使用するとWebブラウザを使用せず、プログラム上から直接Webページの情報を取得することができます。

マニュアル: Requests: HTTP for Humans™

インストールコマンド
pip install requests


sessionとcookieの役割

sessionとcookieの役割について簡単に解説します。

sessionとcookieは、それぞれWebクライアントとWebサーバーが通信を行う際に、使用します。

クライアントがサーバーにログインすると、サーバーはsession_idを発行します。

session_idはクライアントとサーバーの両方に保存され、通信を行なっている間はこのsession_idの送信、受信を繰り返し、通信(セッション)を保持します。

そのおかげで、クライアントがサーバーにログインした状態が維持されます。

cookieというのは、クライアント側でsession_idを保存しておくファイル(もしくデータ領域)のことです。

クライアントがサーバーと通信を行う際は、cookieに記載されている情報(session_idなど)をサーバーに送信します。

参考: Cookieとセッションをちゃんと理解する

検証方法

Django管理者画面のログインページでログイン(POST)を行い、セッションを維持しつつ管理者ページ内の別ページに遷移できるかを確認します。

手順概要
  1. ログインページであるhttp://127.0.0.1/admin/login/へアクセス
  2. ログインページで認証を行い、 認証完了後のページhttp://127.0.0.1/admin/へアクセスする。
  3. 認証が完了している状態でhttp://127.0.0.1/admin/auth/group/へアクセスする。セッションが切れているとこのページには遷移できない。


1. ログインページ




今回の検証対象となるログインページです。

タブ名がLog in | Django site admin
URLがhttp://127.0.0.1/admin/login/であることを確認します。

後述するプログラムでは、inputタグのname属性をキーとしてそれぞれ対応する認証情報をrequestsに渡します。

そうすることで、次の認証完了後のページに遷移することができます。

記述例
login_data = {
   'username': 'admin',
   'password': 'hogehoge'
   'csrfmiddlewaretoken': 'xxxxxxxxxxxxxxxx' 
}

csrfmiddlewaretokenについては、ログインページアクセス時にランダムに生成されるCSRFトークンを渡す必要があります。

上記画像の"value"以降の値をPOSTする際にrequestsへ渡さないと、認証時にエラーとなります。

プログラム上ではBeautifulSoupを利用することでこの値を取得します。

2. 認証完了後のページ

認証が成功するとこの画面に遷移します。

タブ名にSite administrationが含まれており、
URLがhttp://127.0.0.1/admin/であることを確認します。

緑枠のGroupsをクリックすることで、認証完了後に遷移できるページへ移動できます。

なお、セッションが保持されていない場合は、次のページへは遷移せず、先ほどのログインページへ戻ります。

3. 認証完了後に遷移できるページ

セッションが保持されていればこの画面に遷移します。

タブ名にSelect group to changeが含まれており、
URLがhttp://127.0.0.1/admin/auth/group/であることを確認します。

検証環境

上記の環境はDocker-Composeで作成しています。

実際に検証を行なっていみたい方は、ソースコードをGitにアップロードしているのでご利用ください。

git: https://github.com/ruruyuki/django_container_rest

コンテナの使い方

前提

・DockerとDocker Composeがインストール済みであること。
・コマンドラインで作業すること。

作業手順

・上記URL(Git)からダウンロードしたソースを解凍する。
・解凍したディレクトリに「--master 1」などの記述があれば削除し、
 名前がdjango_container_restとなるようにする。
・django_container_restディレクトリに、コマンドラインで移動する。
・移動したディレクトリ内で以下コマンドを実行する。

docker-compose up -d --build

・Webブラウザで「http://127.0.0.1:80/admin」にアクセスする。
 すると、Djangoの管理画面にアクセスできます。

ユーザー名は「admin」
パスワードは「hogehoge」

でログインすることができます。

検証結果

以下のようにコードを記述すると、先述した条件で通信を行うことができます。

結論としては、cookiesを使用してもセッションを保持できなかったため、sessionクラスを使用してコードを記述しています。

import requests
from bs4 import BeautifulSoup
import re

# 1.ログインページにアクセスする
url_login = "http://127.0.0.1/admin/login/"
session = requests.session()
# ログインページへのアクセス完了
req_before_login = session.get(url_login)

# ログインするための情報を準備する
login_data = {
   'username': 'admin',
   'password': 'hogehoge'
}

# ログインするためにcsrfトークンが必要となるため情報を取得
bs = BeautifulSoup(req_before_login.text, 'html.parser')
csrf_token = bs.find(
   attrs={'name':'csrfmiddlewaretoken'}).get('value')
login_data['csrfmiddlewaretoken'] = csrf_token

# 2. ログインページで認証を行い、管理者ページへ遷移する
req_after_login = session.post(url_login, data=login_data)

# 3. 認証完了後のページで他ページへ遷移を行う
url_group = 'http://127.0.0.1/admin/auth/group/'
req_group = session.get(url_group)

print('--- ログイン情報 ---')
print(login_data)
print('---- 認証ページへのアクセス結果 ---')
print(re.search(r'<title.*', req_before_login.text).group(0))
print(req_before_login.status_code)
print('--- 認証完了ページへのアクセス結果 ---')
print(re.search(r'<title.*', req_after_login.text).group(0))
print(req_after_login.status_code)
print('--- 認証完了ページからgroupページへのアクセス結果 ---')
print(re.search(r'<title.*', req_group.text).group(0))
print(req_group.status_code)
実行結果
--- ログイン情報 --- 
{'username': 'admin', 'password': 'hogehoge', 'csrfmiddlewaretoken': 'Hd4zjEI0G3n0JaJB6GrV6vQFMlQoxtIFEkQbYkJNo5ktR6wlvYf7xdDvrDIZW7Q8'}
--- 認証ページへのアクセス結果 ---
<title>Log in | Django site admin</title>
200
--- 認証完了ページへのアクセス結果 ---
<title>Site administration | Django site admin</title\>
200
--- 認証完了ページからgroupページへのアクセス結果 ---
<title>Select group to change | Django site admin</title\>
200

実行結果の<tiltle\>で囲まれた部分が各ページのタブ部分の情報です。

確認すると、Log in -> Site administration -> Select group to change の順番で表示されているため、ログインとその後のページ遷移が成功していることがわかります。

ちなみにログインに失敗すると、ページ遷移が行えず Log in -> Log in -> Log in と表示されます。

解説

sessionとcookiesについて

requestsには次のようにコードを書くと、レスポンスヘッダのcookieを取得してくれる機能があります。

response = requests.get(url)
cookie = response.cookies

しかし、本コードではそれを使用していません。

なぜなら、セッションを維持するのに使用するcookieはログインと同時にリクエストヘッダに付与されるからです。

requestsのcookieは、レスポンスヘッダのcookieしか取得できないので、セッション維持には使えません。

では、どうすれば良いかというと、requestsのsessionクラスを使えばこの問題を解決できます。

sessionインスタンスを使用した通信についてはすべて、リクエストヘッダを含むcookieの状態が保持されます。

マニュアル: セッションオブジェクト

sessionは以下のように記述することで、セッション情報を保持しながらHTTP通信を行うことができます。

import requests
session = requests.session()
session.get(url_1) 
session.post(url_2)
session.get(url_3)
・・・


sessionインスタンスが保持しているcookieを確認する

下記の例のように、session.cookiesと記述することでsessionインスタンスが保持しているcookieの内容を確認することができます。

セッションが保持されている時は、cookieの内容がどの通信でも同じ内容となります。

# sessionメソッドを使用して通信する
session = requests.session()
session_info_1 = session.cookies
result_1 = session.get(url_1)

session_info_2 = session.cookies
result_2 = session.post(url_2, data=data)

session_info_3 = session.cookies
result_3 = session.get(url_3)

# 結果確認
print(session_info_1)
print(result_1.status_code)

print(session_info_2)
print(result_2.status_code)

print(session_info_2)
print(result_2.status_code)
# 実行結果
# session_info_1
<RequestsCookieJar[<Cookie csrftoken=kjvY43iR7Da7zyztYg6EyfvLzxriLAIsOCNMCNleEokZknFNuVdzCM7NFnQxptdE for 127.0.0.1/>, <Cookie sessionid=l17ftt2yovijmtc3ch4vz6eey48tocpb for 127.0.0.1/>]>
# result_1.status_code
200
# session_info_2
<RequestsCookieJar[<Cookie csrftoken=kjvY43iR7Da7zyztYg6EyfvLzxriLAIsOCNMCNleEokZknFNuVdzCM7NFnQxptdE for 127.0.0.1/>, <Cookie sessionid=l17ftt2yovijmtc3ch4vz6eey48tocpb for 127.0.0.1/>]>
# result_2.status_code
200
# session_info_3
<RequestsCookieJar[<Cookie csrftoken=kjvY43iR7Da7zyztYg6EyfvLzxriLAIsOCNMCNleEokZknFNuVdzCM7NFnQxptdE for 127.0.0.1/>, <Cookie sessionid=l17ftt2yovijmtc3ch4vz6eey48tocpb for 127.0.0.1/>]>
# result_3.status_code
200

cookiesを使用したコード例

こちらはsessionクラスを使わず、cookiesを利用して通信を行なったコードです。

先ほどお話した通り、このコードではログイン後のページ遷移が上手く行えません。

import requests
from bs4 import BeautifulSoup
import re

url_login = 'http://127.0.0.1/admin/login/'
req_before_login = requests.get(url_login)
cookie_before_login = req_before_login.cookies

login_data = {
   'username': 'admin',
   'password': 'hogehoge'
}

bs = BeautifulSoup(req_before_login.text, 'html.parser')
csrf_token = bs.find(attrs={'name':'csrfmiddlewaretoken'}).get('value')
login_data['csrfmiddlewaretoken'] = csrf_token

req_after_login = requests.post(url_login,
                                data=login_data,
                                cookies=cookie_before_login)

cookie_after_login = req_after_login.cookies

url_group = 'http://127.0.0.1/admin/auth/group/'
req_group = requests.get(url_group,
                         cookies=cookie_after_login)

cookie_group = req_group.cookies


print('--- ログイン情報 ---')
print(login_data)
print('--- 認証ページへのアクセス結果 ---')
print(re.search(r'<title.*', req_before_login.text).group(0))
print(req_before_login.status_code)
print(cookie_before_login)
print('--- login結果 ---')
print(re.search(r'<title.*', req_after_login.text).group(0))
print(req_after_login.status_code)
print(cookie_after_login)
print('--- group画面への遷移結果 ---')
print(re.search(r'<title.*', req_group.text).group(0))
print(req_group.status_code)
print(cookie_group)
実行結果
--- ログイン情報 ---
{'username': 'admin', 'password': 'hogehoge', 'csrfmiddlewaretoken': 'DJPXPmF54TAr9r27mBJFD5JtoOM8TSfgzkSv5G9X5AlQ7vYslWVOBJ2VB0ZNu6B6'}

--- ログインページへのアクセス結果 ---
<title>Log in | Django site admin</title>
200
<RequestsCookieJar[<Cookie csrftoken=Qr37AviZm64zpc2dTfkEW61jp3epoSSHM26FQPMRnNPYngYySAwNUKkLCfr4Z6ex for 127.0.0.1/>]>

--- login結果 ---
<title>Site administration | Django site admin</title> 
200
<RequestsCookieJar[]>

--- group画面への遷移結果 ---
<title>Log in | Django site admin</title> 
200
<RequestsCookieJar[<Cookie csrftoken=3q7szjHOk9eFkysm17ShWFrwcJd3qQge7Q41pA0eVwMz1tKJ6tnill0YlFL6lyyM for 127.0.0.1/>]>



結果は Log in -> Site administration -> Log in となっています。

この結果は、ログインには成功しているものの、そのセッションは保持されていないため、管理画面内の別ページに遷移しようとするとログインページに戻されてしまっていることを示しています。

以下のようにPOST実行時のcookieをreq_after_login.cookiesで取得しようとしていますが、実行結果の通り、cookiesの値は空となります。

req_after_login = requests.post(url_login,
                                data=login_data,
                                cookies=cookie_before_login)

cookie_after_login = req_after_login.cookies
--- login結果 ---
<title>Site administration | Django site admin</title>
200
<RequestsCookieJar[]>

認証処理が成功するとセッションIDが発行されcookieに保存されますが、このcookieは先述した通りレスポンスヘッダではなく、リクエストヘッダに保持されるため、requestsのcookiesで取得できません。

ゆえにセッションが保持できず、group画面に遷移できず、ログインページに戻っています。

以上からrequestsでセッションを維持したい場合はsessionメソッドを使いましょうという結論になります。

コメント
@Patrice Krueger
2023年11月23日0:51
Hi,

rurukblog.com is only listed in a 8/10,000+ Directories

We have a black friday deal going on at the moment to get your website listed in all 10k+ for $19.95

Visit us on DirectoryBump.com
コメントする
コメント入力

名前 (※ 必須)

メールアドレス (※ 必須 画面には表示されません)

送信