首先说明一下低版本的shiro-spring是没有这个问题的,发现这个问题的shiro-spring是1.5.2版本
没有去考证过到哪个版本会出现这个问题,但是肯定的是新版本shiro-spring对cookie加上了SameSite属性
然后网上说的配置CookieSerializer方式是不会起作用的,原因是当引入shiro-spring之后,对于cookie的序列化操作会托管给shiroweb,而CookieSerializer是springweb中的序列化cookie的类,所以再怎么配置CookieSerializer都不会起作用,应当从shiro配置的角度出发去解决这个问题
在debug的时候发现最后cookie转成header时调用了SimpleCookie中的saveTo方法
public void saveTo(HttpServletRequest request, HttpServletResponse response) {
String name = getName();
String value = getValue();
String comment = getComment();
String domain = getDomain();
String path = calculatePath(request);
int maxAge = getMaxAge();
int version = getVersion();
boolean secure = isSecure();
boolean httpOnly = isHttpOnly();
SameSiteOptions sameSite = getSameSite();
addCookieHeader(response, name, value, comment, domain, path, maxAge, version, secure, httpOnly, sameSite);
}
可以看到,在此处shiro写入了SameSite属性,所以问题在于找出谁创建了SimpleCookie,那么只要设置这个SimpleCookie的SameSite为null就可以了。
再往下找到DefaultWebSessionManager这个类,在此类中调用了SimpleCookie的saveTo方法
private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
if (currentId == null) {
String msg = "sessionId cannot be null when persisting for subsequent requests.";
throw new IllegalArgumentException(msg);
}
Cookie template = getSessionIdCookie();
Cookie cookie = new SimpleCookie(template);
String idString = currentId.toString();
cookie.setValue(idString);
cookie.saveTo(request, response);
log.trace("Set session ID cookie for session with id {}", idString);
}
其中getSessionIdCookie方法为
public Cookie getSessionIdCookie() {
return sessionIdCookie;
}
而在构造器中可以看到sessionIdCookie是在创建DefaultWebSessionManager的时候被初始化的
public DefaultWebSessionManager() {
Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
cookie.setHttpOnly(true); //more secure, protects against XSS attacks
this.sessionIdCookie = cookie;
this.sessionIdCookieEnabled = true;
this.sessionIdUrlRewritingEnabled = true;
}
所以问题的解决办法在于自定义DefaultWebSessionManager,创建一个自定义的SimpleCookie将SameSite属性设置为空即可
解决代码如下,我这里的话因为集成了哨兵redis,所以会有部分不相关的代码,你可以根据自己需要删除或者修改这部分的代码,但是重点SimpleCookie那一部分,根据需要可以自定义cookie的属性
有问题可以发我邮件哦417168602@qq.com
@Bean
public SessionManager sessionManager(RedisSentinelManager redisManager) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
cookie.setHttpOnly(false);
cookie.setSameSite(null);
sessionManager.setSessionIdCookie(cookie);
sessionManager.setSessionIdCookieEnabled(true);
sessionManager.setSessionIdUrlRewritingEnabled(true);
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager);
sessionManager.setSessionDAO(redisSessionDAO);
return sessionManager;
}
@Bean(name = "securityManager")
public SecurityManager securityManager(RedisSentinelManager redisManager, SessionManager sessionManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
List<Realm> realms = new ArrayList<>();
realms.add(authRealm());
realms.add(adminRealm());
securityManager.setRealms(realms);
securityManager.setSessionManager(sessionManager);
RedisCacheManager redisCacheManager = new RedisCacheManager();
//缓存默认30分钟
redisCacheManager.setRedisManager(redisManager);
securityManager.setCacheManager(redisCacheManager);
return securityManager;
}