溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

springboot中如何整合spring-session

發(fā)布時間:2021-06-18 17:41:17 來源:億速云 閱讀:468 作者:Leah 欄目:大數(shù)據(jù)

這期內(nèi)容當中小編將會給大家?guī)碛嘘P(guān)springboot中如何整合spring-session,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。


springboot中整合使用spring-session

spring-session

它可以替代 HttpSesession。而且改動極小,對應用透明。底層可以使用內(nèi)存,Redis等存儲Session信息。通過Redis這種方式可以做到Session共享,在集群環(huán)境中所有節(jié)點共享Session。

文檔 https://docs.spring.io/spring-session/docs/current/reference/html5/

SpringBoot整合

使用 spring-session-data-redis,一定要先整合redis到項目。

依賴
<dependency>
	<groupId>org.springframework.session</groupId>
	<artifactId>spring-session-data-redis</artifactId>
</dependency>
配置

配置類:RedisSessionProperties

spring:
  session:
    timeout: 1800 # session的過期時間,單位是秒
    store-type: REDIS # session的存儲類型,枚舉
    redis:
      namespace: "spring:session" # session存儲在redis中的命名空間
      flush-mode: IMMEDIATE # 刷出模式,枚舉:ON_SAVE ,IMMEDIATE
      cleanup-cron: "0 * * * * *" # 定時清理過期session任務的`cron`表達式

關(guān)于 spring.session.redis.flush-mode

ON_SAVE

只有當 SessionRepository.save(Session) 方法被調(diào)用時, 才會將session中的數(shù)據(jù)同步到redis中. 在web 應用中, 當請求完成響應后, 才開始同步. 也就是說在執(zhí)行response 之前session數(shù)據(jù)都是緩存在本地的。

IMMEDIATE

當執(zhí)行SessionRepository.createSession()時, 會將session數(shù)據(jù)同步到redis中; 當對session的attribute進行set/remove 等操作時, 也會同步session中的數(shù)據(jù)到redis中。它是實時同步的

使用

使用并需要修改什么,像平時一樣。獲取到 Servlet的Session就是了。

controller
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@RequestMapping("/test")
@Controller
public class TestController {
	
	@GetMapping("/session")
	public ModelAndView session(HttpServletRequest request) {
		
		HttpSession httpSession = request.getSession();
		
		// 是否新創(chuàng)建的 true
		System.out.println(httpSession.isNew());
		
		// 實現(xiàn)類 org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper
		System.out.println(httpSession.getClass().getName());
		
		// 寫入數(shù)據(jù)到session(底層使用redis存儲)
		httpSession.setAttribute("name", "SpringBoot中文社區(qū)");
		
		return new ModelAndView("test/test");
	}
}
View
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>Test</title>
	</head>
	<body>
		Hello ${name}	
	</body>
</html>

springboot中如何整合spring-session

Cookie屬性的修改

默認情況下,客戶端使用Cookie來存儲會話id

查看響應信息,可以看到設(shè)置了session的cookie
Connection: keep-alive
Content-Encoding: gzip
Content-Language: zh-CN
Content-Type: text/html;charset=UTF-8
Date: Thu, 17 Oct 2019 08:57:07 GMT
Server: nginx
Set-Cookie: PHPSESSIONID=Y2YwMDM1YjctMjBiYy00OWRiLWI5NGItZjFmNDU4ZDcxNThm; Max-Age=36000; Expires=Thu, 17 Oct 2019 18:57:07 GMT; Path=/; HttpOnly; SameSite=Lax
Transfer-Encoding: chunked
可以通過配置修改cookie的信息。
server:
  servlet:
	session:
	  cookie:
	    name: PHPSESSIONID #cookie名稱
	    domain:  # 域
	    path:  # 路徑
	    comment: # 備注
	    httpOnly: # 是否僅用于http傳輸
	    secure: # 是否僅在SSL的情況下傳輸
	    maxAge: # 生命周期
也可以通過配置修改cookie信息

自定義 CookieSerializer 到IOC。

@Bean
public CookieSerializer cookieSerializer() {
	DefaultCookieSerializer serializer = new DefaultCookieSerializer();
	serializer.setCookieName("JSESSIONID"); 
	serializer.setCookiePath("/"); 
	serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$"); 
	return serializer;
}

Session的索引

通俗的理解就是,可以建立key和session的索引,根據(jù)某些key獲取到session。

FindByIndexNameSessionRepository

需求:根據(jù)用戶id,獲取到它的會話

用戶登錄成功后,把它的id以固定的key存入到Session

value僅僅接受字符串

@Autowired
FindByIndexNameSessionRepository<? extends Session> sessions;
.... 代碼省略
Integer userId = user.getId();
// 往session中存入用戶的id信息
request.getSession().setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, userId + "");
根據(jù)用戶id檢索它的session信息
@Autowired
FindByIndexNameSessionRepository<? extends Session> sessions;
.... 代碼省略
Integer userId = user.getId();
// 返回該用戶的所有的有效session
Map<String, ? extends Session> sessions = this.sessions.findByPrincipalName(userId + "");
for (Session session : sessions.values()) {
	// 根據(jù)sessionId刪除
	this.sessions.deleteById(session.getId());
}
FindByIndexNameSessionRepository 接口源碼
public interface FindByIndexNameSessionRepository<S extends Session> extends SessionRepository<S> {

	String PRINCIPAL_NAME_INDEX_NAME =FindByIndexNameSessionRepository.class.getName()
			.concat(".PRINCIPAL_NAME_INDEX_NAME");

	Map<String, S> findByIndexNameAndIndexValue(String indexName, String indexValue);
	
	default Map<String, S> findByPrincipalName(String principalName) {

		return findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principalName);

	}
}

可以通過findByIndexNameAndIndexValue方法自己建立key和session的索引信息。

事件的監(jiān)聽

session過期,銷毀事件依賴于redis的key過期通知。事件對象通過spring的事件訂閱發(fā)布機制來發(fā)布

事件對象
SessionCreatedEvent				創(chuàng)建
SessionDestroyedEvent
	|-SessionExpiredEvent		過期
	|-SessionDeletedEvent		刪除(用戶主動 invalidate())
監(jiān)聽器的實現(xiàn)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.stereotype.Component;

@Component
public class SpringSessionListener {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(SpringSessionListener.class);
	
	@EventListener(SessionCreatedEvent.class)
	@Async
	public void sessionCreatedEvent(SessionCreatedEvent sessionCreatedEvent) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("session 創(chuàng)建:{}", sessionCreatedEvent.getSessionId());
		}
	}
	
	@EventListener(SessionExpiredEvent.class)
	public void sessionExpiredEvent(SessionExpiredEvent sessionCreatedEvent) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("session 到期:{}", sessionCreatedEvent.getSessionId());
		}
	}
	
	@EventListener(SessionDeletedEvent.class)
	public void sessionDeletedEvent(SessionDeletedEvent sessionCreatedEvent) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("session 刪除:{}", sessionCreatedEvent.getSessionId());
		}
	}
}
使用Servlet的監(jiān)聽器

比較麻煩,需要自己通過代碼配置添加

  • 需要添加SessionEventHttpSessionListenerAdapter到ioc, 通過這個bean的構(gòu)造函數(shù), 添加多個 HttpSessionListener 實現(xiàn) SessionEventHttpSessionListenerAdapter SessionEventHttpSessionListenerAdapter(List<HttpSessionListener> listeners)

  • 但是這個Bean其實框架已經(jīng)自動添加了, 再次添加會導致異常

  • 曲線救國, 從IOC里面讀取到這個bean, 通過反射, 對私有屬性 listeners 添加監(jiān)聽器

@Configuration
public class SpringSessionConfiguration {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(SpringSessionConfiguration.class);
	
	@Autowired SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter;
	
	@PostConstruct
	public void addHttpSessionListener() {
		try {
			Field field = SessionEventHttpSessionListenerAdapter.class.getDeclaredField("listeners");
			field.setAccessible(Boolean.TRUE);
			
			@SuppressWarnings("unchecked")
			List<HttpSessionListener> listeners = (List<HttpSessionListener>) field.get(sessionEventHttpSessionListenerAdapter);
			listeners.add(new SessionListener());
			
			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("添加SESSION監(jiān)聽器");
			}
			
		} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
			e.printStackTrace();
		}
	}
	
	
//	@Bean //BeanDefinitionOverrideException
//	public SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter() {
//		SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter = new SessionEventHttpSessionListenerAdapter(Arrays.asList(new SessionListener()));
//		return sessionEventHttpSessionListenerAdapter;
//	}
}

使用Http Header來解析session id

默認客戶端使用Cookie來存儲session id。但是對于一些客戶端來說,cookie不一定方便,可以通過 http header來攜帶cookie的id。

Session的解析依賴于一個接口: HttpSessionIdResolver

實現(xiàn)類 CookieHttpSessionIdResolver 使用Cookie(默認) HeaderHttpSessionIdResolver 使用Header

配置通過 HttpHeader 來解析session id
@Bean
public HttpSessionIdResolver httpSessionIdResolver() {
	return HeaderHttpSessionIdResolver.xAuthToken();  // 使用 X-Auth-Token 解析Cookie
}

HeaderHttpSessionIdResolver 還支持自定義header的名稱,代碼及簡單,可以自己閱讀學習。

spring-session在redis中數(shù)據(jù)的存儲結(jié)構(gòu)

spring:session:expirations:1570672200000
spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:1
spring:session:sessions:d82bf2bb-deb3-474c-89bb-96c2bfa646c4
spring:session:sessions:expires:94b2ce1f-053e-4c20-a8b7-f4d69102a114

上述就是小編為大家分享的springboot中如何整合spring-session了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI