Python中的WebAuthn/FIDO协议实现:无密码身份验证的底层细节
大家好,今天我们来深入探讨WebAuthn/FIDO协议在Python中的实现,并着重关注无密码身份验证的底层细节。WebAuthn/FIDO是目前最先进、最安全的Web身份验证标准之一,它允许用户使用各种硬件认证器(如指纹识别器、USB安全密钥等)进行身份验证,从而摆脱对传统密码的依赖。
1. WebAuthn/FIDO协议概述
WebAuthn(Web Authentication)是由W3C发布的Web API规范,它定义了Web应用程序如何与认证器进行交互。FIDO(Fast Identity Online)联盟则定义了具体的认证协议,例如CTAP(Client to Authenticator Protocol),用于客户端和认证器之间的通信。
WebAuthn/FIDO协议的核心在于使用公钥密码学进行身份验证。用户设备上的认证器会生成一对密钥:私钥保存在认证器内部,用于签名;公钥则注册到服务器。当用户需要进行身份验证时,服务器会生成一个挑战(challenge),发送给客户端。客户端将这个挑战传递给认证器,认证器使用私钥对挑战进行签名,并将签名返回给服务器。服务器使用之前注册的公钥验证签名,如果验证通过,则认为用户身份验证成功。
2. WebAuthn/FIDO协议的关键流程
WebAuthn/FIDO协议主要包含两个关键流程:注册(Registration)和认证(Authentication)。
- 注册 (Registration): 用户首次使用WebAuthn进行身份验证时,需要先注册一个新的认证器。这个过程涉及到生成密钥对,并将公钥注册到服务器。
- 认证 (Authentication): 用户进行身份验证时,客户端向认证器请求签名,服务器验证签名以确认用户身份。
3. Python中的WebAuthn库:fido2
在Python中,fido2库是实现WebAuthn/FIDO协议的首选工具。它提供了对CTAP1/CTAP2协议的完整支持,并允许开发者方便地集成WebAuthn到他们的Web应用程序中。
安装 fido2 库:
pip install fido2
4. 注册流程的Python实现
下面我们通过一个简单的例子来演示如何在Python中实现WebAuthn的注册流程。
from fido2.server import Fido2Server, RelyingParty
from fido2.client import ClientData, AttestationObject
from fido2 import cbor
import base64
import os
from typing import Dict, Any
# 服务器端配置
RP_ID = "example.com" # Relying Party ID, 你的域名
RP_NAME = "Example WebAuthn Site" # Relying Party Name, 你的网站名称
ORIGIN = "https://example.com" # 网站的Origin,必须是HTTPS
# 初始化Fido2Server
rp = RelyingParty(RP_ID, RP_NAME)
server = Fido2Server(rp)
# 模拟用户数据库,存储用户的凭据信息
user_credentials: Dict[str, Dict[str, Any]] = {}
def start_registration(user_id: str, user_name: str) -> Dict[str, Any]:
"""
开始注册流程,生成注册选项。
"""
attestation_options = server.register_begin(
{"id": user_id.encode("utf-8"), "name": user_name},
attestation="direct", # 设置为 "direct" 以获取认证语句
user_verification="preferred" # 设置用户验证方式
)
return attestation_options
def finish_registration(user_id: str, registration_response: Dict[str, Any]) -> bool:
"""
完成注册流程,验证认证结果,并将凭据信息存储到数据库。
"""
try:
client_data = ClientData(registration_response["clientDataJSON"])
attestation_object = AttestationObject(registration_response["attestationObject"])
server.register_complete(
client_data,
attestation_object,
[user_credentials[user_id]["credential_id"] for user_id in user_credentials if "credential_id" in user_credentials[user_id]]
)
credential_id = base64.b64encode(attestation_object.credential_data.credential_id).decode("utf-8")
# 将凭据信息存储到数据库
user_credentials[user_id] = {
"credential_id": credential_id,
"public_key": base64.b64encode(attestation_object.credential_data.public_key).decode("utf-8"),
"sign_count": 0 # 初始化签名计数器
}
return True
except Exception as e:
print(f"注册失败: {e}")
return False
# 模拟客户端交互
user_id = "user123"
user_name = "John Doe"
# 1. 客户端请求注册选项
registration_options = start_registration(user_id, user_name)
print("注册选项:", registration_options)
# 模拟客户端操作:将注册选项发送给认证器,认证器生成密钥对并返回认证结果
# (这里需要模拟客户端与认证器的交互,例如使用JavaScript的WebAuthn API。这里只演示服务端代码)
# 假设客户端返回的认证结果如下:
registration_response = {
"clientDataJSON": base64.b64encode(b'{"type": "webauthn.create", "challenge": "xxx", "origin": "https://example.com"}').decode("utf-8"), # 替换为真实的clientDataJSON
"attestationObject": base64.b64encode(b'xxx').decode("utf-8") # 替换为真实的attestationObject
}
# 2. 客户端将认证结果发送给服务器
registration_result = finish_registration(user_id, registration_response)
if registration_result:
print("注册成功!")
print("用户凭据:", user_credentials[user_id])
else:
print("注册失败!")
代码解释:
Fido2Server:用于处理WebAuthn协议的核心类。它负责生成注册和认证选项,并验证认证结果。RelyingParty:表示Web应用程序本身。需要提供Relying Party ID(通常是域名)和Relying Party Name。start_registration:生成注册选项。这些选项包含了服务器的配置信息,以及一些安全参数。finish_registration:验证认证结果。它会解析客户端返回的clientDataJSON和attestationObject,并使用server.register_complete方法进行验证。验证成功后,将凭据信息存储到数据库。user_credentials:模拟用户数据库。实际应用中,你需要使用真正的数据库来存储用户的凭据信息。registration_response:模拟客户端返回的认证结果。实际应用中,你需要使用JavaScript的WebAuthn API从客户端获取这个结果。
5. 认证流程的Python实现
下面我们通过一个简单的例子来演示如何在Python中实现WebAuthn的认证流程。
from fido2.server import Fido2Server, RelyingParty
from fido2.client import ClientData, AttestationObject, AuthenticatorData
from fido2 import cbor
import base64
import os
from typing import Dict, Any
# 服务器端配置 (与注册流程相同)
RP_ID = "example.com"
RP_NAME = "Example WebAuthn Site"
ORIGIN = "https://example.com"
# 初始化Fido2Server
rp = RelyingParty(RP_ID, RP_NAME)
server = Fido2Server(rp)
# 模拟用户数据库 (与注册流程相同)
user_credentials: Dict[str, Dict[str, Any]] = {
"user123": {
"credential_id": "eyJoZWFkZXIiOnsiYWxnIjoiRVMyNTYiLCJ0eXAiOiJqd2sifSwiYWxnIjoiRVMyNTYiLCJ0eXAiOiJqd2siLCJ4IjoiWV93ajJ3aVlwUlVVZ09VZU9wT192eV9rR19lVzB5eU94Qnl2VnJ0Z01uZyIsInkiOiJ6dG5QY0x1b014c1FjV3d3NzN5d21mRkJxRzV0T1l2R0c5c19sN3J3Z28iLCJrdHkiOiJFQyIsImNydiI6IlAtMjU2In0=", # 替换为实际注册时存储的 credential_id
"public_key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWd32J27Xv6gT3lWl27J22J4Y3w9dC+o57x4R7d3+0Y7d7/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ3/eJ
更多IT精英技术系列讲座,到智猿学院