socialgekon.com
  • Põhiline
  • Finantsprotsessid
  • Redigeerimine
  • Mobiilne
  • Ui Disain
Tagumine Ots

Ühe klõpsuga Logi sisse Blockchainiga: MetaMask õpetus

Online-kasutajad muutuvad üha vastupidavam tavapärastele e-posti / parooli registreerimisprotsessidele. Ühe klõpsuga sotsiaalse sisselogimise funktsioon Facebooki, Google'i või GitHubi kaudu osutub palju soovitavamaks alternatiiviks. Sellega kaasneb aga kompromiss.

Sotsiaalse meedia sisselogimise integratsiooni plussid:

  • Enam pole tülikat vormitäitmist.
  • Veel üht kasutajanime / parooli paari pole vaja meeles pidada.
  • Kogu protsess võtab minutite asemel sekundeid.

Sotsiaalse meedia sisselogimise integreerimise miinused:



  • Kuna kasutaja teave on laetud välistelt teenusepakkujatelt, tekitab see tohutut privaatsusprobleemi selle pärast, kuidas pakkujad kõiki neid isikuandmeid kasutavad. Näiteks kirjutamise ajal seisab Facebook silmitsi andmete privaatsuse probleemid .

Selles artiklis tutvustatakse uut sisselogimismeetodit plokiahela arendamine : Ühe klõpsuga krüptograafiliselt turvaline sisselogimisvoog, kasutades MetaMask laiendus , kusjuures kõik andmed on salvestatud meie endi tagaküljele. Nimetame seda: 'Logi sisse MetaMaskiga'.

Tuhat sõna väärt pilt on siin sisselogimisvoo demo, mille me ehitame:

Animatsioon, mis näitab MetaMaski sisselogimisdemot.

Näeb hea välja? Alustame!

Kuidas kasutada metamaski ühe klõpsuga sisselogimisvoo jaoks

Põhiidee on see, et konto omandiõigust on krüptograafiliselt lihtne tõendada, allkirjastades osa privaatset võtit kasutades. Kui teil õnnestub allkirjastada täpne andmefail, mille on genereerinud meie tagumine ots, loeb tagumine ots teid selle avaliku aadressi omanikuks. Seetõttu saame ehitada sõnumiallkirjastamise põhise autentimismehhanismi, mille identifikaatoriks on kasutaja avalik aadress.

Kui see ei tundu selge, on see kõik korras, sest me selgitame seda järk-järgult:

  • MetaMaski brauseri laiendus
  • Kuidas sisselogimisvoog töötab
  • Miks sisselogimisvoog töötab?
  • Ehitame selle koos
  • See on täna tootmisvalmis
  • Puudused mobiilseadmes

Pange tähele, et samal ajal kui kasutame Ethereum blockchain (MetaMask, Ethereumi avalikud aadressid), ei vaja see sisselogimisprotsess tegelikult plokiahelat: see vajab ainult oma krüptograafia funktsioone. Nagu öeldud, muutumas MetaMask nii populaarne laiendus , tundub nüüd hea aeg selle sisselogimisvoo tutvustamiseks.

MetaMaski brauseri laiendus

Kui teate juba, mis on MetaMask, jätke see jaotis julgelt vahele.

MetaMask on brauseri pistikprogramm, saadaval kui MetaMask Chrome'i laiendus või Firefoxi lisandmoodul . Põhimõtteliselt toimib see Ethereumi rahakotina: selle installimisega saate juurdepääsu ainulaadsele Ethereumi avalikule aadressile, millega saate hakata eetreid või märke saatma ja vastu võtma.

Kuid MetaMask teeb midagi enamat kui Ethereumi rahakott. Brauserilaiendina saab see suhelda praeguse sirvitava veebilehega. Ta teeb seda, süstides JavaScripti teeki nimega web3.js igal külastataval veebisaidil. Pärast süstimist a web3 objekt on saadaval window.web3 kaudu selle veebisaidi JavaScripti koodis. Kui soovite vaadata, kuidas see objekt välja näeb, sisestage lihtsalt window.web3 Chrome'i või Firefoxi DevToolsi konsoolis, kui teil on installitud MetaMask.

Web3.js on Ethereumi plokiahela JavaScripti liides. Funktsioonid on järgmised:

  • Hankige keti uusim plokk (web3.eth.getBlockNumber)
  • Kontrollige MetaMaskis praegust aktiivset kontot (web3.eth.coinbase)
  • Hankige mis tahes konto saldo (web3.eth.getBalance)
  • Tehingute saatmine (web3.eth.sendTransaction)
  • Kirjutage sõnumid alla praeguse konto privaatvõtmega (web3.personal.sign)
  • … Ja palju rohkem

Kui MetaMask on installitud, pääseb mis tahes esiotsa kood kõigile neile funktsioonidele juurde ja suhelda plokiahelaga . Neid nimetatakse dapps või DApps (detsentraliseeritud rakenduste puhul - mõnikord isegi stiilis “pApps”).

Seotud DAppi arendusega: Ajast lukustatud rahakotid: sissejuhatus Ethereumi nutikatesse lepingutesse

Enamik web3.js-i funktsioone on lugemisfunktsioonid (saada blokeerimine, tasakaalu hankimine jne) ja web3 annab kohe vastuse. Kuid mõned funktsioonid (näiteks web3.eth.sendTransaction ja web3.personal.sign) vajavad jooksvat kontot, et allkirjastada mõned andmed oma privaatvõtmega. Need funktsioonid käivitavad MetaMaskil kinnitusekraani kuvamise, et kontrollida, kas kasutaja teab, millele ta alla kirjutab.

Vaatame, kuidas MetaMaskit selleks kasutada. Lihtsa testi tegemiseks kleepige DevToolsi konsooli järgmine rida:

web3.personal.sign(web3.fromUtf8('Hello from ApeeScape!'), web3.eth.coinbase, console.log);

See käsk tähendab: allkirjastage minu sõnum, teisendatuna utf8-st heksiks, coinbase'i kontoga (s.o jooksev konto) ja tagasihelistamisena printige allkiri. Ilmub MetaMask hüpikaken ja kui te sellele alla kirjutate, prinditakse allkirjastatud kiri.

MetaMask kinnitus hüpikaken

Kasutame web3.personal.sign meie sisselogimisvoos.

Viimane märkus selle jaotise kohta: MetaMask süstib teie praegusesse brauserisse web3.js, kuid tegelikult on ka muid eraldiseisvaid brausereid, mis süstivad ka veebi 3.js, näiteks Udu , näiteks. Kuid minu arvates pakub MetaMask täna parimat UX-i ja lihtsamat üleminekut tavakasutajatele dappide uurimiseks.

Kuidas sisselogimisvoog töötab

Alustame kuidas . The kuidas veenan teid loodetavasti, et see on turvaline, nii et ma hoian seda miks osa lühike.

Nagu ülevaates öeldud, unustame plokiahela. Meil on traditsiooniline Web 2.0 klient-server RESTful arhitektuur. Teeme ühe eelduse: et kõigil meie esilehekülge külastavatel kasutajatel on MetaMask installitud. Selle eeldusega näitame, kuidas töötab paroolideta krüptograafiliselt turvaline sisselogimisvoog.

1. samm: muutke kasutajamudelit (tagumine osa)

Esiteks meie User mudelil peab olema kaks uut kohustuslikku välja: publicAddress ja nonce. Lisaks publicAddress peab olema ainulaadne. Võite säilitada tavalised username, email ja password väljad - eriti kui soovite oma MetaMaski sisselogimist rakendada paralleelselt e-posti / parooli sisselogimisega -, kuid need on valikulised.

Registreerumisprotsess on ka veidi erinev, kuna publicAddress on registreerumisel kohustuslik väli, kui kasutaja soovib kasutada MetaMaski sisselogimist. Võite olla kindel, et kasutaja ei pea kunagi oma publicAddress käsitsi, kuna selle saab tuua web3.eth.coinbase

2. samm: looge nonces (tagumine osa)

Looge iga andmebaasi kasutaja jaoks juhuslik string nonce valdkonnas. Näiteks nonce võib olla suur juhuslik täisarv.

3. samm: kasutaja otsib oma nonce (esiosa)

Eeldades, et MetaMask on olemas, on meil oma JavaScripti esiplaani koodis juurdepääs window.web3 Seetõttu võime helistada web3.eth.coinbase praeguse MetaMaski konto avaliku aadressi saamiseks.

Kui kasutaja klõpsab sisselogimisnuppu, käivitame tagaküljele API-kõne, et hankida tema avaliku aadressiga seotud nonce. Midagi marsruudi sarnast filtri parameetriga GET /api/users?publicAddress=${publicAddress} peaks tegema. Muidugi, kuna see on autentimata API-kõne, tuleks selle tagaosa konfigureerida nii, et sellel marsruudil kuvatakse ainult avalikku teavet (sealhulgas nonce).

Kui eelmine taotlus ei anna tulemust, tähendab see, et praegune avalik aadress pole veel registreerunud. Kõigepealt peame looma uue konto POST /users kaudu, edastades publicAddress taotlusorganis. Teiselt poolt, kui on tulemus, siis salvestame selle nonce

4. samm: kasutaja allkirjastab nonce'i (esiosa)

Kui kasutajaliides saab nonce eelmise API kõne vastusena käivitab see järgmise koodi:

web3.personal.sign(nonce, web3.eth.coinbase, callback);

See palub MetaMaskil kuvada kinnituse hüpik sõnumi allkirjastamiseks. Selles hüpikus kuvatakse nonce, et kasutaja teaks, et ta ei kirjuta alla pahatahtlikele andmetele.

Kui ta selle aktsepteerib, kutsutakse tagasihelistamisfunktsioon argumendina allkirjastatud sõnumiga (nimega signature). Esipaneel teeb seejärel veel ühe API-kõne POST /api/authentication -le, läbides keha mõlema signature -ga ja publicAddress.

5. samm: allkirja kontrollimine (tagumine osa)

Kui tagumine ots saab POST /api/authentication päringu, tõmbab see kasutaja kõigepealt andmebaasi, mis vastab publicAddress esitatud taotlusorganis. Eelkõige tõmbab see seotud nonce.

Kui teil on olemas nonce, avalik aadress ja allkiri, saab tagumine ots seda teha krüptograafiliselt kontrollima et kasutaja on õigesti allkirjastanud nonce'i. Sellisel juhul on kasutaja tõendanud, et ta omab avalikku aadressi ja võime teda pidada autentseks. JWT või seansi identifikaatori saab seejärel tagastada kasutajaliidesesse.

6. toiming: muutke Nonce (tagumine osa)

Kasutaja välistamiseks sama allkirjaga uuesti sisselogimisest (juhul, kui see satub ohtu) veendume, et järgmine kord, kui sama kasutaja soovib sisse logida, peab ta uue allkirjastama. See saavutatakse teise juhusliku nonce genereerimise teel selle kasutaja jaoks ja säilitades selle andmebaasis.

Ja seal sa lähed! Nii haldame paroolita sisselogimisvoogu, mis ei allkirjasta.

Miks sisselogimisvoog töötab?

Autentimine on definitsiooni järgi tegelikult ainult konto omandiõiguse tõend. Kui tuvastate oma konto ainulaadselt avaliku aadressi abil, on selle omamise tõendamine krüptograafiliselt tühine.

Et vältida juhtumit, kus häkker saab kätte ühe kindla kirja ja selle allkirja (kuid mitte teie tegeliku privaatvõtme), sunnime sõnumit allkirjastama:

  1. Pakub tagumine ots ja
  2. Regulaarselt muutuv

Muutsime seda oma selgituses pärast igat edukat sisselogimist, kuid võis ette kujutada ka ajatemplipõhist mehhanismi.

Ülevaade MetaMaski sisselogimisvoo kuuest etapist.

Ehitame selle koos

Selles jaotises vaatan ükshaaval läbi kuus ülaltoodud sammu. Näitan mõningaid koodijuppe, kuidas seda sisselogimisvoogu nullist üles ehitada või olemasolevasse tagumisse ossa integreerida ilma liigsete pingutusteta.

Selle artikli jaoks lõin väikese demo-rakenduse. Kasutatav virn on järgmine:

  • Node.js, Express ja SQLite (Sequelize ORM-i kaudu) RESTful API juurutamiseks tagumises osas. See tagastab eduka autentimise korral JWT.
  • Reageerige esilehel ühe lehega rakendus.

Püüan kasutada võimalikult vähe raamatukogusid. Loodan, et kood on piisavalt lihtne, et saaksite selle hõlpsalt teistele tehnikakogudele teisaldada.

Kogu projekti saab näha aastal see GitHubi hoidla . Korraldatakse demo siin .

1. samm: muutke kasutajamudelit (tagumine osa)

Kohustuslik on kaks välja: publicAddress ja nonce. Algatame nonce juhusliku suure arvuna. Pärast iga edukat sisselogimist tuleks seda numbrit muuta. Lisasin ka valikulise username siin olev väli, mida kasutaja saaks muuta.

const User = sequelize.define('User', { nonce: { allowNull: false, type: Sequelize.INTEGER.UNSIGNED, defaultValue: () => Math.floor(Math.random() * 1000000) // Initialize with a random nonce }, publicAddress: { allowNull: false, type: Sequelize.STRING, unique: true, validate: { isLowercase: true } }, username: { type: Sequelize.STRING, unique: true } });

Selle lihtsustamiseks määrasin publicAddress väli väiketähtedena. Rangem rakendamine lisaks valideerimisfunktsiooni, et kontrollida, kas kõik siin olevad aadressid on kehtivad Ethereumi aadressid .

2. samm: looge nonces (tagumine osa)

Seda tehakse defaultValue() funktsioon ülaltoodud mudeli definitsioonis.

3. samm: kasutaja otsib oma nonce (esiosa)

Järgmine samm on lisada tagaküljele mõni katlakoodi kood, et käsitleda CRUD-meetodeid User mudel, mida me siin ei tee.

Üleminek kasutajaliidese koodile, kui kasutaja klõpsab sisselogimisnuppu, on meie handleClick käitleja teeb järgmist:

class Login extends Component { handleClick = () => { // --snip-- const publicAddress = web3.eth.coinbase.toLowerCase(); // Check if user with current publicAddress is already present on back end fetch(`${process.env.REACT_APP_BACKEND_URL}/users?publicAddress=${publicAddress}`) .then(response => response.json()) // If yes, retrieve it. If no, create it. .then( users => (users.length ? users[0] : this.handleSignup(publicAddress)) ) // --snip-- }; handleSignup = publicAddress => fetch(`${process.env.REACT_APP_BACKEND_URL}/users`, { body: JSON.stringify({ publicAddress }), headers: { 'Content-Type': 'application/json' }, method: 'POST' }).then(response => response.json()); }

Siit leiame aktiivse MetaMaski konto web3.eth.coinbase -ga. Seejärel kontrollime, kas see publicAddress on juba tagumises otsas olemas või mitte. Me kas leiame selle, kui kasutaja on juba olemas, või kui pole, loome uue konto jaotises handleSignup meetod.

4. samm: kasutaja allkirjastab nonce'i (esiosa)

Liigume edasi oma handleClick -s meetod. Nüüd on meie valduses kasutaja, kelle annab tagumine ots (olgu see siis allalaaditud või vastloodud). Eelkõige on meil nende nonce ja publicAddress. Nii et oleme valmis allkirjastama nonce selle publicAddress -ga seotud privaatvõtmega kasutades web3.personal.sign. Seda tehakse handleSignMessage funktsioon.

Pange tähele, et web3.personal.sign võtab esimese argumendina stringi kuueteistkümnendsüsteemi. Peame teisendama oma UTF-8 kodeeritud stringi kuuskantvormingusse, kasutades web3.fromUtf8 Samuti otsustasin mitte ainult allkirjastada, vaid allkirjastada kasutajasõbralikuma lause, kuna seda kuvatakse MetaMaski kinnitus hüpikus: I am signing my once-time nonce: ${nonce}

class Login extends Component { handleClick = () => { // --snip-- fetch(`${process.env.REACT_APP_BACKEND_URL}/users?publicAddress=${publicAddress}`) .then(response => response.json()) // If yes, retrieve it. If no, create it. .then( users => (users.length ? users[0] : this.handleSignup(publicAddress)) ) // Popup MetaMask confirmation modal to sign message .then(this.handleSignMessage) // Send signature to back end on the /auth route .then(this.handleAuthenticate) // --snip-- }; handleSignMessage = ({ publicAddress, nonce }) => { return new Promise((resolve, reject) => web3.personal.sign( web3.fromUtf8(`I am signing my one-time nonce: ${nonce}`), publicAddress, (err, signature) => { if (err) return reject(err); return resolve({ publicAddress, signature }); } ) ); }; handleAuthenticate = ({ publicAddress, signature }) => fetch(`${process.env.REACT_APP_BACKEND_URL}/auth`, { body: JSON.stringify({ publicAddress, signature }), headers: { 'Content-Type': 'application/json' }, method: 'POST' }).then(response => response.json()); }

Kui kasutaja on kirjale edukalt alla kirjutanud, liigume handleAuthenticate meetod. Saadame lihtsalt päringu /auth marsruudi tagaosas, saates meie publicAddress samuti signature kasutaja äsja allkirjastatud sõnumist.

5. samm: allkirja kontrollimine (tagumine osa)

See on veidi keerulisem osa. Tagaosa saab päringu /auth marsruut, mis sisaldab publicAddress ja a signature ning peab kontrollima, kas see publicAddress on alla kirjutanud õigele nonce.

Esimene samm on kasutaja andmebaasist hankimine nimetatud publicAddress; on ainult üks, sest me määratlesime publicAddress unikaalse väljana andmebaasis. Seejärel määrasime teate msg kui 'kirjutan alla oma ...', täpselt nagu 4. sammu esiosas, selle kasutaja meelepärasusega.

Järgmine plokk on kontroll ise. Sellega on seotud krüptograafia. Kui tunnete end seiklushimulisena, soovitan teil sellest lähemalt lugeda elliptilise kõvera allkirjad .

Selle ploki kokkuvõtteks on see, mida ta teeb, arvestades meie msg (sisaldab nonce) ja meie signature, ecrecover funktsioon väljastab avaliku aadressi, mida kasutati msg allkirjastamiseks. Kui see sobib meie publicAddress päringu asutuselt, siis tõendas taotluse teinud kasutaja, et omab edukalt publicAddress Peame neid autentseks.

User.findOne({ where: { publicAddress } }) // --snip-- .then(user => { const msg = `I am signing my one-time nonce: ${user.nonce}`; // We now are in possession of msg, publicAddress and signature. We // can perform an elliptic curve signature verification with ecrecover const msgBuffer = ethUtil.toBuffer(msg); const msgHash = ethUtil.hashPersonalMessage(msgBuffer); const signatureBuffer = ethUtil.toBuffer(signature); const signatureParams = ethUtil.fromRpcSig(signatureBuffer); const publicKey = ethUtil.ecrecover( msgHash, signatureParams.v, signatureParams.r, signatureParams.s ); const addressBuffer = ethUtil.publicToAddress(publicKey); const address = ethUtil.bufferToHex(addressBuffer); // The signature verification is successful if the address found with // ecrecover matches the initial publicAddress if (address.toLowerCase() === publicAddress.toLowerCase()) { return user; } else { return res .status(401) .send({ error: 'Signature verification failed' }); } })

Eduka autentimise korral genereerib tagumine JWT ja saadab selle kliendile tagasi. See on klassikaline autentimisskeem ja kood JWT integreerimiseks teie taguotsaga leiate repost .

6. toiming: muutke Nonce (tagumine osa)

Viimane samm on turvalisuse huvides nonce'i muutmine. Kusagil pärast edukat autentimist lisage see kood:

// --snip-- .then(user => { user.nonce = Math.floor(Math.random() * 1000000); return user.save(); }) // --snip--

See ei olnud nii raske, kas pole? Jällegi, kui soovite näha, kuidas kogu rakendus on juhtmega ühendatud (JWT-põlvkond, CRUD-marsruudid, localStorage jne), vaadake julgelt GitHubi repo .

See on täna tootmisvalmis

Kuigi plokiahelal võivad olla vead ja see on alles imikueas, ei saa ma piisavalt rõhutada, kuidas seda sisselogimisvoogu saaks rakendada mis tahes olemasoleval veebisaidil täna . Siin on loetelu argumentidest, miks seda sisselogimisvoogu eelistatakse mõlema e-posti / parooli asemel ja sotsiaalne sisselogimine:

  • Suurenenud turvalisus : Omandiõiguse tõendamine avaliku võtmega krüpteerimise teel on vaieldamatult turvalisem kui omandiõiguse tõendamine e-posti / parooli või kolmanda osapoole poolt - seda enam, et MetaMask salvestab mandaadid kohapeal teie arvutisse, mitte veebiserveritesse, mis teeb rünnaku pind väiksem.
  • Lihtsustatud UX : See on ühe klõpsuga (okei, võib-olla kahe klõpsuga) sisselogimisvoog, mis tehakse käputäie sekunditega, ilma et oleks vaja parooli sisestada või meelde jätta.
  • Suurenenud privaatsus : E-posti pole vaja ja keegi kolmas osapool pole kaasatud.

Muidugi saab MetaMaski sisselogimisvoogu suurepäraselt kasutada paralleelselt teiste traditsiooniliste sisselogimismeetoditega. Iga konto ja tema valduses oleva avaliku aadressi vahel tuleb kaardistada.

Kuid see sisselogimisvoog ei sobi kõigile:

  • Kasutajatel peab olema MetaMask installitud : See sisselogimisvoog ilmselgelt ei tööta ilma MetaMaskita ega web3 toega brauserita. Kui teie publikut ei huvita krüptorahad, on väike võimalus, et nad isegi kaaluksid MetaMaski installimist. Loodame hiljutise krüptobuumi abil, et liigume a Veeb 3.0 Internet .
  • Osa tööd tuleb teha tagumises otsas : Nagu nägime, on selle sisselogimisvoo lihtsa versiooni juurutamine üsna lihtne. Kuid selle integreerimiseks olemasolevasse keerulisse süsteemi on vaja mõningaid muudatusi kõigis autentimist puudutavates valdkondades: registreerumine, andmebaas, autentimisviisid jne. See kehtib eriti seetõttu, et iga konto seostatakse ühe või mitme avaliku aadressiga.
  • See ei tööta mobiilseadmes : See väärib oma osa - loe edasi.

Puudused mobiilseadmes

Nagu nägime, web3 on selle sisselogimisvoo eeltingimus. Töölauabrauserites süstib MetaMask seda. Mobiilibrauserites pole aga laiendusi, nii et see sisselogimisvoog ei toimi mobiilseadme Safaris, Chrome'is ega Firefoxis kohe valmis. On mõned eraldiseisvad mobiilibrauserid, mis süstivad web3 - põhiliselt brauserisse pakitud MetaMask. Need on selle kirjutamise alguses üsna varajases staadiumis, kuid kui olete huvitatud, vaadake seda Šifreeri , Staatus ja Toshi . „Logi sisse MetaMaskiga” töötab nende mobiilibrauseritega.

Mobiilirakenduste puhul on vastus jah, sisselogimisvoog töötab, kuid ettevalmistamiseks on palju eeltöid. Põhimõtteliselt peaksite ise lihtsa Ethereumi rahakoti uuesti üles ehitama. See hõlmab avalike aadresside genereerimist, algsõnade taastamist ja turvalist privaatvõtmete salvestamist, samuti web3.personal.sign ja kinnitus hüpikaken. Õnneks on teid abistavaid raamatukogusid. Ülioluline valdkond, millele keskenduda, on loomulikult turvalisus, kuna rakendus ise hoiab privaatvõtit. Töölauabrauserites delegeerisime selle ülesande MetaMaskile.

Nii et ma väidaksin, et lühike vastus on eitav, see sisselogimisvoog täna mobiilis ei tööta. Selles suunas pingutatakse, kuid lihtne lahendus jääb tänapäeval paralleelseks traditsiooniliseks sisselogimismeetodiks mobiilikasutajatele.

Laske oma kasutajatel MetaMaskiga sisse logida

Tutvustasime selles artiklis ühe klõpsuga krüptograafiliselt turvalist sisselogimisvoogu, kaasamata kolmandat osapoolt, nimega „Logi sisse MetaMaskiga”. Selgitasime, kuidas taguotsaga loodud juhusliku nonce'i digitaalallkiri võib tõendada konto omamist ja seega autentimist. Uurisime ka selle sisselogimismehhanismi kompromisse võrreldes tavapäraste e-posti / parooli või sotsiaalse sisselogimisega nii töölaual kui ka mobiilseadmes.

Ehkki sellise sisselogimisvoo sihtgrupp on tänapäevalgi väike, loodan südamest, et mõned teist tunnevad paralleelselt traditsiooniliste sisselogimisvoogudega inspiratsiooni pakkuda oma veebirakenduses MetaMaskiga sisselogimist - ja ma tahaksin sellest kuulda kui teete. Kui teil on küsimusi, võtke julgelt ühendust allpool toodud kommentaaridega.

Seotud: Ultimate ENS ja ppApp juhendaja

Katkiste tarneahelate tervendamine: tootmine väljaspool Hiinat

Kasumlikkus Ja Tõhusus

Katkiste tarneahelate tervendamine: tootmine väljaspool Hiinat
Kümme levinumat alglaadimisviga, mida arendajad teevad

Kümme levinumat alglaadimisviga, mida arendajad teevad

Veebi Kasutajaliides

Lemmik Postitused
Eeltreenitud mudelitest maksimumi võtmine
Eeltreenitud mudelitest maksimumi võtmine
Tähed joondatud: IMDb reitingusüsteemi täiustamine
Tähed joondatud: IMDb reitingusüsteemi täiustamine
Uued reaalsused: VR, AR, MR ja disaini tulevik
Uued reaalsused: VR, AR, MR ja disaini tulevik
7 fototaustarakendust iPhone'ile, et muuta teie võtte tausta
7 fototaustarakendust iPhone'ile, et muuta teie võtte tausta
IPhone 12 võrdlus: iPhone 12 vs 12 Pro vs Pro Max vs Mini
IPhone 12 võrdlus: iPhone 12 vs 12 Pro vs Pro Max vs Mini
 
Kuidas teha Instagrami fotosid nagu professionaal, kasutades ainult oma iPhone'i
Kuidas teha Instagrami fotosid nagu professionaal, kasutades ainult oma iPhone'i
4 kontrasti tüüpi fotograafias ja kuidas neid õigesti kasutada
4 kontrasti tüüpi fotograafias ja kuidas neid õigesti kasutada
10 Instagrami kaasamishäkki, mida mõjutajad kasutavad ja armastavad
10 Instagrami kaasamishäkki, mida mõjutajad kasutavad ja armastavad
Kuidas teha iPhone'is loomingulist topeltsäritusega pildistamist
Kuidas teha iPhone'is loomingulist topeltsäritusega pildistamist
Kaugtöö ja teenuste globaliseerumine
Kaugtöö ja teenuste globaliseerumine
Kategooriad
Kasumlikkus Ja TõhususMuuUx DisainTöö TulevikiOS-i näpunäitedTrendidVeaotsingAndmeteadus Ja AndmebaasidJaotatud VõistkonnadProjekti Juht

© 2023 | Kõik Õigused Kaitstud

socialgekon.com