#JavaScriptで生成したランダムなコードが、日付が変わるまで維持される方法を教えてください。
##実現したいこと
利用者が名前を入力・登録するとアルファベット1文字+数字2桁のゆーざコードが作成される(重複あり)というページを作っているのですが、ランダムなコードを生成することはできてもそれを日付が変わるまで維持させる方法がわかりません。現状、ページを更新する度にユーザコードが変わってしまい、その後他のページで利用することができません。
一度生成されたコードはその日のうちは変わることなく、日付が変われば次のコードが表示される、という仕組みしたいのですが、どのように記述すれば宜しいのでしょうか。
##コード
'use strict';
// 配列と乱数を定義
const codeArea = document.getElementById('code-area');
const alphabet = ["A", "C", "F", "N", "S"];
const num1 = Math.floor(Math.random() * (alphabet.length - 1));
const score = ["11", "12", "13", "14", "21", "22", "23", "24", "25", "31", "32", "33", "34"];
const num2 = Math.floor(Math.random() * (score.length - 1));
// ページ読み込み時の処理
window.onload = function() {
// 名前が登録されていれば当日のコードが表示される。
if(localStorage.getItem('savedName')) {
const name = localStorage.getItem('savedName');
codeArea.innerHTML = <p>${name}さんの今日のユーザコードは${alphabet[num1]}${score[num2]}です。
;
} else {
codeArea.innerHTML = '<p>名前を入力して今日のユーザコードを取得してください。</p><p><input type="text" id="your-name" size="30" value="山田太郎"> <button type="button" id="input-name" onclick="saveName();">登録 ▶</button></p>';
}
}
function saveName() {
let yourName = document.getElementById('your-name').value;
localStorage.setItem('savedName', yourName);
document.location.reload();
}
宜しくお願い致します。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ベストアンサー
修正案の一例として、回答します。要点としては以下の2点です。
(1) localStorage に保存する情報を追加
localStorage に保存する情報に、入力された名前に加えて、以下の2点を追加します。
- 生成されたユーザーコード
- 上記のユーザーコードの有効期限を表す文字列
そこで、localStorage には以下のキーと値をsetItem します。
- キー:
yourInfo
- 値: 以下のようなJSON文字列
json
1{"name":"山田太郎","code":"A11","expireDate":"2021-12-04 23:59:59"}
expireDate
は、ユーザーコードが生成された日時の時刻部分を、その日の最終時刻を表す23:59:59
に変えたものです。
(2) 日時の操作にDayjsを利用する。
表示の際に、localStorage に yourInfo
キーで上記のJSONが保存されていればparseして、現在日時とexpireDateを比較することになります。この際に、日時の操作で便利なライブラリDayjsを使います。Dayjsを使えば、「ある時点の当日最終日時」といった日時も容易に作ることができます。
以下は、上記(1)、(2)による修正コードの例です。
index.html
html
1<!DOCTYPE html> 2<html lang="ja"> 3<head> 4 <meta charset="UTF-8"> 5 <title>tera: 372186</title> 6 <script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.10.7/dayjs.min.js" 7 integrity="sha512-bwD3VD/j6ypSSnyjuaURidZksoVx3L1RPvTkleC48SbHCZsemT3VKMD39KknPnH728LLXVMTisESIBOAb5/W0Q==" 8 crossorigin="anonymous" 9 referrerpolicy="no-referrer"></script> 10 <script src="script.js"></script> 11</head> 12<body> 13<div id="code-area">code-area</div> 14</body> 15</html> 16
script.js
javascript
1'use strict'; 2 3const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss'; 4 5// ページ読み込み時の処理 6window.onload = function () { 7 const codeArea = document.getElementById('code-area'); 8 const inputHtml = `<p>名前を入力して今日のユーザコードを取得してください。</p> 9 <p> 10 <input type="text" id="your-name" size="30" value="山田太郎"> 11 <button type="button" id="input-name" onclick="saveUserInfo();">登録 ▶</button> 12 </p>`; 13 14 const yourInfoJson = localStorage.getItem('yourInfo'); 15 16 if (yourInfoJson) { 17 const { name, code, expireDate } = JSON.parse(yourInfoJson); 18 if (dayjs().isBefore(dayjs(expireDate, DATE_FORMAT))){ 19 codeArea.innerHTML = `<p>${name}さんの今日のユーザコードは${code}です。`; 20 } else { 21 localStorage.removeItem('yourInfo'); 22 codeArea.innerHTML = inputHtml; 23 } 24 } else { 25 codeArea.innerHTML = inputHtml; 26 } 27} 28 29// ユーザーコード作成関数 30function makeUserCode() { 31 const randomChoice = ary => ary[Math.floor(Math.random() * ary.length)]; 32 const alphabet = ["A", "C", "F", "N", "S"]; 33 const score = ["11", "12", "13", "14", "21", "22", "23", "24", "25", "31", "32", "33", "34"]; 34 return [alphabet, score].map(randomChoice).join(''); 35} 36 37// ユーザー情報保存関数 38function saveUserInfo() { 39 const name = document.getElementById('your-name').value; 40 const code = makeUserCode(); 41 const expireDate = dayjs().endOf('day').format(DATE_FORMAT); 42 const userInfo = { name, code, expireDate }; 43 44 localStorage.setItem('yourInfo', JSON.stringify(userInfo)); 45 document.location.reload(); 46} 47
上記のコードでは、Dayjsのメソッドとして、
の2点を使っています。
追記
上記のコードで、expireDate
を作るのに
javascript
1const expireDate = dayjs().endOf('day').format(DATE_FORMAT);
としていましたが、見直してみると、以下にするほうがより正確でした。
javascript
1const expireDate = dayjs().add(1, 'day').startOf('day').format(DATE_FORMAT);
これで、expireDate にはユーザーコードが生成された時点の翌日0時0分0秒に設定されるので、
javascript
1if (dayjs().isBefore(dayjs(expireDate, DATE_FORMAT))){
によって現在日時と比較される日時として、より妥当なものになります。
追記2
上記までのコードだと、localStorageに保存されたyourInfoのexpireDateを改ざんすることで、有効期限を故意に伸ばすことができてしまうので暗号化します。暗号化には、crypto-js を使います。
index.html
diff
1 crossorigin="anonymous" 2 referrerpolicy="no-referrer"></script> 3+ <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script> 4 <script src="script.js"></script> 5 </head> 6 <body>
script.js
diff
1 if (yourInfoJson) { 2 const { name, code, expireDate } = JSON.parse(yourInfoJson); 3- if (dayjs().isBefore(dayjs(expireDate, DATE_FORMAT))){ 4+ const decryptedExpireDate = CryptoJS.AES.decrypt(expireDate, `${name}${code}`).toString(CryptoJS.enc.Utf8); 5+ if (dayjs().isBefore(dayjs(decryptedExpireDate, DATE_FORMAT))){ 6 codeArea.innerHTML = `<p>${name}さんの今日のユーザコードは${code}です。`; 7 } else { 8 localStorage.removeItem('yourInfo');
diff
1 const name = document.getElementById('your-name').value; 2 const code = makeUserCode(); 3 const expireDate = dayjs().add(1, 'day').startOf('day').format(DATE_FORMAT); 4- const userInfo = { name, code, expireDate }; 5+ const encryptedExpireDate = CryptoJS.AES.encrypt(expireDate,`${name}${code}`).toString(); 6+ const userInfo = { name, code, expireDate: encryptedExpireDate }; 7 localStorage.setItem('yourInfo', JSON.stringify(userInfo)); 8 document.location.reload(); 9 }
しかし、これでも script.js が簡単に読めてしまっては、プログラムが読める人なら、expireDateの暗号化に、crypto-jsを使っていて、暗号化と復号化のカギに ${name}${code}
を使っていることが読み取れてしまいますので、 復号されたexpireDateが 2050-01-01 00:00:00
になるように、localStorageを書き換えてしまうかもしれません。これを防ぐために script.jsを難読化します。
たとえばJavaScript Obfuscator Tool というオンラインの難読化ツールを使うと、script.js が以下のように難読化されます。
javascript
1'use strict';function _0x1d81(_0x6942b4,_0x34924a){const _0x1e71e1=_0x1e71();return _0x1d81=function(_0x1d8116,_0x127fa8){_0x1d8116=_0x1d8116-0x82;let _0x1f92bb=_0x1e71e1 ・・・以下省略
(※回答文字数の上限を超えるため、省略しました)
これでも万全ではありませんから、より安全にするには入力された名前からのユーザーコードの生成とそのコードの有効期限の管理を、サーバーサイドで行い、画面側でそのコードを使うときはサーバーサイドへそのコードがまだ使えるかの問い合わせをすることが考えられます。
追記3
コメントから頂きました
今回のコードで名前の入力を2度目以降省略したい時には、
との件について回答します。ご提示の
”name”を単独でlocalStorageに保存した後、”if (yourInfoJson)”の外側に追加でif文を記載すれば宜しいのでしょうか。
という修正でも出来るとは思いますが、localStorageに保存する情報はこれまでに回答したyourInfo
キーで保存する以下の形式のJSONオブジェクト
json
1{"name":"山田太郎","code":"N23","expireDate":"U2FsdGVkX1+Nm9QtGzUMtF8UaJXYYtIQXAT4ODxj6o8p5o9skBrOqnGoQ0vji5Hw"}
のみでも対応は可能かと思います。たとえば、追記2に記載したexpireDateを暗号化する修正後のコードに対して、さらに以下の**(1)〜(3)**のような修正を行います。
(1) const inputHtml を固定のHTMLから、HTMLを返す関数にする。
inputHtml
を修正して、名前を引数name
で受け取り、それが
- undefined(などfalsy)のときには名前を入力できるtype="text"のinputとし
- 一文字以上の文字列のときは、type="hidden" のinputとする
ようなHTMLを返す関数にします。これは以下のような修正です。
diff
1 window.onload = function () { 2 const codeArea = document.getElementById('code-area'); 3- const inputHtml = `<p>名前を入力して今日のユーザコードを取得してください。</p> 4+ const inputHtml = name => `<p>${name ? '' : '名前を入力して'}今日のユーザコードを取得してください。</p> 5 <p> 6- <input type="text" id="your-name" size="30" value="山田太郎"> 7+ <input type="${name ? 'hidden' : 'text'}" id="your-name" size="30" value="${name || '山田太郎'}"> 8 <button type="button" id="input-name" onclick="saveUserInfo();">登録 ▶</button> 9 </p>`;
(2) 上記の(1)で関数にした inputHtml を使う箇所の修正
以下のように修正します。
- キー
yourInfo
で localStorage にJSONが保存されていた場合:
現在日時が(暗号から戻された)expireDate以降となっている、または、localStorageに暗号化されたexpireDateが改ざんされるなどして、decryptedExpireDateが日付として不正となっている場合に、
ⅰ) localStorage から yourInfo
をremoveItem()で消していたが、消さないでおく
ⅱ) codeArea.innerHTML に inputHtml(name)
が返す、(inputのtypeがhiddenになっている)HTMLを入れる。
- キー
yourInfo
で localStorage にJSONが保存されていなかった場合:
ⅲ) codeArea.innerHTML に inputHtml()
が返す、(inputのtypeがtextになっている)HTMLを入れる。
diff
1 if (dayjs().isBefore(dayjs(decryptedExpireDate, DATE_FORMAT))){ 2 codeArea.innerHTML = `<p>${name}さんの今日のユーザコードは${code}です。`; 3 } else { 4- localStorage.removeItem('yourInfo'); 5- codeArea.innerHTML = inputHtml; 6+ codeArea.innerHTML = inputHtml(name); 7 } 8 } else { 9- codeArea.innerHTML = inputHtml; 10+ codeArea.innerHTML = inputHtml(); 11 }
(3) 暗号化されたexpireDateの復号時に例外が発生した場合の対応
さらに、
今回のコードで名前の入力を2度目以降省略したい時には、
の件とは関係ありませんが、localStorageに保存されている暗号化されたexpireDateを、ユーザーが改ざんした場合に、
javascript
1const decryptedExpireDate = CryptoJS.AES.decrypt(expireDate, `${name}${code}`).toString(CryptoJS.enc.Utf8);
の部分で例外が発生する場合があるので、これを catch しておきます。
diff
1 if (yourInfoJson) { 2 const { name, code, expireDate } = JSON.parse(yourInfoJson); 3- const decryptedExpireDate = CryptoJS.AES.decrypt(expireDate, `${name}${code}`).toString(CryptoJS.enc.Utf8); 4+ let decryptedExpireDate = null; 5+ try { 6+ decryptedExpireDate = CryptoJS.AES.decrypt(expireDate, `${name}${code}`).toString(CryptoJS.enc.Utf8); 7+ } catch (e) { 8+ console.error(e); 9+ } 10 if (dayjs().isBefore(dayjs(decryptedExpireDate, DATE_FORMAT))){ 11 codeArea.innerHTML = `<p>${name}さんの今日のユーザコードは${code}です。`; 12
この修正で、例外が発生した場合、decryptedExpireDate
はnull
になりますが、その場合も
javascript
1dayjs(decryptedExpireDate, DATE_FORMAT)
はエラーにならず、日時として不正な内容を含むdayjsオブジェクトになり、それを比較対象にした isBefore 判定は(期待どおり) false を返してくれます。
以上の修正**(1)〜(3)**によって、意図した動作のものになるかと思います。
投稿2021/12/04 03:47
編集2021/12/07 15:58退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/12/06 17:20
退会済みユーザー
2021/12/07 15:55
0
日付が変わるまで維持させる方法
「ユーザ名+yyyymmdd」の文字列からハッシュ値を求め、その値に応じてユーザーコードを取得するというのは如何でしょうか?
以下、サンプルコードになります。同じ日であれば同じユーザーコードを返すことは確認しました。
javascript
1function getYYYYMMDD(dt) { 2 const yy = dt.getFullYear(); 3 const mm = ('00' + (dt.getMonth()+1)).slice(-2); 4 const dd = ('00' + dt.getDate()).slice(-2); 5 return (yy + mm + dd); 6} 7 8function getHashCodeByName(name) { 9 var hash = 0; 10 if (name.length == 0) { 11 return hash; 12 } 13 for (let i = 0; i < name.length; i++) { 14 const c = name.charCodeAt(i); 15 hash = ((hash<<5)-hash)+c; 16 hash = hash & hash; 17 } 18 return Math.abs(hash); 19} 20 21function getUserCodeByUserName(userName) { 22 // 配列と乱数を定義 23 const hashCode = getHashCodeByName(userName); 24 const alphabet = ["A", "C", "F", "N", "S"]; 25 const num1 = hashCode % alphabet.length; 26 const score = ["11", "12", "13", "14", "21", "22", "23", "24", "25", "31", "32", "33", "34"]; 27 const num2 = hashCode % score.length; 28 const userCode = alphabet[num1] + score[num2]; 29 return userCode; 30} 31 32// テスト 33console.log("helloさんの 今日 のユーザーコード:"+getUserCodeByUserName("hello"+getYYYYMMDD(new Date()))); 34console.log("helloさんの 今日 のユーザーコード:"+getUserCodeByUserName("hello"+getYYYYMMDD(new Date()))); 35 36console.log("abcさんの 2021/12/04 のユーザーコード:"+getUserCodeByUserName("abc20211204")); 37console.log("abcさんの 2021/12/05 のユーザーコード:"+getUserCodeByUserName("abc20211205")); 38console.log("abcさんの 2021/12/06 のユーザーコード:"+getUserCodeByUserName("abc20211206")); 39console.log("abcさんの 2021/12/04 のユーザーコード:"+getUserCodeByUserName("abc20211204")); 40 41console.log("defさんの 2021/12/04 のユーザーコード:"+getUserCodeByUserName("def20211204")); 42console.log("defさんの 2021/12/05 のユーザーコード:"+getUserCodeByUserName("def20211205")); 43console.log("defさんの 2021/12/06 のユーザーコード:"+getUserCodeByUserName("def20211206")); 44console.log("defさんの 2021/12/04 のユーザーコード:"+getUserCodeByUserName("def20211204")); 45
■ テスト結果
text
1helloさんの 今日 のユーザーコード:N21 2helloさんの 今日 のユーザーコード:N21 3 4abcさんの 2021/12/04 のユーザーコード:N24 5abcさんの 2021/12/05 のユーザーコード:F23 6abcさんの 2021/12/06 のユーザーコード:C22 7abcさんの 2021/12/04 のユーザーコード:N24 8 9defさんの 2021/12/04 のユーザーコード:S14 10defさんの 2021/12/05 のユーザーコード:A21 11defさんの 2021/12/06 のユーザーコード:C22 12defさんの 2021/12/04 のユーザーコード:S14
<参考>
■ 日付をYYYY-MM-DDの書式で返す関数 (JavaScript)
https://qiita.com/toshimin/items/5f13c3b4c28825219231
■ JavaScript用の単純な(非セキュア)ハッシュ関数?
https://www.webdevqa.jp.net/ja/javascript/javascript%E7%94%A8%E3%81%AE%E5%8D%98%E7%B4%94%E3%81%AA%EF%BC%88%E9%9D%9E%E3%82%BB%E3%82%AD%E3%83%A5%E3%82%A2%EF%BC%89%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E9%96%A2%E6%95%B0%EF%BC%9F/972873955/
■ 剰余 (%) - JavaScript | MDN
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Remainder
投稿2021/12/03 19:49
総合スコア4648
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。