问题
Expected behavior
Show users a random value (color) from the arr
array on each refresh. Each day a different file (file-1.json
, file-2.json
, etc) should be loaded into arr
. This should reset at 00:00 UTC server time. Only values from that day's associated file should be displayed.
Unexpected behavior
All works well, until users change the clock on their local machines/phones. Then they can effectively see future & past values as well. Date/time can be manipulated client-side, which shouldn't happen. Everyone should get the server's time, whatever that is.
Examples
Good: if you visit/refresh the site on 2020-04-18, you should get a random color picked from
file-1.json
.Good: if you visit/refresh the site on 2020-04-19, you should get a random color picked from
file-2.json
.Bad: on the other hand, if on 2020-04-18 you change your local clock to 2020-04-19, you shouldn't be able to get a random color from 2020-04-19 since that date is in the future. You should still get a value from today's file, since it is still 2020-04-18 server time.
script.js
:
// Store today's date
const today = new Date();
var arr = [];
// Define milliseconds per day
const msPerDay = 1000*60*60*24;
// Uncomment line below to test future date by using the getDateByOffset() function
//const today = getDateByOffset(1);
// Get difference (in days) between two dates
function getDiffInDays(date1, date2){
// `|0` is same as Math.floor(...)
return ((date2.getTime() - date1.getTime())/msPerDay)|0;
}
// Get date by offset in days (Useful for testing tomorrow's date and so on)
function getDateByOffset(days=0){
const today = new Date();
return new Date((today.getTime()/msPerDay + days)*msPerDay);
}
// Get offset index for the json file
function getIndex(){
// Define the starting date for "file-1.json"
const startDate = new Date(Date.parse('4/18/2020'));
// Will range from 1 instead of 0
return getDiffInDays(startDate, today) + 1;
}
new Promise(resolve=>{
// Get the json file based on the offset
$.getJSON(`file-${getIndex()}.json`, resolve);
})
.then(json=>{
// Add it to the `arr` array
arr = [...arr,...json];
})
.then(()=>{
console.log(arr);
$("#show").text(arr[Math.floor(Math.random() * arr.length)]);
})
DEMO on Netlify
How to replicate the issue
Simply change your local machine or phone's clock to 2020-04-19. Then you would get tomorrow's colors.
EDIT: just to be 100% clear, whatever the user does with his local device settings (change clock, IP address, etc) -- he should NOT be able to influence the website time.
EDIT2: created this endpoint to retrieve date and time: https://bluexpress.netlify.app/.netlify/functions/server/getdate but I'm not sure if it's the right solution or how to properly integrate it into the site. Assistance appreciated!
回答1:
Then answer is simple: don't compute the file name on the client, rather make it a single URI which dynamically computes the JSON on the server depending on the day.
As long as the client can request any of the JSON data by changing the URI, users which are a little bit savvy can access past and future days, even without changing the clock. As a general rule, don't trust the client, always make sure to check things on the server.
That being said, you could also keep the client logic but make the server deny (e.g. 404 or so) access to the file of the "wrong day" depending on the date. But you'll have to have server logic to rule out client-side abuse.
回答2:
You can use World Time API's /api/ip
feature to get the api based on the user's public ip address. Something like
// Modify the getIndex function to receive today's date instead
function getIndex(today){
const startDate = new Date(Date.parse('4/15/2020'));
const offset = getDiffInDays(startDate, today) + 1;
return offset;
}
// Get date from server
function getServerDate(){
// Unfortunately, we need to bypass Cross-Origin Request (CORs)
return fetch(`https://cors-anywhere.herokuapp.com/http://worldtimeapi.org/api/ip`)
// Convert response to json
.then(res=>res.json())
// Get the datetime from object and create a Date object
.then(({datetime})=>new Date(Date.parse(datetime)))
// Get json index based on the date
.then(getIndex);
}
And using above function you can simply
// Get today's date from server
getServerDate()
.then(index=>new Promise(resolve=>{
// Get the json file based on the index returned
$.getJSON(`data-${index}.json`, resolve);
}))
.then(json=>{
// Append to arr and whatever you like here
})
回答3:
the answer can be given based on
- If the HTML is rendered using a server side script
- If you have a sever endpoint to retrieve the date and time of the server
you can do either one of it and get the server's time to javascript's visible scope as a variable
Then use that variable to decide the file names as file-1
file-2
etc
To use Node.JS server side code, please follow Netlify: Punch Setup
来源:https://stackoverflow.com/questions/61286220/using-js-server-time-instead-of-client-time-to-avoid-this-behavior-where-users-c