开发者

Remember-me cookie and Varnish

开发者 https://www.devze.com 2023-04-03 21:53 出处:网络
I\'m trying to get Spring Security\'s remember-me feature to work with Varnish but this seems incredibly hard. With regular login, it\'s easy, I just setup Varnish to bypass cache for j_spring_securit

I'm trying to get Spring Security's remember-me feature to work with Varnish but this seems incredibly hard. With regular login, it's easy, I just setup Varnish to bypass cache for j_spring_security_check URL, but with remember-me, any URL can be an entry point for login. If the first thing a user hits when they open up their browser is an URL that Varnish skips (i.e. bypasses cache for), then all is fine, but if the user hits homepage (or anything that Varnish caches), something strange happens: while the user does get logged in, I get a CookieTheftException and thus the remember-me cookie gets canceled, so no further auto-logins are possible. When I think about it, it sounds like these two (remember-me and Varnish) can simply never work together! Can this be true?

Any ideas what could have went wrong? How is it possible to get remember-me to work with Varnish? Could the hashing function be a problem?

I'm posting parts of my Varnish configs below (skipped the hashing function definition, please say if you think it's relevant):

sub vcl_recv {
    # Forward IP to Apache log
    unset req.http.X-Forwarded-For;
    set req.http.X-Forwarded-For = client.ip;

        if (req.http.host ~ "(?i)mysite\.com$") {
        if (req.restarts == 0) {
                    set req.backend = mysite;
        }
        else {
            error 750 "mysite";
        }
    }

    # static content should always be cached
    if (req.url ~ "\.(js|css|gif|jpg|jpeg|png|swf|flv|txt|pdf|mp3)$") {
        unset req.http.Cookie;
        return(lookup);
    }

    # only cache "get" or "head" requests
    if (req.request != "GET" && req.request != "HEAD") {
        return (pass);
    }

    # do not cache http authentication
    if (req.http.Authorization || req.http.Authenticate) {
        return (pass);
    }

    # do not cache Spring Security URLs, esi, personal pages etc.
        if (req.url ~ "^(/logout|/j_spring_security_check|/personal/)" || req.url ~ "\?service=esi") {
            return(pass);
    }

    return (lookup); # skip default vcl_recv
}

sub vcl_fetch { 
    # Try again if backend not responding
    if (beresp.status != 200 && beresp.status != 403 && beresp.status != 404 && beresp.status != 301 && beresp.status != 302 && beresp.status != 401) {
        return(restart);
    }

    # block sensitive files
    if (req.url ~ "\.(bak|conf|config|ear|exe|gz|jar|log|old|properties|tar|tmp|tgz|war)$") {
        error 405 "Not allowed";
    }

    # do esi processing for all non-static resources
    if (req.url !~ "\.(js|css|gif|jpg|jpeg|png|swf|flv|txt|xml|html|htm|pdf|mp3|doc)$") {       
        esi;
    }

    # do not cache when told not to
    if (req.http.Cache-Control ~ "no-cache") {
        return (pass);
    }

    # do not cache Spring Security URLs, esi, personal pages etc.
    if (req.url ~ "^(/logout|/j_spring_security_check|/personal/)" || req.url ~ "\?service=esi") {
        set beresp.http.Cache-Control = "private, no-cache, no-store, must-revalidate";
        set beresp.http.Pragma = "no-cache";
        set beresp.http.Expires = "Sat, 01 Jan 2000 00:00:00 GMT";
        return(pass);
    }

    # static content should always be cached
    if (req.url ~ "\.(js|css|gif|jpg|jpeg|png|swf|flv|txt|xml|html|htm|pdf|mp3|doc)$") {
        unset beresp.http.set-cookie;
        set bere开发者_运维问答sp.ttl = 1h;
    } else {
        set beresp.ttl = 300s;
    }
}

UPDATE: I documented my final implementation nicely here.


The RememberMe feature works by sending a special cookie to the server. The server knows how to interpret the cookie value (e.g. the username + password are encoded in there, or it contains a persistent token linking to the user) and can do a login for the user.

By default (default.vcl), Varnish will not interfere with requests containing cookies: they're passed. However, your vcl file doesn't look at request cookies, and instructs varnish to do a lookup anyway (in vcl_recv). Every client probably has a (SESSION) cookie, so it makes sense to ignore request cookies in many -but not all- cases.

Your vcl file should detect the rememberme request cookie in vcl_recv, and do a pass accordingly. Something like (but check the cookie name):

if (req.http.Cookie ~ "rememberme=" ) {
   return (pass);
}

Additionally, if you keep having CookieTheftExceptions, check if you cache any responses containing set-cookie headers. This way, people end up with the same sessions...

0

精彩评论

暂无评论...
验证码 换一张
取 消