Skip to content

Commit 96f0f6c

Browse files
committed
feat: implement JWT authentication and enhance image processing
- Added JWT authentication middleware to secure the avatar API endpoint. - Implemented functions to decode JWT from request headers and retrieve user information. - Enhanced image processing in face_detect.py to resize avatars to 50x50 pixels before saving. - Updated requirements.txt to include PyJWT for JWT handling. - Introduced settings.py for database configuration with environment variables. - Added sample images for testing avatar processing.
1 parent 1c3b44d commit 96f0f6c

File tree

6 files changed

+56
-5
lines changed

6 files changed

+56
-5
lines changed

ts-avatar-service/app.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import json
77
import base64
88
import traceback
9+
import jwt
10+
from datetime import datetime, timezone
911

1012
from face_detect import check
1113

@@ -21,9 +23,40 @@
2123

2224
receive_path = r"./received/"
2325

26+
# Secret key should be kept in environment variable in production
27+
JWT_SECRET = os.environ.get("JWT_SECRET", "secret")
28+
JWT_ALG = os.environ.get("JWT_ALG", "HS256")
29+
30+
31+
def decode_jwt_from_header(req):
32+
"""Extract and validate JWT from Authorization header. Returns claims dict or None."""
33+
auth = req.headers.get("Authorization")
34+
if not auth or not auth.startswith("Bearer "):
35+
return None
36+
token = auth[7:]
37+
try:
38+
claims = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALG])
39+
# check expiration
40+
exp = claims.get("exp")
41+
if exp and datetime.fromtimestamp(exp, tz=timezone.utc) < datetime.now(tz=timezone.utc):
42+
return None
43+
return claims
44+
except jwt.PyJWTError:
45+
return None
46+
47+
def get_user_info(req):
48+
claims = decode_jwt_from_header(req)
49+
if claims is None:
50+
return None
51+
return claims.get("id")
2452

2553
@app.route('/api/v1/avatar', methods=["POST"])
2654
def hello():
55+
# jwt auth
56+
user_id = get_user_info(request)
57+
if user_id is None:
58+
return jsonify({"msg": "Invalid or missing token"}), 401
59+
2760
# receive file
2861
data = request.get_data().decode('utf-8')
2962
data = json.loads(data)
@@ -42,6 +75,7 @@ def hello():
4275
if type(result) == dict and result.get("msg") is not None:
4376
return jsonify(result), 400
4477

78+
# attach user info if needed
4579
return result, 200
4680

4781

ts-avatar-service/face_detect.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,26 @@ def check(img):
3333
height = d.bottom() - d.top()
3434
width = d.right() - d.left()
3535

36-
# 根据人脸大小生成空的图像
36+
# 根据人脸大小生成空的图像并裁剪
3737
img_blank = np.zeros((height, width, 3), np.uint8)
38-
3938
for i in range(height):
4039
for j in range(width):
4140
img_blank[i][j] = img[d.top() + i][d.left() + j]
4241

43-
print("Save to:", path_save + "img_face_" + str(k + 1) + ".jpg")
44-
cv2.imwrite(path_save + "img_face_" + str(k + 1) + ".jpg", img_blank)
42+
# 将头像缩放到 50x50
43+
avatar_50 = cv2.resize(img_blank, (50, 50), interpolation=cv2.INTER_AREA)
44+
45+
# 保存(可选)
46+
save_path = f"{path_save}img_face_{k + 1}.jpg"
47+
print("Save to:", save_path)
48+
cv2.imwrite(save_path, avatar_50)
4549

46-
base64_str = cv2.imencode('.jpg',img_blank)[1].tostring()
50+
# 编码为 base64 返回
51+
base64_str = cv2.imencode('.jpg', avatar_50)[1].tobytes()
4752
base64_str = base64.b64encode(base64_str)
53+
print(f'length: {len(base64_str)}')
4854
return base64_str
4955

56+
if __name__ == "__main__":
57+
img = cv2.imread("./images/test.png")
58+
print(check(img))
2.17 KB
Loading
6.78 KB
Loading

ts-avatar-service/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ numpy>=1.24.0
99
opencv-python>=4.8.0
1010
Pillow>=10.0.0
1111
Werkzeug>=3.0.0
12+
PyJWT>=2.8.0

ts-avatar-service/settings.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import os
2+
3+
host = os.getenv("AVATAR_MYSQL_HOST", "ts-avatar-mysql")
4+
port = os.getenv("AVATAR_MYSQL_PORT", 3306)
5+
user = os.getenv("AVATAR_MYSQL_USER", "root")
6+
password = os.getenv("AVATAR_MYSQL_PASSWORD", "Abcd1234#")
7+
db = os.getenv("AVATAR_MYSQL_DATABASE", "ts-avatar-mysql")

0 commit comments

Comments
 (0)