You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于本地文件的Web认证:自用购物清单程序认证方案改造问询

Hey there! Let's walk through implementing this UUID-based authentication flow for your home shopping list app—since it's just you and your spouse, we can keep things straightforward but secure while working with Friend, Clojure, and your existing stack.

Step 1: Set Up Server-Side UUID Storage

First, we need a way to track valid UUIDs for you and your spouse. Since it's a small user base, you can start with a simple in-memory set, or use an EDN file for persistence (so you don't have to restart the server if you ever need to update UUIDs).

;; Option 1: In-memory (simple, restarts reset the list)
(def authorized-uuids #{"your-uuid-v4-here" "spouses-uuid-v4-here"})

;; Option 2: Load from an EDN file (persistent)
(defn load-authorized-uuids []
  (try
    (clojure.edn/read-string (slurp "authorized-uuids.edn"))
    (catch Exception _ #{})))

(def authorized-uuids (load-authorized-uuids))

Generate proper UUIDv4s using (java.util.UUID/randomUUID) in your REPL—these are long, random, and hard to guess.

Step 2: Build a Custom Friend Authentication Backend

Friend uses "backends" to handle credential validation. We'll create a custom backend that checks for the UUID in incoming requests.

First, write a credentials-fn that extracts the UUID from a request header (we'll use X-User-UUID for consistency) and verifies it against our authorized set:

(defn uuid-credentials-fn [request]
  (let [uuid (get-in request [:headers "x-user-uuid"])]
    (when (contains? authorized-uuids uuid)
      {:identity uuid       ; Unique identifier for the user
       :roles #{:user}})))  ; Optional roles (you could add :admin if needed)

Then configure Friend to use this custom backend, replacing your existing Cookie-based auth. We'll also set up an unauthenticated-handler that serves a page with our UUID-reading JavaScript:

(require '[friend.core :as friend]
         '[friend.backends.custom :as custom]
         '[hiccup.core :as hiccup])

(defn auth-protected-routes []
  (friend/authenticate
    ;; Your existing app routes go here
    (routes
      (GET "/" [] (hiccup/html [:h1 "Your Shopping List"]))
      (POST "/add-item" req (handle-add-item req)))
    {:allow-anon? false
     :unauthenticated-handler (fn [_req]
                                ;; Serve the auth page with our JS
                                (hiccup/html
                                  [:html
                                   [:head [:title "Authenticate"]]
                                   [:body
                                    [:h2 "Welcome to the Shopping List"]
                                    [:script
                                     ;; We'll add our JS code here next
                                     ]]]))
     :auth-backends [(custom/custom-backend
                       {:credentials-fn uuid-credentials-fn})]}))

Step 3: Frontend JavaScript to Manage UUIDs

Here's the catch: browser JavaScript can't directly read local files from your device (security restriction!). So we'll adjust to a user-friendly alternative that fits your "local storage" goal: let users input their UUID once, then store it in the browser's localStorage so it's automatically sent with every request.

Add this script to the unauthenticated handler page:

// Get or prompt for the user's UUID
function getStoredUUID() {
  let uuid = localStorage.getItem('shopping-list-uuid');
  if (!uuid) {
    uuid = prompt('Please enter your unique shopping list UUID:');
    if (uuid) {
      localStorage.setItem('shopping-list-uuid', uuid);
    }
  }
  return uuid;
}

// Inject UUID into all fetch/AJAX requests
document.addEventListener('DOMContentLoaded', () => {
  // Override fetch to add the UUID header
  const originalFetch = window.fetch;
  window.fetch = function(url, options = {}) {
    const uuid = getStoredUUID();
    if (uuid) {
      options.headers = {
        ...options.headers,
        'X-User-UUID': uuid
      };
    }
    return originalFetch(url, options);
  };

  // Handle form submissions (if your app uses forms)
  document.querySelectorAll('form').forEach(form => {
    form.addEventListener('submit', (e) => {
      const uuid = getStoredUUID();
      if (uuid) {
        const hiddenInput = document.createElement('input');
        hiddenInput.type = 'hidden';
        hiddenInput.name = 'uuid';
        hiddenInput.value = uuid;
        form.appendChild(hiddenInput);
      }
    });
  });

  // Redirect to the app once we have a UUID
  if (getStoredUUID()) {
    window.location.href = '/';
  }
});

This script will prompt the user for their UUID the first time they visit, save it locally, and automatically attach it to every subsequent request.

Step 4: Test and Tweak

  1. Generate UUIDs for you and your spouse using (java.util.UUID/randomUUID) in your Clojure REPL.
  2. Update your authorized-uuids set (or EDN file) with these values.
  3. Deploy the updated code to your home server.
  4. On each device, visit the app—you'll be prompted for the UUID once, then automatically authenticated from then on.

Quick Security Notes

Even for home use, a few small steps keep things safe:

  • Use HTTPS for your local server (you can generate a self-signed cert with openssl if you don't have a public domain).
  • Periodically regenerate UUIDs if you ever share a device with someone else.
  • Keep your server's UUID storage file (if using EDN) readable only by the server process.

内容的提问来源于stack exchange,提问作者Kingfranz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:40:42