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

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

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

spring-session的使用

由“记住我”引发的一个问题

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

【记住我】× 一般会把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属性,核心方法在于。

@Override

public 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;

// @Component

public 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;

@Configuration

public 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;

}

}

【记住我】√

响应Cookie,存储时间是 7 天

e2d81bbd37f1395efbebaf5ffc19e6e8.png

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

463f06a1252f00360dadb5a8bd4c0b51.png

【记住我】×

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

a9e1737e173e32471c621de6bbee9f02.png

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

79774b63051cbae07a8c58730ce13c4e.png

End

spring-session 动态的设置Cookie的max-age属性,我目前就想到了这种解决方式。你如果有更优雅的方案。记得告诉我。:see_no_evil:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值