当前位置:首页>python>Java SSO SDK 迁移至 Python 云函数(Serverless)

Java SSO SDK 迁移至 Python 云函数(Serverless)

  • 2026-09-08 00:48:47
Java SSO SDK 迁移至 Python 云函数(Serverless)

一、 背景与挑战

将 Java SSO SDK 迁移至 Python 云函数(Serverless)时,我们面临三大挑战:

  1. 环境限制
    云函数无法安装 rsa、pyjwt等第三方库,必须使用原生代码。
  2. 非标协议
    JWT 签名逻辑与标准规范不一致,直接导致服务端报 Object reference空指针错误。
  3. 参数陷阱
    跳转参数名特定为 return_url,且对 URL 编码有严格要求。

二、 核心算法流程图解

1. 非标准 JWT 生成流程(关键差异)

标准 JWT 是对 "UrlSafeBase64" 字符串进行签名,而 对 "StandardBase64" 字符串进行签名。这是导致验签失败的根本原因。

2. 纯 Python 实现 RSA 签名(零依赖)

为了摆脱对第三方库的依赖,我们利用 Python 内置的大数运算 pow(m, d, n)手动实现了 PKCS#1 v1.5 签名


三、 核心实力代码 (Python)

以下代码剔除了辅助工具类,保留了解决报错的关键逻辑:

  1. _generate_id_token
    复刻了非标 JWT 签名流程。
  2. execute
    处理了 return_url、KID 生成及 Payload 结构嵌套问题。
    import json, time, base64, hashlibclass SsoTokenService:    ENCODING = "utf-8"    # 厂商公钥 (用于生成KID)    PUBLIC_KEY_STR = """-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"""    # 厂商私钥 (用于签名)    PRIVATE_KEY_STR = """-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"""    def execute(self, **kwargs):        """主执行逻辑"""        # 1. 处理跳转地址 (关键点:参数名必须是 return_url)        # 优先使用传入参数,否则使用硬编码的已验证长链接兜底        DEFAULT_URL = "https%3A%2F%2Fwww.example.com%2Fportal..."         target_url = kwargs.get("return_url") or DEFAULT_URL        # (此处省略防止二次编码的逻辑...)        # 2. 构造 Payload (关键点:类型匹配与结构嵌套)        # Java端要求 exp/iat 为 String,且 cls 必须嵌套        payload_dict = {            "iss": kwargs.get("iss", "default_iss"),            "sub": kwargs.get("sub", "user@email.com"),            "aud": kwargs.get("aud", "tenant_id"),            "exp": str(int(time.time()) + 900), # 转String            "iat": str(int(time.time())),       # 转String            "cls": kwargs.get("cls", {"appid": "100"}) # 必须嵌套        }        # 3. 生成 KID (关键点:保留PEM头尾标签进行Hash)        kid = hashlib.sha256(self.PUBLIC_KEY_STR.replace("\n", "").replace("\r", "").encode()).hexdigest()        header_json = json.dumps({"alg": "RS256", "kid": kid}, separators=(',', ':'))        payload_json = json.dumps(payload_dict, separators=(',', ':'))        # 4. 生成 Token (调用非标逻辑)        id_token = self._generate_id_token(header_json, payload_json)        return f"https://sso.example.com/Auth?id_token={id_token}&return_url={target_url}"    def _generate_id_token(self, header_str, payload_str):        """        [核心算法] 复刻 Java 非标 JWT 生成流程        """        # 1. 先转为【标准】Base64 (含 + / =)        std_h = base64.b64encode(header_str.encode()).decode()        std_p = base64.b64encode(payload_str.encode()).decode()        # 2. 拼接 (注意:对标准Base64串进行拼接)        sign_content = f"{std_h}.{std_p}"        # 3. 签名 (得到标准Base64签名)        std_sig = self._sign_rsa_manual(sign_content)        # 4. 最后统一转为 URL-Safe (替换字符,去等号)        to_safe = lambda s: s.replace('+', '-').replace('/', '_').replace('=', '')        return f"{to_safe(std_h)}.{to_safe(std_p)}.{to_safe(std_sig)}"    def _sign_rsa_manual(self, content):        """        [零依赖] 纯 Python 实现 RSA SHA256 签名 (s = m^d mod n)        """        # (此处省略 ASN.1 解析 n, d 的代码...)        # n, d = self._parse_pem(self.PRIVATE_KEY_STR)        # 1. SHA256 摘要        msg_hash = hashlib.sha256(content.encode()).digest()        # 2. PKCS#1 v1.5 填充 (含 OID)        oid = b'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'        # padding 逻辑...        # 3. 核心数学运算        m_int = int.from_bytes(padding, 'big')        s_int = pow(m_int, d, n)         return base64.b64encode(s_int.to_bytes(key_len, 'big')).decode()

四、 避坑

  1. 协议对齐
    不要盲目信任通用库(如 pyjwt),当服务端抛出空指针异常时,极大概率是 Base64 编码方式或 JSON 结构不匹配。
  2. 拥抱底层
    在 FaaS 环境下,手写 RSA 算法虽然硬核,但能带来最佳的兼容性和零依赖体验。
  3. 细节决定成败
    参数名(return_urlvs redirect_url)、URL 编码次数、KID Hash 规则,任何一个细节的偏差都会导致 SSO 链路断裂。

最新文章

随机文章