学术搜索引擎:高效检索学术资源的关键工具与应用指南
2026/1/19 16:21:46
| 问题 | 说明 |
|---|
- 中心化风险| 平台掌握用户身份,可滥用或被攻破(如 Facebook 数据泄露)
- 重复 KYC| 每个新服务都要重新提交身份证、住址等
- 信息过曝| 验证年龄需提交完整出生日期
- 格式:
did:example:123456abcdef- 特性:用户创建、用户控制、无需注册机构
- 存储:区块链 / IPFS / 去中心化存储
- 由Issuer(发行方)签发(如政府、银行)
- 包含Claims(声明)+Issuer 签名
- 示例:
{ "@context": ["https://www.w3.org/2018/credentials/v1"], "type": ["VerifiableCredential", "AgeCredential"], "issuer": "did:example:gov123", "issuanceDate": "2025-01-01T00:00:00Z", "credentialSubject": { "id": "did:example:user456", "birthDate": "2000-05-20" }, "proof": { /* 签名 */ } }
- 用户从多个 VC 中选择性披露信息
- 可附加零知识证明实现最小化披露
信任模型转变:
从“相信平台”→“验证密码学证明”
[Vue 前端] │ ├── 1. 生成挑战(nonce) ←── [Flask 后端] │ ├── 2. 用户钱包签名 nonce + DID │ (或生成 ZK 证明) │ └── 3. 提交 VP → [Flask 验证] → 登录成功| 功能 | 技术 | 说明 |
|---|
- DID 管理|
ethr-did(以太坊兼容) | 支持 EVM 链- VC 签发/验证|
veramo(JavaScript SDK) | W3C 兼容- ZK 电路| Circom + SnarkJS | 浏览器友好
- 钱包连接| WalletConnect v2 | 支持多钱包
- 后端验证| Python
py_did+ 自定义 ZK 验证器 |
# routes/auth.py from flask import session import secrets @app.route('/auth/challenge') def get_challenge(): challenge = secrets.token_urlsafe(32) session['challenge'] = challenge return jsonify({"challenge": challenge})<script setup> import { EthereumProvider } from '@walletconnect/ethereum-provider' const connectWallet = async () => { const provider = await EthereumProvider.init({ projectId: 'YOUR_WALLETCONNECT_ID', chains: [1], methods: ['eth_requestAccounts', 'personal_sign'] }) await provider.enable() window.ethereum = provider } const loginWithDID = async () => { // 1. 获取挑战 const res = await fetch('/auth/challenge') const { challenge } = await res.json() // 2. 获取用户地址(作为 DID) const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }) const did = `did:pkh:eip155:1:${accounts[0].toLowerCase()}` // 3. 签名挑战 const signature = await window.ethereum.request({ method: 'personal_sign', params: [challenge, accounts[0]] }) // 4. 提交 VP await fetch('/auth/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ did, challenge, signature }) }) } </script># utils/did_auth.py from ecdsa import VerifyingKey, SECP256k1 import sha3 from eth_utils import to_checksum_address def verify_did_signature(did: str, challenge: str, signature: str) -> bool: # 1. 从 DID 提取地址 if not did.startswith('did:pkh:eip155:1:'): return False address = to_checksum_address('0x' + did.split(':')[-1]) # 2. 构造签名消息(EIP-191) message = f"\x19Ethereum Signed Message:\n{len(challenge)}{challenge}" message_hash = sha3.keccak_256(message.encode()).digest() # 3. 恢复公钥 sig = bytes.fromhex(signature[2:]) # 去掉 0x vk = VerifyingKey.from_signature( sig[:-1], # 去掉恢复 ID message_hash, curve=SECP256k1, hashfunc=sha3.keccak_256 ) recovered_addr = to_checksum_address(vk.to_string('compressed')[1:]) return recovered_addr == address优势:
- 无密码、无 OAuth 依赖
- 用户用已有钱包即可登录
用户想证明自己 ≥18 岁,但不想透露具体生日。
// circuits/age_check.circom pragma circom 2.0.0; template AgeCheck() { signal input birthYear; // 用户私有输入 signal input currentYear; // 公共输入 signal output isAdult; // 公共输出 signal diff; diff <== currentYear - birthYear; isAdult <== diff >= 18 ? 1 : 0; } component main = AgeCheck();# 编译电路 circom age_check.circom --r1cs --wasm --sym # 启动 SnarkJS 服务(用于可信设置) snarkjs groth16 setup age_check.r1cs pot17_final.ptau age_check_0000.zkey// zk/age_prover.ts import { groth16 } from 'snarkjs' import ageCheckWasm from './age_check.wasm?url' import zkey from './age_check_0000.zkey?url' export async function generateAgeProof(birthYear: number, currentYear: number = 2025) { const { proof, publicSignals } = await groth16.fullProve( { birthYear, currentYear }, ageCheckWasm, zkey ) return { proof, publicSignals // [isAdult (0/1), currentYear] } }# utils/zk_verifier.py import json from py_ecc.bn128 import bn128_curve as curve from snarkjs import groth16_verify # 假设有 Python 绑定 def verify_age_proof(proof_json: dict, public_signals: list) -> bool: with open('verification_key.json') as f: vkey = json.load(f) return groth16_verify(vkey, public_signals, proof_json)隐私保障:
- 后端只知道
isAdult=1,不知道birthYear=2000- 证明不可伪造(基于椭圆曲线密码学)
# services/vc_issuer.py from veramo import create_verifiable_credential def issue_kyc_vc(user_did: str, name: str, birth_date: str): credential = { "@context": ["https://www.w3.org/2018/credentials/v1"], "type": ["VerifiableCredential", "KYCCredential"], "issuer": BANK_DID, "issuanceDate": datetime.utcnow().isoformat() + "Z", "credentialSubject": { "id": user_did, "name": name, "birthDate": birth_date } } return create_verifiable_credential(credential, issuer_private_key)# routes/verify_kyc.py from veramo import verify_credential @app.route('/verify/kyc', methods=['POST']) def verify_kyc(): vp = request.json # Verifiable Presentation # 1. 验证 VC 签名 if not verify_credential(vp['verifiableCredential']): return jsonify({"error": "Invalid VC"}), 400 # 2. 检查 DID 是否在许可列表 if vp['holder']['id'] not in allowed_dids: return jsonify({"error": "Unauthorized"}), 403 return jsonify({"status": "verified"})优势:
- 用户一次 KYC,多处复用
- 平台无需存储敏感个人信息
<template> <button @click="login" :disabled="loading"> {{ loading ? 'Connecting...' : 'Login with Wallet' }} </button> </template> <script setup> import { useDIDAuth } from '@/composables/didAuth' const { login, loading } = useDIDAuth() </script>// composables/didAuth.ts import { ref } from 'vue' export function useDIDAuth() { const loading = ref(false) const login = async () => { loading.value = true try { await connectWallet() const { challenge } = await fetchChallenge() const { did, signature } = await signChallenge(challenge) await submitVP({ did, challenge, signature }) // 登录成功,跳转 router.push('/dashboard') } finally { loading.value = false } } return { login, loading } }用户体验:与传统“微信登录”按钮无异,但更安全、更隐私。
| 设备 | 电路复杂度 | 生成时间 |
|---|
- 现代笔记本| AgeCheck(简单) | ~800ms
- iPhone 14| 同上 | ~1.2s
- 低端安卓| 同上 | ~2.5s
优化:
- 预加载 WASM 模块
- 使用 Web Worker 避免阻塞 UI
| 钱包 | DID 支持 | VC 支持 |
|---|
- MetaMask| ✅ (via Snap) | ❌
- Polygon ID Wallet| ✅ | ✅
- Microsoft Entra Verified ID| ✅ | ✅
策略:优先支持 Polygon ID,降级到 DID 签名。
@mattrglobal/bbs-signaturesdid:pkh:eip155:1:0x...→alice.eth可验证网络不是加密乌托邦,而是下一代互联网信任基础设施。