JSON Web令牌是一种开放标准,用于基于JSON格式创建访问令牌。通常用于在客户端服务器应用程序中传递身份验证数据。维基百科
在浏览器中存储敏感数据时,您只需要使用两个可用选项之一:cookie或localStorage。在这里,每个人都选择品尝。但是,我将本文专门介绍了Secret Service,这是一种在D-Bus上运行的服务,旨在在Linux中存储“秘密”。
该服务具有GNOME Keyring用于存储应用程序密钥的API。
为什么要特勤局
问题是,我没有在浏览器中收到令牌。我正在为类似于git中使用的控制台应用程序编写客户端身份验证。
由于我不想在下次启动应用程序时强制用户登录,因此立即出现了如何存储详细信息的问题。
最初,有一个选项可以将令牌存储在加密文件中,但是它立即消失了,因为我猜想我的加密和解密功能就像一辆自行车。
然后,我考虑了Linux如何保密,结果证明在其他操作系统中也实现了类似的机制。
结果,Linux用户帐户的密码将用作令牌的访问密钥。
特勤局架构一目了然
秘密服务的主要数据结构是具有属性和秘密的元素的集合。
采集
这是各种身份验证数据的集合。系统使用别名“ default”下的默认集合。所有用户应用程序都写入其中。海马会向我们展示。
如您所见,Google Chrome和VSCode已保存在我的存储中。我的应用程序也将保存在这里。
每个这样的记录称为一个项目。
元件
集合中存储属性和机密的部分。
属性
一对类型键,值,其中包含应用程序的名称并用于标识元素。
秘密
, , , .
Secret Service
, flow chart.
« ?» — .
« » — .
« » — .
« API» — .
« » — .
« » — .
Python
Click Framework CLI .
import click
, , . . , .
@click.group()
def cli():
pass
.
:
$ app login
Email:
Password:
:
$ app login
Logged in!
login
@cli.command(help="Login into your account.")
@click.option(
'--email',
prompt=True,
help='Registered email address.')
@click.option(
'--password',
prompt=True,
hide_input=True,
help='Password provided at registration.'
)
def login(email, password):
pass
if __name__ == '__main__':
cli()
login
, , email password.
@cli.command
, @click.option
.
, hide_input
.
prompt
, lick Framework , .
True
False
, :
True
Click Framework . . , WEB API Secret Service, Secret Service API;False
Click Framework . , , Secret Service.
, prompt_desicion
. Secret Service. , Secret Service API .
. , , Click Framework.
, , Click Framework, .
app Click Framework. auth, Auth .
.
├── auth.py
└── app.py
prompt_desicion
auth
Auth
auth
.
@cli.command(help="Login into your account.")
@click.option(
'--email',
prompt=auth.prompt_desicion,
help='Registered email address.')
@click.option(
'--password',
prompt=auth.prompt_desicion,
hide_input=True,
help='Password provided at registration.'
)
def login(email, password):
pass
if __name__ == '__main__':
cli()
Python SecreteStorage, Secret Service API.
, Secret Service.
Secret Service API WEB API, prompt_desicion
.
requests — HTTP WEB API.
secretstorage — Secret Service API.
json — .
import requests
import secretstorage
import json
Secrete Storage
class Auth:
def __init__(self, email=None, password=None):
# ,
# Secret Service
self._attributes = {'application': 'MyApp'}
# Dbus
self._connection = secretstorage.dbus_init()
# -
self._collection = secretstorage.collection.get_default_collection(
self._connection
)
#
self._items = self._collection.search_items(self._attributes)
#
self._stored_secret = self.get_stored_secret()
.
Secret Service self._attributes
.
«_»
, . , . , . , , . , .
, . () SecretStorage () . , , self._items
.
get_stored_secret
, .
class Auth:
def get_stored_secret(self):
for item in self._items:
if item:
return json.loads(item.get_secret())
Item
secretstorage
, get_secret
, .
. .
.
True False — ,
, , : « — False».
class Auth:
def __init__(self, email=None, password=None):
# ,
self.prompt_desicion = False
. , .
class Auth:
def __init__(self, email=None, password=None):
# ,
#
if self._stored_secret:
# token
self.token = self._stored_secret['token']
#
elif email and password:
# WEB API
self.token = self.get_token(email, password)
#
self._valid_secret = {'token': self.token}
# Secret Service
self.set_stored_secret()
else:
# Secret Storage,
#
self.prompt_desicion = True
- .
Secret Storage API.
, .
Secret Storage.
Secret Storage API.
, Secret Storage.
, .
, WEB API.
, Secret Storage.
Secret Storage WEB API.
WEB API
class Auth:
def get_token(self, email: str, password: str) -> str:
try:
response = requests.post(
API_URL,
data= {
'email': email,
'passwd': password
})
data = response.json()
except requests.exceptions.ConnectionError:
raise requests.exceptions.ConnectionError()
if response.status_code != 200:
raise requests.exceptions.HTTPError(data['msg'])
return data['data']['token']
API_URL
API. , . , POST «email» «passwd».
API , API .
API «msg» . try .
«data».
Secret Storage
class Auth:
def set_stored_secret(self):
self._collection.create_item(
'MyApp',
self._attributes,
bytes((json.dumps(self._valid_secret)), 'utf-8')
)
create_item
, .
lick Framework
auth
.
from auth import Auth
Secret Storage.
auth = Auth()
.
@cli.command(help="Login into VPN Manager account.")
@click.option(
'--email',
prompt=auth.prompt_desicion,
help='Registered email address.')
@click.option(
'--password',
prompt=auth.prompt_desicion,
hide_input=True,
help='Password provided at registration.'
)
login.
def login(email, password):
global auth
try:
#
if auth.prompt_desicion:
# Secret Storage
auth = Auth(email, password)
except Exception:
return click.echo('No API connection')
# , .
click.echo(auth.token)
单击框架自动生成帮助。为此,他需要我在help
装饰器的参数中指定的行。
$ python app.py
Usage: app.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
login Login into your account.
登录命令帮助
$ python app.py login --help
Usage: app.py login [OPTIONS]
Login into your account
Options:
--email TEXT Registered email address
--password TEXT Password provided at registration
--help Show this message and exit.
检查一下
使用命令启动应用程序后,python app.py login
它将要求输入邮件和密码。如果此数据正确,则相应的元素将出现在“秘密服务”中。
它实际上存储令牌。
当您重新启动应用程序时,它不会询问详细信息,而是会从Secret Service下载令牌。