metaMask login

This commit is contained in:
hujiabin 2024-06-29 16:52:33 +08:00
parent 5fce327ce6
commit a32a4e4e39
7 changed files with 767 additions and 568 deletions

View File

@ -43,6 +43,7 @@
"vue-splitpane": "1.0.4",
"vuedraggable": "2.20.0",
"vuex": "3.1.0",
"web3": "^1.7.4",
"xlsx": "0.14.1"
},
"devDependencies": {

View File

@ -7,6 +7,13 @@ export function login(data) {
data
})
}
export function metamaskLogin(data) {
return request({
url: '/user/metamask-login',
method: 'post',
data
})
}
export function getInfo(token) {
return request({
@ -21,3 +28,11 @@ export function logout() {
method: 'get'
})
}
export function getNonce(account) {
return request({
url: '/user/getNonce?account=' + account,
method: 'get'
})
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718869327969" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5949" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M517.77536 972.8c133.98016-30.19776 211.70176-70.42048 301.2608-156.3136 29.32736-28.11904 174.04928-63.68256 190.4128-100.83328 23.72608-53.8624-58.2144-115.72224-58.2144-178.40128 0-240.54784-194.06848-435.5584-433.4592-435.5584-239.38048 0-433.44896 195.01056-433.44896 435.5584 0 68.21888-86.13888 124.14976-58.3168 181.62688 15.28832 31.56992 164.1472 73.216 189.51168 97.60768C305.08032 902.68672 383.55968 942.60224 517.77536 972.8z" fill="#FFD797" p-id="5950"></path><path d="M908.77952 65.5872c-0.88064 1.40288 79.0528 276.7872 32.68608 379.32032a439.0912 439.0912 0 0 1 9.76896 92.34432c0 62.6688 81.94048 124.54912 58.2144 178.40128-1.71008 3.8912-4.88448 7.74144-9.216 11.55072-146.59584-39.44448-297.6768-4.31104-453.25312 105.41056l-5.64224 4.00384H494.592C336.24064 723.2 182.55872 686.92992 33.536 727.78752c-3.54304-3.0208-6.11328-5.9904-7.5264-8.9088-27.82208-57.47712 58.3168-113.408 58.3168-181.62688 0-27.4944 2.53952-54.39488 7.38304-80.47616-59.62752-90.97216 27.136-389.7344 26.22464-391.18848 81.39776-14.41792 159.06816 8.76544 233.03168 69.5296a430.40768 430.40768 0 0 1 166.8096-33.42336 430.44864 430.44864 0 0 1 160.94208 31.01696C751.77984 73.728 828.4672 51.36384 908.77952 65.5872z" fill="#FF6F24" p-id="5951"></path><path d="M283.1872 315.51488c31.10912-104.9088-23.97184-186.24512-165.25312-244.0192 1.024 1.65888-77.7216 340.93056 28.89728 383.11936-3.51232 2.92864 41.94304-43.42784 136.35584-139.10016z m461.48608 0c-31.10912-104.9088 23.97184-186.24512 165.24288-244.0192-1.024 1.65888 77.7216 340.93056-28.89728 383.11936 3.52256 2.92864-41.9328-43.42784-136.3456-139.10016z" fill="#922101" opacity=".574" p-id="5952"></path><path d="M647.69024 629.00224c-0.21504-60.29312 44.56448-83.94752 134.3488-70.95296-2.78528 3.072 6.78912 44.93312-40.51968 69.15072-11.264 5.76512-42.53696 6.36928-93.82912 1.80224z m-251.46368 0c0.22528-60.29312-44.55424-83.94752-134.33856-70.95296 2.78528 3.072-6.79936 44.93312 40.50944 69.15072 11.264 5.76512 42.53696 6.36928 93.82912 1.80224z" fill="#350000" p-id="5953"></path><path d="M743.10656 565.72928l0.04096 0.1536c0.78848 3.42016 1.20832 6.99392 1.20832 10.6496 0 23.4496-17.14176 42.82368-39.424 46.00832-3.42016 0.1536-7.2192 0.21504-11.39712 0.18432a45.93664 45.93664 0 0 1-36.7104-26.61376c10.25024-24.832 39.0144-34.95936 86.28224-30.38208z m-459.84768 0l-0.04096 0.1536a47.13472 47.13472 0 0 0-1.20832 10.6496c0 23.4496 17.14176 42.82368 39.424 46.00832 3.40992 0.1536 7.2192 0.21504 11.39712 0.18432a45.93664 45.93664 0 0 0 36.7104-26.61376c-10.26048-24.832-39.0144-34.95936-86.28224-30.38208z" fill="#923320" p-id="5954"></path><path d="M328.6528 574.72c-4.12672 10.24-6.25664 17.43872-6.38976 21.58592-0.23552 6.89152 1.8944 12.5952 6.38976 17.1008 0.768 0.54272 6.44096-5.30432 6.144-17.87904-0.1024-3.72736-2.1504-10.6496-6.144-20.80768z m372.72576 0c-4.12672 10.24-6.2464 17.43872-6.38976 21.58592-0.22528 6.89152 1.90464 12.5952 6.38976 17.1008 0.77824 0.54272 6.44096-5.30432 6.144-17.87904-0.09216-3.72736-2.14016-10.6496-6.144-20.80768z" fill="#FFFFFF" opacity=".632" p-id="5955"></path><path d="M520.42752 863.8464c37.24288 0 67.4304-27.81184 67.4304-62.1056 0-34.304-134.8608-34.304-134.8608 0s30.19776 62.1056 67.4304 62.1056z" fill="#040C12" p-id="5956"></path><path d="M520.15104 152.02304c8.86784 146.97472 40.57088 238.83776 95.10912 275.57888l2.19136 1.4336-0.24576 0.07168c-6.42048 2.53952-71.46496 73.5744-96.80896 138.2912-0.0512 0.08192-0.09216 0-0.12288-0.26624-0.04096 0.256-0.08192 0.34816-0.12288 0.26624-25.35424-64.7168-90.38848-135.75168-96.8192-138.2912l-0.24576-0.07168 2.2016-1.4336c53.71904-36.1984 85.1968-125.87008 94.45376-269.03552l0.4096-6.5536z" fill="#C74D22" p-id="5957"></path><path d="M735.15008 721.8688c49.55136 0 89.72288-17.17248 89.72288-38.3488s-40.17152-38.3488-89.72288-38.3488c-49.5616 0-89.72288 17.17248-89.72288 38.3488s40.17152 38.3488 89.72288 38.3488z m-443.89376 0c49.5616 0 89.72288-17.17248 89.72288-38.3488s-40.17152-38.3488-89.72288-38.3488-89.72288 17.17248-89.72288 38.3488 40.17152 38.3488 89.72288 38.3488z" fill="#C53028" fill-opacity=".3" p-id="5958"></path></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -7,6 +7,7 @@ const getters = {
cachedViews: state => state.tagsView.cachedViews,
token: state => state.user.token,
name: state => state.user.name,
nonce: state => state.user.nonce,
introduction: state => state.user.introduction,
roles: state => state.user.roles,
permission_routes: state => state.permission.routes,

View File

@ -6,7 +6,8 @@ const state = {
token: getToken(),
name: '',
introduction: '',
roles: []
roles: [],
nonce: ''
}
const mutations = {
@ -21,6 +22,9 @@ const mutations = {
},
SET_ROLES: (state, roles) => {
state.roles = roles
},
SET_NONCE: (state, nonce) => {
state.nonce = nonce
}
}
@ -97,6 +101,13 @@ const actions = {
resolve()
})
},
// get nonce
getNonce({ commit }, nonce) {
return new Promise(resolve => {
commit('SET_NONCE', nonce)
resolve()
})
},
// dynamically modify permissions
async changeRoles({ commit, dispatch }, role) {

View File

@ -1,22 +1,58 @@
<template>
<div class="social-signup-container">
<div class="sign-btn" @click="wechatHandleClick('wechat')">
<span class="wx-svg-container"><svg-icon icon-class="wechat" class="icon" /></span>
WeChat
</div>
<div class="sign-btn" @click="tencentHandleClick('tencent')">
<span class="qq-svg-container"><svg-icon icon-class="qq" class="icon" /></span>
QQ
<!-- <div class="sign-btn" @click="wechatHandleClick('wechat')">-->
<!-- <span class="wx-svg-container"><svg-icon icon-class="wechat" class="icon" /></span>-->
<!-- WeChat-->
<!-- </div>-->
<!-- <div class="sign-btn" @click="tencentHandleClick('tencent')">-->
<!-- <span class="qq-svg-container"><svg-icon icon-class="qq" class="icon" /></span>-->
<!-- QQ-->
<!-- </div>-->
<div class="sign-btn" @click="marketHandleClick('MetaMask')">
<span class="qq-svg-container"><svg-icon icon-class="MetaMask" class="icon" /></span>
Market
</div>
</div>
</template>
<script>
// import openWindow from '@/utils/open-window'
import Web3 from 'web3'
import { getNonce, metamaskLogin } from '@/api/user'
export default {
name: 'SocialSignin',
data() {
return {
provider: '',
web3: '',
chainId: '',
account: '',
redirect: undefined,
otherQuery: {}
}
},
watch: {
$route: {
handler: function(route) {
const query = route.query
if (query) {
this.redirect = query.redirect
this.otherQuery = this.getOtherQuery(query)
}
},
immediate: true
}
},
methods: {
getOtherQuery(query) {
return Object.keys(query).reduce((acc, cur) => {
if (cur !== 'redirect') {
acc[cur] = query[cur]
}
return acc
}, {})
},
wechatHandleClick(thirdpart) {
alert('ok')
// this.$store.commit('SET_AUTH_TYPE', thirdpart)
@ -32,6 +68,131 @@ export default {
// const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/auth-redirect')
// const url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirect_uri
// openWindow(url, thirdpart, 540, 540)
},
async marketHandleClick(thirdpart) {
if (!this.hasMetamask()) {
this.$message({
message: '请先安装MetaMask插件',
type: 'error',
duration: 1200
})
return
}
this.provider = await this.connectMetaMask()
if (!this.provider) {
return
}
this.web3 = new Web3(this.provider)
this.chainId = await this.web3.eth.getChainId()
const accounts = await this.web3.eth.getAccounts()
if (accounts && accounts.length > 0) {
this.account = accounts[0]
}
await getNonce(this.account).then(response => {
if (response.code === 0) {
this.$store.dispatch('user/getNonce', response.data)
} else {
this.$message({
message: response.message,
type: 'error',
duration: 1200
})
return
}
}).catch((err) => {
console.log('Api getNonce Error:' + err)
})
await this.metaMaskLogin()
},
hasMetamask() {
if (typeof window.ethereum !== 'undefined') {
return true
} else {
return false
}
},
connectMetaMask() {
var provider = window.ethereum
try {
provider.request({ method: 'eth_requestAccounts' })
} catch (error) {
if (error.code === -32002) {
throw new Error('MeatMask not login, Open MeatMask and login first')
} else {
throw new Error('User Rejected')
}
}
return provider
},
async metaMaskLogin() {
const account = this.account
const net_id = this.chainId
const nonce = this.$store.getters.nonce
const tips = 'This signature is only used for verify your account'
const signMsg = {
tips,
nonce
}
const EIP721_DOMAIN_DATA = [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' }
]
const signObj = {
types: {
EIP712Domain: EIP721_DOMAIN_DATA,
set: [
{ name: 'tips', type: 'string' },
{ name: 'nonce', type: 'string' }
]
},
primaryType: 'set',
domain: {
name: 'Auth',
version: '1'
},
message: signMsg
}
const signature = await this.signData(signObj, account)
const authData = {
account,
nonce,
signature,
tips,
net_id
}
metamaskLogin(authData).then(response => {
this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
console.log(response)
}).catch((err) => {
console.log('Api getNonce Error:' + err)
})
},
async signData(signObj, signer) {
const msgParams = JSON.stringify(signObj)
const from = signer
const params = [from, msgParams]
const result = await this.sendCmd(
'eth_signTypedData_v4',
params,
from
)
return result.result
},
async sendCmd(method, params, from) {
return new Promise((resolve, reject) => {
this.web3.currentProvider.sendAsync({
method,
params,
from
}, async function(err, result) {
if (err) {
reject && reject(err)
return
}
resolve && resolve(result)
})
})
}
}
}

View File

@ -45,16 +45,25 @@
</el-form-item>
</el-tooltip>
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>
<div style="position:relative;margin-top: 30px">
<el-button class="thirdparty-button" type="primary" @click="showDialog=true">
Or connect with
</el-button>
</div>
</el-form>
<el-dialog title="Or connect with" :visible.sync="showDialog">
<social-sign />
</el-dialog>
</div>
</template>
<script>
import { validUsername } from '@/utils/validate'
import SocialSign from './components/SocialSignin'
export default {
name: 'Login',
components: {},
components: { SocialSign },
data() {
const validateUsername = (rule, value, callback) => {
if (!validUsername(value)) {