fly-common 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 fly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,197 @@
1
+ Metadata-Version: 2.4
2
+ Name: fly-common
3
+ Version: 0.1.0
4
+ Summary: 通用模块
5
+ Author-email: fly <fzkf117@163.com>
6
+ License: MIT
7
+ Keywords: utils,tools
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.6
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: annotated-types==0.7.0
25
+ Requires-Dist: cffi==2.0.0
26
+ Requires-Dist: cryptography==46.0.5
27
+ Requires-Dist: ecdsa==0.19.1
28
+ Requires-Dist: passlib==1.7.4
29
+ Requires-Dist: pyasn1==0.6.3
30
+ Requires-Dist: pycparser==2.23
31
+ Requires-Dist: pydantic==2.12.5
32
+ Requires-Dist: pydantic-core==2.41.5
33
+ Requires-Dist: python-jose==3.5.0
34
+ Requires-Dist: rsa==4.9.1
35
+ Requires-Dist: six==1.17.0
36
+ Requires-Dist: typing-extensions==4.15.0
37
+ Requires-Dist: typing-inspection==0.4.2
38
+ Dynamic: license-file
39
+
40
+ # fly-common
41
+
42
+ > 一个轻量级的 Python 公共工具库,专注于安全认证和常用工具函数
43
+
44
+ ## 📖 介绍
45
+
46
+ fly-common 是一个功能精简但实用的 Python 公共库,主要提供以下核心功能:
47
+
48
+ - **JWT Token 管理**:支持对称加密(HS256)和非对称加密(RS256)的 JWT Token 生成、验证和刷新
49
+ - **密码加密**:提供 MD5、PBKDF2 等多种加密方式
50
+ - **工具函数**:提供单例模式、随机字符串生成等实用工具
51
+
52
+ ## ✨ 功能特性
53
+
54
+ ### 安全模块 (safety)
55
+
56
+ #### JWT Token
57
+ - 支持对称加密(HS256)和非对称加密(RS256)
58
+ - 提供 Access Token 和 Refresh Token 的生成
59
+ - Token 验证与刷新机制
60
+ - 支持自定义过期时间、签发者(issuer)和受众(audience)
61
+ - 单例模式实现,确保全局唯一实例
62
+
63
+ #### 密码加密
64
+ - MD5 加密
65
+ - PBKDF2 加密(推荐用于密码存储)
66
+ - 密码验证功能
67
+ - 随机字符串生成
68
+ - 数字验证码生成
69
+
70
+ ### 工具模块 (tools)
71
+
72
+ - 线程安全的单例模式装饰器
73
+
74
+ ## 📦 安装
75
+
76
+ ### 环境要求
77
+
78
+ - Python >= 3.9
79
+
80
+ ### 安装依赖
81
+
82
+ ```bash
83
+ pip install -r requirements.txt
84
+ ```
85
+
86
+ ## 🚀 使用说明
87
+
88
+ ### JWT Token 使用示例
89
+
90
+ #### 对称加密(HS256)
91
+
92
+ ```python
93
+ from fly_common.safety.jwt_token import JWTSymmetry
94
+
95
+ # 初始化(单例模式)
96
+ jwt_sym = JWTSymmetry(
97
+ secret_key="your-secret-key",
98
+ access_token_expire_seconds=86400, # 24小时
99
+ refresh_token_expire_seconds=604800, # 7天
100
+ issuer="your-app",
101
+ audience="your-users"
102
+ )
103
+
104
+ # 生成 Token
105
+ result = jwt_sym.create_at_rf_token(payload={"sub": "user123"})
106
+ if result.ok:
107
+ access_token = result.data["access_token"]
108
+ refresh_token = result.data["refresh_token"]
109
+
110
+ # 验证 Token
111
+ verify_result = jwt_sym.verify_token(access_token)
112
+ if verify_result.ok:
113
+ print("Token 有效:", verify_result.data)
114
+
115
+ # 刷新 Token
116
+ refresh_result = jwt_sym.refresh_token(access_token, refresh_token)
117
+ if refresh_result.ok:
118
+ new_tokens = refresh_result.data
119
+ ```
120
+
121
+ #### 非对称加密(RS256)
122
+
123
+ ```python
124
+ from fly_common.safety.jwt_token import JWTAsymmetry
125
+
126
+ # 初始化(单例模式)
127
+ jwt_asym = JWTAsymmetry(
128
+ private_key="your-private-key",
129
+ public_key="your-public-key",
130
+ algorithm="RS256"
131
+ )
132
+
133
+ # 使用方式与对称加密相同
134
+ result = jwt_asym.create_at_rf_token(payload={"sub": "user123"})
135
+ ```
136
+
137
+ ### 密码加密使用示例
138
+
139
+ ```python
140
+ from fly_common.safety.md5 import md5, en_password, check_password, code_number
141
+
142
+ # MD5 加密
143
+ hashed = md5("your-string")
144
+
145
+ # 密码加密(PBKDF2)
146
+ password_hash = en_password("user-password")
147
+
148
+ # 验证密码
149
+ is_valid = check_password("user-password", password_hash)
150
+
151
+ # 生成验证码
152
+ code = code_number(6) # 生成6位数字验证码
153
+ ```
154
+
155
+ ### 单例模式使用示例
156
+
157
+ ```python
158
+ from fly_common.tools.single import Singleton
159
+
160
+ @Singleton
161
+ class MyService:
162
+ def __init__(self):
163
+ self.data = []
164
+
165
+ # 获取实例
166
+ service1 = MyService()
167
+ service2 = MyService()
168
+
169
+ # service1 和 service2 是同一个实例
170
+ assert service1 is service2
171
+ ```
172
+
173
+ ## 📁 项目结构
174
+
175
+ ```
176
+ fly-common/
177
+ ├── safety/ # 安全模块
178
+ │ ├── jwt_token.py # JWT Token 管理
179
+ │ └── md5.py # 密码加密工具
180
+ ├── tools/ # 工具模块
181
+ │ └── single.py # 单例模式
182
+ └── __init__.py
183
+ ```
184
+
185
+ ## 🤝 参与贡献
186
+
187
+ 欢迎提交 Issue 和 Pull Request!
188
+
189
+ 1. Fork 本仓库
190
+ 2. 新建特性分支 (`git checkout -b feature/AmazingFeature`)
191
+ 3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
192
+ 4. 推送到分支 (`git push origin feature/AmazingFeature`)
193
+ 5. 提交 Pull Request
194
+
195
+ ## 📄 许可证
196
+
197
+ 本项目采用 MIT 许可证
@@ -0,0 +1,158 @@
1
+ # fly-common
2
+
3
+ > 一个轻量级的 Python 公共工具库,专注于安全认证和常用工具函数
4
+
5
+ ## 📖 介绍
6
+
7
+ fly-common 是一个功能精简但实用的 Python 公共库,主要提供以下核心功能:
8
+
9
+ - **JWT Token 管理**:支持对称加密(HS256)和非对称加密(RS256)的 JWT Token 生成、验证和刷新
10
+ - **密码加密**:提供 MD5、PBKDF2 等多种加密方式
11
+ - **工具函数**:提供单例模式、随机字符串生成等实用工具
12
+
13
+ ## ✨ 功能特性
14
+
15
+ ### 安全模块 (safety)
16
+
17
+ #### JWT Token
18
+ - 支持对称加密(HS256)和非对称加密(RS256)
19
+ - 提供 Access Token 和 Refresh Token 的生成
20
+ - Token 验证与刷新机制
21
+ - 支持自定义过期时间、签发者(issuer)和受众(audience)
22
+ - 单例模式实现,确保全局唯一实例
23
+
24
+ #### 密码加密
25
+ - MD5 加密
26
+ - PBKDF2 加密(推荐用于密码存储)
27
+ - 密码验证功能
28
+ - 随机字符串生成
29
+ - 数字验证码生成
30
+
31
+ ### 工具模块 (tools)
32
+
33
+ - 线程安全的单例模式装饰器
34
+
35
+ ## 📦 安装
36
+
37
+ ### 环境要求
38
+
39
+ - Python >= 3.9
40
+
41
+ ### 安装依赖
42
+
43
+ ```bash
44
+ pip install -r requirements.txt
45
+ ```
46
+
47
+ ## 🚀 使用说明
48
+
49
+ ### JWT Token 使用示例
50
+
51
+ #### 对称加密(HS256)
52
+
53
+ ```python
54
+ from fly_common.safety.jwt_token import JWTSymmetry
55
+
56
+ # 初始化(单例模式)
57
+ jwt_sym = JWTSymmetry(
58
+ secret_key="your-secret-key",
59
+ access_token_expire_seconds=86400, # 24小时
60
+ refresh_token_expire_seconds=604800, # 7天
61
+ issuer="your-app",
62
+ audience="your-users"
63
+ )
64
+
65
+ # 生成 Token
66
+ result = jwt_sym.create_at_rf_token(payload={"sub": "user123"})
67
+ if result.ok:
68
+ access_token = result.data["access_token"]
69
+ refresh_token = result.data["refresh_token"]
70
+
71
+ # 验证 Token
72
+ verify_result = jwt_sym.verify_token(access_token)
73
+ if verify_result.ok:
74
+ print("Token 有效:", verify_result.data)
75
+
76
+ # 刷新 Token
77
+ refresh_result = jwt_sym.refresh_token(access_token, refresh_token)
78
+ if refresh_result.ok:
79
+ new_tokens = refresh_result.data
80
+ ```
81
+
82
+ #### 非对称加密(RS256)
83
+
84
+ ```python
85
+ from fly_common.safety.jwt_token import JWTAsymmetry
86
+
87
+ # 初始化(单例模式)
88
+ jwt_asym = JWTAsymmetry(
89
+ private_key="your-private-key",
90
+ public_key="your-public-key",
91
+ algorithm="RS256"
92
+ )
93
+
94
+ # 使用方式与对称加密相同
95
+ result = jwt_asym.create_at_rf_token(payload={"sub": "user123"})
96
+ ```
97
+
98
+ ### 密码加密使用示例
99
+
100
+ ```python
101
+ from fly_common.safety.md5 import md5, en_password, check_password, code_number
102
+
103
+ # MD5 加密
104
+ hashed = md5("your-string")
105
+
106
+ # 密码加密(PBKDF2)
107
+ password_hash = en_password("user-password")
108
+
109
+ # 验证密码
110
+ is_valid = check_password("user-password", password_hash)
111
+
112
+ # 生成验证码
113
+ code = code_number(6) # 生成6位数字验证码
114
+ ```
115
+
116
+ ### 单例模式使用示例
117
+
118
+ ```python
119
+ from fly_common.tools.single import Singleton
120
+
121
+ @Singleton
122
+ class MyService:
123
+ def __init__(self):
124
+ self.data = []
125
+
126
+ # 获取实例
127
+ service1 = MyService()
128
+ service2 = MyService()
129
+
130
+ # service1 和 service2 是同一个实例
131
+ assert service1 is service2
132
+ ```
133
+
134
+ ## 📁 项目结构
135
+
136
+ ```
137
+ fly-common/
138
+ ├── safety/ # 安全模块
139
+ │ ├── jwt_token.py # JWT Token 管理
140
+ │ └── md5.py # 密码加密工具
141
+ ├── tools/ # 工具模块
142
+ │ └── single.py # 单例模式
143
+ └── __init__.py
144
+ ```
145
+
146
+ ## 🤝 参与贡献
147
+
148
+ 欢迎提交 Issue 和 Pull Request!
149
+
150
+ 1. Fork 本仓库
151
+ 2. 新建特性分支 (`git checkout -b feature/AmazingFeature`)
152
+ 3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
153
+ 4. 推送到分支 (`git push origin feature/AmazingFeature`)
154
+ 5. 提交 Pull Request
155
+
156
+ ## 📄 许可证
157
+
158
+ 本项目采用 MIT 许可证
@@ -0,0 +1,56 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fly-common" # 项目名称
7
+ version = "0.1.0" # 项目版本号
8
+ description = "通用模块" # 项目备注
9
+ readme = "README.md"
10
+ requires-python = ">=3.9" # 最低python版本支持
11
+ authors = [
12
+ { name = "fly", email = "fzkf117@163.com" }
13
+ ]
14
+ license = { text = "MIT" }
15
+
16
+ dependencies = [
17
+ "annotated-types==0.7.0",
18
+ "cffi==2.0.0",
19
+ "cryptography==46.0.5",
20
+ "ecdsa==0.19.1",
21
+ "passlib==1.7.4",
22
+ "pyasn1==0.6.3",
23
+ "pycparser==2.23",
24
+ "pydantic==2.12.5",
25
+ "pydantic-core==2.41.5",
26
+ "python-jose==3.5.0",
27
+ "rsa==4.9.1",
28
+ "six==1.17.0",
29
+ "typing-extensions==4.15.0",
30
+ "typing-inspection==0.4.2",
31
+ ]
32
+
33
+ keywords = ["utils", "tools"]
34
+
35
+ classifiers = [
36
+ "Development Status :: 4 - Beta",
37
+ "Intended Audience :: Developers",
38
+ "Operating System :: OS Independent",
39
+ "Programming Language :: Python :: 3",
40
+ "Programming Language :: Python :: 3.6",
41
+ "Programming Language :: Python :: 3.7",
42
+ "Programming Language :: Python :: 3.8",
43
+ "Programming Language :: Python :: 3.9",
44
+ "Programming Language :: Python :: 3.10",
45
+ "Programming Language :: Python :: 3.11",
46
+ "Programming Language :: Python :: 3.12",
47
+ "Programming Language :: Python :: 3.13",
48
+ "Topic :: Software Development :: Libraries :: Python Modules"
49
+ ]
50
+
51
+ [tool.setuptools]
52
+ package-dir = {"" = "src"}
53
+
54
+ [tool.setuptools.packages.find]
55
+ where = ["src"]
56
+ include = ["fly_common*"] # 需要打包的项目
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
File without changes
@@ -0,0 +1,290 @@
1
+ import uuid
2
+ from datetime import datetime, timedelta, UTC
3
+ from typing import Union, Dict, Any, List
4
+ from pydantic import BaseModel
5
+ from jose import jwt, JWTError, ExpiredSignatureError
6
+ from ..tools.single import Singleton
7
+
8
+
9
+ class Response(BaseModel):
10
+ ok: bool
11
+ msg: str = ""
12
+ data: Union[Dict[str, Any], List[Dict[str, Any]], None] = None
13
+
14
+
15
+ class BaseJWT:
16
+ """
17
+ JWT 基类(模板类)
18
+ 子类只需要实现:
19
+ - _encode
20
+ - _decode
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ access_token_expire_seconds: int = 86400,
26
+ refresh_token_expire_seconds: int = 604800,
27
+ issuer: str = None,
28
+ audience: str = None
29
+ ):
30
+ self.access_token_expire_seconds = access_token_expire_seconds
31
+ self.refresh_token_expire_seconds = refresh_token_expire_seconds
32
+ self.issuer = issuer
33
+ self.audience = audience
34
+
35
+ # =========================
36
+ # 子类必须实现
37
+ # =========================
38
+ def _encode(self, payload: dict) -> str:
39
+ raise NotImplementedError
40
+
41
+ def _decode(self, token: str, verify_exp: bool = True) -> dict:
42
+ raise NotImplementedError
43
+
44
+ # ==========================================================
45
+ # 创建 Access + Refresh Token
46
+ # ==========================================================
47
+ def create_at_rf_token(self, payload: dict) -> Response:
48
+ """
49
+ payload 建议:
50
+ {
51
+ "sub": user_id # 标准字段(用户唯一标识)
52
+ }
53
+ """
54
+
55
+ now = datetime.now(UTC)
56
+ jti = uuid.uuid4().hex # token 唯一ID
57
+
58
+ base_payload = {
59
+ **payload,
60
+ "jti": jti,
61
+ }
62
+
63
+ # 可选字段(只有配置才加)
64
+ if self.issuer:
65
+ base_payload["iss"] = self.issuer
66
+
67
+ if self.audience:
68
+ base_payload["aud"] = self.audience
69
+
70
+ # Access Token
71
+ access_payload = {
72
+ **base_payload,
73
+ "token_type": "access",
74
+ "iat": now,
75
+ "exp": now + timedelta(seconds=self.access_token_expire_seconds),
76
+ }
77
+
78
+ # Refresh Token
79
+ refresh_payload = {
80
+ **base_payload,
81
+ "token_type": "refresh",
82
+ "iat": now,
83
+ "exp": now + timedelta(seconds=self.refresh_token_expire_seconds),
84
+ }
85
+
86
+ return Response(
87
+ ok=True,
88
+ msg="生成成功",
89
+ data={
90
+ "access_token": self._encode(access_payload),
91
+ "refresh_token": self._encode(refresh_payload),
92
+ })
93
+
94
+ # ==========================================================
95
+ # 验证 Token(严格校验)
96
+ # ==========================================================
97
+ def verify_token(self, token: str) -> Response:
98
+ try:
99
+ payload = self._decode(token, verify_exp=True)
100
+ return Response(ok=True, msg="验证成功", data=payload)
101
+
102
+ except ExpiredSignatureError:
103
+ return Response(ok=False, msg="Token 过期")
104
+
105
+ except JWTError:
106
+ return Response(ok=False, msg="Token 非法")
107
+
108
+ except Exception as e:
109
+ return Response(ok=False, msg=str(e))
110
+
111
+ # ==========================================================
112
+ # 解析 Token(允许过期)
113
+ # ==========================================================
114
+ def parse_token(self, token: str) -> Response:
115
+ """
116
+ 用于 refresh 场景
117
+ """
118
+ try:
119
+ payload = self._decode(token, verify_exp=False)
120
+ return Response(ok=True, msg="解析成功", data=payload)
121
+
122
+ except JWTError:
123
+ return Response(ok=False, msg="Token 非法")
124
+
125
+ except Exception as e:
126
+ return Response(ok=False, msg=str(e))
127
+
128
+ # ==========================================================
129
+ # 刷新 Token(仅基础逻辑)
130
+ # ==========================================================
131
+ def refresh_token(self, access_token: str, refresh_token: str) -> Response:
132
+ """
133
+ ⚠️ 注意:
134
+ ✔ 不做黑名单
135
+ ✔ 不做真正“轮换控制”
136
+ ✔ 只负责生成新 token
137
+ """
138
+
139
+ # 1. 校验 refresh_token
140
+ rf_res = self.verify_token(refresh_token)
141
+ if not rf_res.ok:
142
+ return rf_res
143
+
144
+ rf_payload = rf_res.data
145
+
146
+ if rf_payload.get("token_type") != "refresh":
147
+ return Response(ok=False, msg="RefreshToken 非法")
148
+
149
+ # 2. 解析 access_token(允许过期)
150
+ at_res = self.parse_token(access_token)
151
+ if not at_res.ok:
152
+ return at_res
153
+
154
+ at_payload = at_res.data
155
+
156
+ # 3. 校验 jti 一致
157
+ if at_payload.get("jti") != rf_payload.get("jti"):
158
+ return Response(ok=False, msg="Token 不一致")
159
+
160
+ # 4. 提取业务 payload(去掉系统字段)
161
+ base_payload = {
162
+ k: v for k, v in at_payload.items()
163
+ if k not in ["exp", "iat", "token_type"]
164
+ }
165
+
166
+ # 5. 生成新 token(⚠️这里只是“重新签发”,不是完整轮换策略)
167
+ return Response(ok=True, msg="刷新成功", data=self.create_at_rf_token(base_payload).data)
168
+
169
+
170
+ @Singleton
171
+ class JWTSymmetry(BaseJWT):
172
+ """
173
+ 对称 JWT(HS256)
174
+
175
+ ✔ 支持 issuer / audience 可选(None 不校验)
176
+ ✔ 标准 JWT 字段(sub / jti / token_type)
177
+ ✔ 只负责 token 处理(不包含黑名单、轮换策略)
178
+ """
179
+
180
+ def __init__(self, secret_key: str, algorithm="HS256", **kwargs):
181
+ super().__init__(**kwargs)
182
+ self.secret_key = secret_key
183
+ self.algorithm = algorithm
184
+
185
+ # ==========================================================
186
+ # 内部:编码
187
+ # ==========================================================
188
+ def _encode(self, payload: dict) -> str:
189
+ return jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
190
+
191
+ # ==========================================================
192
+ # 内部:解码(关键:动态校验 issuer / audience)
193
+ # ==========================================================
194
+ def _decode(self, token: str, verify_exp: bool = True) -> dict:
195
+ """
196
+ 支持 issuer / audience 为 None 时自动关闭校验
197
+ """
198
+
199
+ options = {
200
+ "verify_exp": verify_exp,
201
+ "verify_aud": self.audience is not None,
202
+ "verify_iss": self.issuer is not None,
203
+ }
204
+
205
+ decode_kwargs = {
206
+ "key": self.secret_key,
207
+ "algorithms": [self.algorithm],
208
+ "options": options,
209
+ }
210
+
211
+ # 只有配置了才传(否则会报错)
212
+ if self.audience is not None:
213
+ decode_kwargs["audience"] = self.audience
214
+
215
+ if self.issuer is not None:
216
+ decode_kwargs["issuer"] = self.issuer
217
+
218
+ return jwt.decode(token, **decode_kwargs)
219
+
220
+ def create_at_rf_token(self, payload: dict) -> Response:
221
+ return super().create_at_rf_token(payload)
222
+
223
+ def verify_token(self, token: str) -> Response:
224
+ # 直接调用父类实现
225
+ return super().verify_token(token)
226
+
227
+ def parse_token(self, token: str) -> Response:
228
+ return super().parse_token(token)
229
+
230
+ def refresh_token(self, access_token: str, refresh_token: str) -> Response:
231
+ return super().refresh_token(access_token, refresh_token)
232
+
233
+
234
+ @Singleton
235
+ class JWTAsymmetry(BaseJWT):
236
+
237
+ def __init__(self, private_key: str, public_key: str, algorithm="RS256", **kwargs):
238
+ super().__init__(**kwargs)
239
+ self.private_key = private_key
240
+ self.public_key = public_key
241
+ self.algorithm = algorithm
242
+
243
+ # ==========================================================
244
+ # 内部:编码(用私钥签名)
245
+ # ==========================================================
246
+ def _encode(self, payload: dict) -> str:
247
+ return jwt.encode(payload, self.private_key, algorithm=self.algorithm)
248
+
249
+ # ==========================================================
250
+ # 内部:解码(用公钥验证 + 动态校验 iss / aud)
251
+ # ==========================================================
252
+ def _decode(self, token: str, verify_exp: bool = True) -> dict:
253
+ """
254
+ ✔ 使用公钥验证签名
255
+ ✔ 支持 issuer / audience 为 None 时不校验
256
+ """
257
+
258
+ options = {
259
+ "verify_exp": verify_exp,
260
+ "verify_aud": self.audience is not None,
261
+ "verify_iss": self.issuer is not None,
262
+ }
263
+
264
+ decode_kwargs = {
265
+ "key": self.public_key,
266
+ "algorithms": [self.algorithm],
267
+ "options": options,
268
+ }
269
+
270
+ # 只有配置了才传,否则会报错
271
+ if self.audience is not None:
272
+ decode_kwargs["audience"] = self.audience
273
+
274
+ if self.issuer is not None:
275
+ decode_kwargs["issuer"] = self.issuer
276
+
277
+ return jwt.decode(token, **decode_kwargs)
278
+
279
+ def create_at_rf_token(self, payload: dict) -> Response:
280
+ return super().create_at_rf_token(payload)
281
+
282
+ def verify_token(self, token: str) -> Response:
283
+ # 直接调用父类实现
284
+ return super().verify_token(token)
285
+
286
+ def parse_token(self, token: str) -> Response:
287
+ return super().parse_token(token)
288
+
289
+ def refresh_token(self, access_token: str, refresh_token: str) -> Response:
290
+ return super().refresh_token(access_token, refresh_token)
@@ -0,0 +1,40 @@
1
+ import hashlib
2
+ import random
3
+ import uuid
4
+ from passlib.handlers.pbkdf2 import pbkdf2_sha256
5
+
6
+
7
+ def md5(s: str) -> str:
8
+ """返回字符串的 MD5 值"""
9
+ return hashlib.md5(s.encode('utf-8')).hexdigest()
10
+
11
+
12
+ def random_str() -> str:
13
+ """返回唯一随机字符串(基于 UUID + MD5)"""
14
+ return hashlib.md5(uuid.uuid1().bytes).hexdigest()
15
+
16
+
17
+ def en_password(psw: str) -> str:
18
+ """使用 PBKDF2 进行加密"""
19
+ return pbkdf2_sha256.hash(psw)
20
+
21
+
22
+ def check_password(password: str, hashed: str) -> bool:
23
+ """验证密码"""
24
+ try:
25
+ return pbkdf2_sha256.verify(password, hashed)
26
+ except Exception as e:
27
+ return False
28
+
29
+
30
+ def code_number(length: int) -> str:
31
+ """生成指定长度的纯数字验证码"""
32
+ return ''.join(random.choices('0123456789', k=length))
33
+
34
+
35
+
36
+ if __name__ == '__main__':
37
+ password = "25d55ad283aa400af464c76d713c07ad" # "12345678"
38
+ other = en_password(md5(password))
39
+ print(other)
40
+ print(check_password(md5(password), other))
File without changes
@@ -0,0 +1,14 @@
1
+ from threading import Lock
2
+
3
+ class Singleton:
4
+ def __init__(self, cls):
5
+ self._cls = cls
6
+ self._instance = None
7
+ self._lock = Lock()
8
+
9
+ def __call__(self, *args, **kwargs):
10
+ if self._instance is None:
11
+ with self._lock:
12
+ if self._instance is None:
13
+ self._instance = self._cls(*args, **kwargs)
14
+ return self._instance
@@ -0,0 +1,197 @@
1
+ Metadata-Version: 2.4
2
+ Name: fly-common
3
+ Version: 0.1.0
4
+ Summary: 通用模块
5
+ Author-email: fly <fzkf117@163.com>
6
+ License: MIT
7
+ Keywords: utils,tools
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.6
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: annotated-types==0.7.0
25
+ Requires-Dist: cffi==2.0.0
26
+ Requires-Dist: cryptography==46.0.5
27
+ Requires-Dist: ecdsa==0.19.1
28
+ Requires-Dist: passlib==1.7.4
29
+ Requires-Dist: pyasn1==0.6.3
30
+ Requires-Dist: pycparser==2.23
31
+ Requires-Dist: pydantic==2.12.5
32
+ Requires-Dist: pydantic-core==2.41.5
33
+ Requires-Dist: python-jose==3.5.0
34
+ Requires-Dist: rsa==4.9.1
35
+ Requires-Dist: six==1.17.0
36
+ Requires-Dist: typing-extensions==4.15.0
37
+ Requires-Dist: typing-inspection==0.4.2
38
+ Dynamic: license-file
39
+
40
+ # fly-common
41
+
42
+ > 一个轻量级的 Python 公共工具库,专注于安全认证和常用工具函数
43
+
44
+ ## 📖 介绍
45
+
46
+ fly-common 是一个功能精简但实用的 Python 公共库,主要提供以下核心功能:
47
+
48
+ - **JWT Token 管理**:支持对称加密(HS256)和非对称加密(RS256)的 JWT Token 生成、验证和刷新
49
+ - **密码加密**:提供 MD5、PBKDF2 等多种加密方式
50
+ - **工具函数**:提供单例模式、随机字符串生成等实用工具
51
+
52
+ ## ✨ 功能特性
53
+
54
+ ### 安全模块 (safety)
55
+
56
+ #### JWT Token
57
+ - 支持对称加密(HS256)和非对称加密(RS256)
58
+ - 提供 Access Token 和 Refresh Token 的生成
59
+ - Token 验证与刷新机制
60
+ - 支持自定义过期时间、签发者(issuer)和受众(audience)
61
+ - 单例模式实现,确保全局唯一实例
62
+
63
+ #### 密码加密
64
+ - MD5 加密
65
+ - PBKDF2 加密(推荐用于密码存储)
66
+ - 密码验证功能
67
+ - 随机字符串生成
68
+ - 数字验证码生成
69
+
70
+ ### 工具模块 (tools)
71
+
72
+ - 线程安全的单例模式装饰器
73
+
74
+ ## 📦 安装
75
+
76
+ ### 环境要求
77
+
78
+ - Python >= 3.9
79
+
80
+ ### 安装依赖
81
+
82
+ ```bash
83
+ pip install -r requirements.txt
84
+ ```
85
+
86
+ ## 🚀 使用说明
87
+
88
+ ### JWT Token 使用示例
89
+
90
+ #### 对称加密(HS256)
91
+
92
+ ```python
93
+ from fly_common.safety.jwt_token import JWTSymmetry
94
+
95
+ # 初始化(单例模式)
96
+ jwt_sym = JWTSymmetry(
97
+ secret_key="your-secret-key",
98
+ access_token_expire_seconds=86400, # 24小时
99
+ refresh_token_expire_seconds=604800, # 7天
100
+ issuer="your-app",
101
+ audience="your-users"
102
+ )
103
+
104
+ # 生成 Token
105
+ result = jwt_sym.create_at_rf_token(payload={"sub": "user123"})
106
+ if result.ok:
107
+ access_token = result.data["access_token"]
108
+ refresh_token = result.data["refresh_token"]
109
+
110
+ # 验证 Token
111
+ verify_result = jwt_sym.verify_token(access_token)
112
+ if verify_result.ok:
113
+ print("Token 有效:", verify_result.data)
114
+
115
+ # 刷新 Token
116
+ refresh_result = jwt_sym.refresh_token(access_token, refresh_token)
117
+ if refresh_result.ok:
118
+ new_tokens = refresh_result.data
119
+ ```
120
+
121
+ #### 非对称加密(RS256)
122
+
123
+ ```python
124
+ from fly_common.safety.jwt_token import JWTAsymmetry
125
+
126
+ # 初始化(单例模式)
127
+ jwt_asym = JWTAsymmetry(
128
+ private_key="your-private-key",
129
+ public_key="your-public-key",
130
+ algorithm="RS256"
131
+ )
132
+
133
+ # 使用方式与对称加密相同
134
+ result = jwt_asym.create_at_rf_token(payload={"sub": "user123"})
135
+ ```
136
+
137
+ ### 密码加密使用示例
138
+
139
+ ```python
140
+ from fly_common.safety.md5 import md5, en_password, check_password, code_number
141
+
142
+ # MD5 加密
143
+ hashed = md5("your-string")
144
+
145
+ # 密码加密(PBKDF2)
146
+ password_hash = en_password("user-password")
147
+
148
+ # 验证密码
149
+ is_valid = check_password("user-password", password_hash)
150
+
151
+ # 生成验证码
152
+ code = code_number(6) # 生成6位数字验证码
153
+ ```
154
+
155
+ ### 单例模式使用示例
156
+
157
+ ```python
158
+ from fly_common.tools.single import Singleton
159
+
160
+ @Singleton
161
+ class MyService:
162
+ def __init__(self):
163
+ self.data = []
164
+
165
+ # 获取实例
166
+ service1 = MyService()
167
+ service2 = MyService()
168
+
169
+ # service1 和 service2 是同一个实例
170
+ assert service1 is service2
171
+ ```
172
+
173
+ ## 📁 项目结构
174
+
175
+ ```
176
+ fly-common/
177
+ ├── safety/ # 安全模块
178
+ │ ├── jwt_token.py # JWT Token 管理
179
+ │ └── md5.py # 密码加密工具
180
+ ├── tools/ # 工具模块
181
+ │ └── single.py # 单例模式
182
+ └── __init__.py
183
+ ```
184
+
185
+ ## 🤝 参与贡献
186
+
187
+ 欢迎提交 Issue 和 Pull Request!
188
+
189
+ 1. Fork 本仓库
190
+ 2. 新建特性分支 (`git checkout -b feature/AmazingFeature`)
191
+ 3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
192
+ 4. 推送到分支 (`git push origin feature/AmazingFeature`)
193
+ 5. 提交 Pull Request
194
+
195
+ ## 📄 许可证
196
+
197
+ 本项目采用 MIT 许可证
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/fly_common/__init__.py
5
+ src/fly_common.egg-info/PKG-INFO
6
+ src/fly_common.egg-info/SOURCES.txt
7
+ src/fly_common.egg-info/dependency_links.txt
8
+ src/fly_common.egg-info/requires.txt
9
+ src/fly_common.egg-info/top_level.txt
10
+ src/fly_common/safety/__init__.py
11
+ src/fly_common/safety/jwt_token.py
12
+ src/fly_common/safety/md5.py
13
+ src/fly_common/tools/__init__.py
14
+ src/fly_common/tools/single.py
@@ -0,0 +1,14 @@
1
+ annotated-types==0.7.0
2
+ cffi==2.0.0
3
+ cryptography==46.0.5
4
+ ecdsa==0.19.1
5
+ passlib==1.7.4
6
+ pyasn1==0.6.3
7
+ pycparser==2.23
8
+ pydantic==2.12.5
9
+ pydantic-core==2.41.5
10
+ python-jose==3.5.0
11
+ rsa==4.9.1
12
+ six==1.17.0
13
+ typing-extensions==4.15.0
14
+ typing-inspection==0.4.2
@@ -0,0 +1 @@
1
+ fly_common