Skip to content

短信认证api#3

Open
zeroHYH wants to merge 9 commits intoSDU-toolhub:masterfrom
zeroHYH:master
Open

短信认证api#3
zeroHYH wants to merge 9 commits intoSDU-toolhub:masterfrom
zeroHYH:master

Conversation

@zeroHYH
Copy link

@zeroHYH zeroHYH commented Feb 11, 2026

No description provided.

Copilot AI review requested due to automatic review settings February 11, 2026 06:20
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an SMS-based authentication flow against SDU CAS, exposing it as a small FastAPI service and providing a local interactive script to manually complete the captcha + SMS login flow.

Changes:

  • Added sms_auth_api.py implementing /code, /sms, and /login endpoints for captcha retrieval, SMS code sending, and login + user info fetch.
  • Added cas_sms_auth.py as a Tkinter-assisted CLI script to complete CAS SMS login and print resulting cookies.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 14 comments.

File Description
sms_auth_api.py New FastAPI-based SMS auth API with in-memory session/cookie handling and upstream calls to pass.sdu.edu.cn + service.sdu.edu.cn.
cas_sms_auth.py New interactive Tkinter script to obtain image captcha, request SMS code, and perform CAS login.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5 to +13
import httpx
from fastapi import Body, FastAPI, HTTPException, Request, Response
from httpx import Client, Cookies, HTTPError

from uniform_login_des import strEnc

app = FastAPI(debug=True, title="统一认证短信登录")


Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running FastAPI(debug=True) should not be the default for a deployed auth API, since debug mode can leak internal details. Consider making this configurable via environment (or default to debug=False).

Suggested change
import httpx
from fastapi import Body, FastAPI, HTTPException, Request, Response
from httpx import Client, Cookies, HTTPError
from uniform_login_des import strEnc
app = FastAPI(debug=True, title="统一认证短信登录")
import os
import httpx
from fastapi import Body, FastAPI, HTTPException, Request, Response
from httpx import Client, Cookies, HTTPError
from uniform_login_des import strEnc
DEBUG = os.getenv("SMS_AUTH_API_DEBUG", "").lower() in ("1", "true", "yes")
app = FastAPI(debug=DEBUG, title="统一认证短信登录")

Copilot uses AI. Check for mistakes.
app = FastAPI(debug=True, title="统一认证短信登录")


login_sessions: dict[str, Cookies] = {}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

login_sessions is a global in-memory dict used for auth session state. This is not safe under multi-threaded access and won’t work correctly with multiple Uvicorn/Gunicorn workers (sessions will be lost or inconsistent). Use a process-safe store (Redis/db) or at least guard access with a lock and document the single-process limitation.

Copilot uses AI. Check for mistakes.
sms_auth_api.py Outdated
raise HTTPException(503, "cas unavailable")
login_sessions[(ls := uuid7().hex)] = upstream.cookies
response = Response(upstream.content, media_type="image/gif")
response.set_cookie("login_session", ls, max_age=60, httponly=True)
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The session cookie is set without secure and samesite attributes. For an authentication flow, consider secure=True (when served over HTTPS) and an explicit samesite policy to reduce CSRF risk; also consider path/domain scoping as appropriate.

Suggested change
response.set_cookie("login_session", ls, max_age=60, httponly=True)
response.set_cookie(
"login_session",
ls,
max_age=60,
httponly=True,
secure=True,
samesite="lax",
)

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +27
upstream = httpx.get(f"https://pass.sdu.edu.cn/cas/code?{random()}")
except HTTPError:
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outbound httpx.get/post calls have no explicit timeout, so a slow/unresponsive upstream can tie up your worker threads indefinitely. Pass a reasonable timeout (and consider reusing a shared httpx.Client for connection pooling).

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +59
if res.json().get("redirectUrl") != "login":
raise HTTPException(400, "Invalid code")
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

res.json() can raise JSONDecodeError (and res could be a non-2xx response) which currently bubbles up as a 500. Consider calling res.raise_for_status() and handling JSON decode/shape errors, mapping them to a 502/503 with a clear message.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +56
client = Client()

if mobile == "":
mobile = input("手机号: ")
if service == "":
exit("请填写service")
while True:
window = Tk()
window.title("图片验证码")
window.bind("<Escape>", lambda e: window.destroy())
photo = PhotoImage(data=client.get("https://pass.sdu.edu.cn/cas/code").content)
img_label = Label(window, image=photo)
img_label.pack(pady=10)
window.mainloop()
imgcode = input("图片验证码: ")
url = (
client.post(
"https://pass.sdu.edu.cn/cas/loginByMorE",
data={
"method": "sendMobileCode",
"sendConfirm": imgcode,
"mobile": mobile,
"random": random(),
},
)
.json()
.get("redirectUrl")
)
if url == "login":
break
else:
print("图片验证码错误,请重试。")

mobilecode = input("短信验证码: ")
if (
url := client.post(
"https://pass.sdu.edu.cn/cas/loginByMorE",
data={
"method": "login",
"mobile": mobile,
"mobileCode": mobilecode,
"random": random(),
"service": service,
},
)
.json()
.get("redirectUrl")
):
res = client.get(url, follow_redirects=True)
print(client.cookies)
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The httpx.Client() is never closed. Wrap it in a context manager (with Client() as client:) or call client.close() at the end to avoid leaving connections open.

Suggested change
client = Client()
if mobile == "":
mobile = input("手机号: ")
if service == "":
exit("请填写service")
while True:
window = Tk()
window.title("图片验证码")
window.bind("<Escape>", lambda e: window.destroy())
photo = PhotoImage(data=client.get("https://pass.sdu.edu.cn/cas/code").content)
img_label = Label(window, image=photo)
img_label.pack(pady=10)
window.mainloop()
imgcode = input("图片验证码: ")
url = (
client.post(
"https://pass.sdu.edu.cn/cas/loginByMorE",
data={
"method": "sendMobileCode",
"sendConfirm": imgcode,
"mobile": mobile,
"random": random(),
},
)
.json()
.get("redirectUrl")
)
if url == "login":
break
else:
print("图片验证码错误,请重试。")
mobilecode = input("短信验证码: ")
if (
url := client.post(
"https://pass.sdu.edu.cn/cas/loginByMorE",
data={
"method": "login",
"mobile": mobile,
"mobileCode": mobilecode,
"random": random(),
"service": service,
},
)
.json()
.get("redirectUrl")
):
res = client.get(url, follow_redirects=True)
print(client.cookies)
with Client() as client:
if mobile == "":
mobile = input("手机号: ")
if service == "":
exit("请填写service")
while True:
window = Tk()
window.title("图片验证码")
window.bind("<Escape>", lambda e: window.destroy())
photo = PhotoImage(data=client.get("https://pass.sdu.edu.cn/cas/code").content)
img_label = Label(window, image=photo)
img_label.pack(pady=10)
window.mainloop()
imgcode = input("图片验证码: ")
url = (
client.post(
"https://pass.sdu.edu.cn/cas/loginByMorE",
data={
"method": "sendMobileCode",
"sendConfirm": imgcode,
"mobile": mobile,
"random": random(),
},
)
.json()
.get("redirectUrl")
)
if url == "login":
break
else:
print("图片验证码错误,请重试。")
mobilecode = input("短信验证码: ")
if (
url := client.post(
"https://pass.sdu.edu.cn/cas/loginByMorE",
data={
"method": "login",
"mobile": mobile,
"mobileCode": mobilecode,
"random": random(),
"service": service,
},
)
.json()
.get("redirectUrl")
):
res = client.get(url, follow_redirects=True)
print(client.cookies)

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +12
if service == "":
exit("请填写service")
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exit("请填写service") relies on the interactive helper and is not guaranteed in non-interactive/script contexts. Prefer sys.exit(...) for scripts.

Copilot uses AI. Check for mistakes.
sms_auth_api.py Outdated

def clean_expired_sessions():
now = datetime.now().timestamp()
[login_sessions.pop(u) for u in login_sessions if now > UUID(u).time + 60 * 1000]
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean_expired_sessions() mutates login_sessions while iterating over it (for u in login_sessions + pop), which will raise RuntimeError: dictionary changed size during iteration. Build a list of expired keys first (e.g., expired = [...]), then delete them in a second loop; also avoid list-comprehensions for side effects.

Suggested change
[login_sessions.pop(u) for u in login_sessions if now > UUID(u).time + 60 * 1000]
expired_keys = []
for u in login_sessions:
if now > UUID(u).time + 60 * 1000:
expired_keys.append(u)
for u in expired_keys:
login_sessions.pop(u, None)

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,109 @@
from datetime import datetime
from random import random
from uuid import UUID, uuid7
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from uuid import ... uuid7 will fail on Python versions where uuid7() is not available in the stdlib (common today). Either pin the project to a Python version that provides uuid7, or switch to a supported UUID generator (e.g., uuid4) and store timestamps separately for expiry.

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +11
from fastapi import Body, FastAPI, HTTPException, Request, Response
from httpx import Client, Cookies, HTTPError

from uniform_login_des import strEnc

app = FastAPI(debug=True, title="统一认证短信登录")
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module introduces a FastAPI app, but requirements.txt currently does not include fastapi (or an ASGI server like uvicorn). As-is, a fresh install from requirements will fail at import/runtime; add the missing dependencies or document the expected runtime environment.

Copilot uses AI. Check for mistakes.
2. Refactor session management functions and update cookie expiration time
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants