千家信息网

spring-session中怎么动态修改cookie的max-age

发表于:2024-09-30 作者:千家信息网编辑
千家信息网最后更新 2024年09月30日,这篇文章给大家介绍spring-session中怎么动态修改cookie的max-age,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。使用spring-session时,动态修改
千家信息网最后更新 2024年09月30日spring-session中怎么动态修改cookie的max-age

这篇文章给大家介绍spring-session中怎么动态修改cookie的max-age,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

使用spring-session时,动态修改cookie的max-age

不论是使用spring提供的spring-session,还是使用servle容器实现的http session。原理都是把session-idcookie的形式存储在客户端。每次请求都带上cookie。服务器通过session-id,找到session

spring-session的使用

https://springboot.io/t/topic/864

由"记住我"引发的一个问题

用户登录的时候,通常需要一个【记住我】的选择框,表示是否要长期的保持会话。

【记住我】× 一般会把cookie的max-age设置为 -1,表示在浏览器关闭的时候,就自动的删除cookie。对于客户端而言关闭了浏览器,就是丢失了会话,需要重新的登录系统。特别在公共场合登陆了某些系统后,忘记执行'退出'操作,直接关闭了浏览器,后面使用电脑的人打开浏览器,也必须先登录才可以访问系统。这样在一定的程度上保证了安全性。

【记住我】√ 一般在自己私人电脑上选择,目的是为了避免重复的登录操作。登录成功,一般会把max-age的值设置为比较长,就算是关闭了浏览器。重新打开,也不需要再次执行登录操作。

spring-session 配置cookie的max-age属性

使用spring-session时,可以通过yml配置,或者代码配置的形式来设置max-age的属性。但是问题在于所有的session创建,都是使用同样的属性。在【记住我】这个功能上会出现一些问题

固定设置:max-age=-1,那么就算是勾选了【记住我】,也会因为浏览器关闭删除cookie,而丢失会话。下次打开浏览器还是需要重新执行登录

固定设置: max-age=604800(7天),那么用户在未勾选【记住我】的情况下,关闭浏览器。cookie并不会被立即删除,任何人再次打开这个系统。都不需要登录就可以直接操作系统。

总的来说就是,固定的max-age属性,会导致【记住我】功能失效。

使用spring-session时的解决方案

spring-session 通过接口 CookieSerializer,来完成对客户端cookie的读写操作。并且提供了一个默认的实现类: DefaultCookieSerializer。我们想要动态的修改cookie的max-age属性,核心方法在于。

@Overridepublic void writeCookieValue(CookieValue cookieValue) {        ...        StringBuilder sb = new StringBuilder();        sb.append(this.cookieName).append('=');        ...        int maxAge = getMaxAge(cookieValue);  // 读取maxAge属性        if (maxAge > -1) {                sb.append("; Max-Age=").append(cookieValue.getCookieMaxAge());                ZonedDateTime expires = (maxAge != 0) ? ZonedDateTime.now(this.clock).plusSeconds(maxAge)                                : Instant.EPOCH.atZone(ZoneOffset.UTC);                sb.append("; Expires=").append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME));        }        ...}private int getMaxAge(CookieValue cookieValue) {        int maxAge = cookieValue.getCookieMaxAge();        if (maxAge < 0) {                if (this.rememberMeRequestAttribute != null                                && cookieValue.getRequest().getAttribute(this.rememberMeRequestAttribute) != null) {                        cookieValue.setCookieMaxAge(Integer.MAX_VALUE);                }                else if (this.cookieMaxAge != null) {                        cookieValue.setCookieMaxAge(this.cookieMaxAge);  // 如果 DefaultCookieSerializer 设置了maxAge属性,则该属性优先                }        }        return cookieValue.getCookieMaxAge(); // cookieValue 默认的maxAge属性 = -1}

可以看出,spring-session并没使用servlet提供的cookie api来响应cookie。而是自己构造Cookie头。而且还提供了Servlet还未实现的,Cookie的新属性:sameSite,可以用来防止csrf攻击。

覆写 DefaultCookieSerializer

import javax.servlet.http.HttpServletRequest;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Value;import org.springframework.session.web.http.DefaultCookieSerializer;// @Componentpublic class DynamicCookieMaxAgeCookieSerializer extends DefaultCookieSerializer {                private static final Logger LOGGER = LoggerFactory.getLogger(DynamicCookieMaxAgeCookieSerializer.class);                public static final String COOKIE_MAX_AGE = "cookie.max-age";                @Value("${server.servlet.session.cookie.max-age}")        private Integer cookieMaxAge;                @Override        public void writeCookieValue(CookieValue cookieValue) {                HttpServletRequest request = cookieValue.getRequest();                // 从request域读取到cookie的maxAge属性                Object attribute = request.getAttribute(COOKIE_MAX_AGE);                if (attribute != null) {                        cookieValue.setCookieMaxAge((int) attribute);                } else {                        // 如果未设置,就使用默认cookie的生命周期                        cookieValue.setCookieMaxAge(this.cookieMaxAge);                }                if (LOGGER.isDebugEnabled()) {                        LOGGER.debug("动态设置cooke.max-age={}", cookieValue.getCookieMaxAge());                }                super.writeCookieValue(cookieValue);        }}

原理就是,把cookie的maxAge属性存储到request域。在响应客户端之前,动态的设置。

添加到IOC

import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.session.web.http.CookieSerializer;import com.video.manager.spring.session.DynamicCookieMaxAgeCookieSerializer;@Configurationpublic class SpringSessionConfiguration {                @Value("${server.servlet.session.cookie.name}")        private String cookieName;                @Value("${server.servlet.session.cookie.secure}")        private Boolean cookieSecure;        //      @Value("${server.servlet.session.cookie.max-age}")//      private Integer cookieMaxAge;                @Value("${server.servlet.session.cookie.http-only}")        private Boolean cookieHttpOnly;        @Value("${server.servlet.session.cookie.same-site}")        private String cookieSameSite;                @Bean        public CookieSerializer cookieSerializer() {                DynamicCookieMaxAgeCookieSerializer serializer = new DynamicCookieMaxAgeCookieSerializer();                serializer.setCookieName(this.cookieName);                // serializer.setCookieMaxAge(this.cookieMaxAge);                serializer.setSameSite(this.cookieSameSite);                serializer.setUseHttpOnlyCookie(this.cookieHttpOnly);                serializer.setUseSecureCookie(this.cookieSecure);                return serializer;        }}

使用 @Value,读取yml配置中的Cookie属性。

测试接口

import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.servlet.ModelAndView;import com.video.manager.spring.session.DynamicCookieMaxAgeCookieSerializer;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;@Controller@RequestMapping("/test")public class TestController {        static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);                @GetMapping("/session")        public ModelAndView session(HttpServletRequest request,                         @RequestParam("remember")Boolean remember) {                                HttpSession httpSession = request.getSession();                LOGGER.debug("httpSession={}", httpSession);                                if (!remember) {  // 不记住我                        // 设置cookie的生命周期为 -1                        request.setAttribute(DynamicCookieMaxAgeCookieSerializer.COOKIE_MAX_AGE, -1);                        // 设置session仅缓存30分钟                        httpSession.setMaxInactiveInterval(60 * 30);                }                                ModelAndView modelAndView = new ModelAndView("test/test");                return modelAndView;        }}

【记住我】√

http://localhost/test/session?remember=true

响应Cookie,存储时间是 7 天

redis的session存储,缓存时间是7天

【记住我】×

http://localhost/test/session?remember=false

响应Cookie,存储时间是:-1,临时会话设置成功,浏览器关闭Cookie删除

redis的session存储,缓存时间是30分钟,超过30分钟不活动,自动删除

关于spring-session中怎么动态修改cookie的max-age就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

0