1. Предпосылки
Автор увидел проблему совместного использования сеанса, упомянутую пользователями сети в группе обсуждения, поэтому я проверил документ в частном порядке и, наконец, заблокировал использование Spring Session для решения проблемы и записал его здесь.
2. Введение
Spring Session предоставляет набор API для управления информацией о сеансе пользователя.
Использование Spring Session может заменить реализацию HttpSession в контейнере проекта. Кроме того, Spring Session также предоставляет следующие функции:
1) Кластеризация сеансов: Spring Session упрощает поддержку кластеризованных сеансов. С Spring Session нам не нужно привязывать проекты к конкретным приложениям для сеансов кластеризации.
2) Мультибраузерные сеансы: Spring Session предоставляет решение для управления несколькими сеансами в одном браузере.
3) RESTful API: Spring Session позволяет добавить идентификатор сеанса в заголовок запроса для реализации RESTful API.
В этой статье представлена функция первой точки.
Схематическая диаграмма выглядит следующим образом:
После того, как браузер инициирует запрос, tomcat (указанный распределением балансировки нагрузки на конкретный tomcat) получает идентификатор сеанса в запросе и находит соответствующий объект Session в контейнере хранилища сеансов.
Для контейнера хранилища сеансов Spring Session предоставляет множество решений:
1) HttpSession with Redis
2) HttpSession with Pivotal GemFire
3) HttpSession with JDBC
4) HttpSession with Mongo
5) HttpSession with Hazelcast
3. Реализация
JDK: 1.8
Контейнер: Томкэт 8
Контейнер хранилища сеансов: Redis 3.2.0
В этом тесте используется решение HttpSession with Redis.Для удобства тестирования автор развертывает проект на двух котах на одной виртуальной машине и использует для запуска порты 8080 и 8081.
3.1 Добавить зависимости
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.9.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
Связанные банки, такие как Spring, здесь опущены.
3.2 файл applicationContext-session.xml
<context:annotation-config/>
<!-- redis 连接池 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="20"></property>
<property name="maxIdle" value="1"></property>
</bean>
<!-- redis 连接工厂 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
destroy-method="destroy">
<property name="hostName" value="192.168.2.11"/>
<property name="port" value="6379"/>
<property name="timeout" value="5000"/>
<property name="password" value=""/>
<property name="usePool" value="true"/>
<property name="poolConfig" ref="jedisPoolConfig"/>
</bean>
<!-- spring session 配置 -->
<bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="600"/>
</bean>
Конфигурация в applicationContext-session.xml заставляет Spring создавать bean-компонент (фильтр) с именем springSessionRepositoryFilter при запуске проекта, и bean-компонент отвечает за замену реализации HttpSession на Spring Session . Spring Session использует Redis для хранения информации о сеансе на стороне клиента.
3.3 файл web.xml
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
После того, как каждый клиентский запрос проходит через DelegatingFilterProxy, DelegatingFilterProxy будет искать bean-компонент с именем springSessionRepositoryFilter в контейнере Spring и преобразовывать его в фильтр для фильтрации данных запроса.
3.4 Внутренний код
@Controller
public class LoginController {
@RequestMapping("login")
public String login(String userName, String password,HttpServletRequest request) {
// 首次登录
if ("admin".equals(userName) && "admin".equals(password)) {
HttpSession session = request.getSession();
session.setAttribute("userName", userName);
return "manageUI";
}
// 如果已经登录过,从另一个 tomcat 访问该方法,跳转到 manageUI 页面可以查看 session 信息
if ("".equals(userName) && "".equals(password)) {
return "manageUI";
}
return "redirect:/index.jsp";
}
@RequestMapping("logout")
public String logout(HttpSession session) {
session.removeAttribute("userName");
session.removeAttribute("url");
return "redirect:/index.jsp";
}
}
3.5 Интерфейсный код
страница index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>登陆界面</title>
<link href="/resources/css/bootstrap.min.css" rel="stylesheet">
<style>
html {
background: url("/resources/images/bg.png") no-repeat center center;
}
label {
color: #fff;
}
.container {
position:absolute;
top:50%;
left:50%;
margin-top: -115px;
margin-left: -250px;
width: 500px;
height:230px;
padding:50px;
border: 2px solid #eee;
border-radius: 5px;
box-shadow:5px 5px 16px #000;
}
</style>
</head>
<body>
<div class="container">
<form class="form-horizontal" role="form" action="/login" method="post">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">用户名</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="userName" placeholder="用户名">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">密码</label>
<div class="col-sm-10">
<input type="password" class="form-control" name="password" placeholder="密码">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary" style="width: 100%">登陆</button>
</div>
</div>
</form>
</div>
</body>
</html>
страница manageUI.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>管理界面</title>
<link href="/resources/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="jumbotron">
<h3>端口为 8080 的页面</h3>
<h3>用户名:${sessionScope.userName}(session 域数据)</h3>
<p><a class="btn btn-lg btn-success" href="/logout" role="button">注销</a></p>
</div>
</div>
</body>
</html>
Примечание. Страницу проекта 8081 необходимо изменить на «страницу с портом 8081».
4. Демонстрация
ожидаемый результат:
1) Сначала войдите в проект через порт 8080 и войдите в систему, перейдите к интерфейсу управления и отобразите сохраненную информацию.
2) Посетите страницу проекта порта 8081 в том же браузере, нажмите кнопку входа в систему, не вводя пароль учетной записи, и он перейдет непосредственно к интерфейсу управления. Если сеанс является общим, вы можете просмотреть информацию, сохраненную в сеансе проектом порта 8080, в интерфейсе управления. Иначе наоборот.
Демонстрационная схема выглядит следующим образом:
Использование Spring Session действительно удобно и просто, нам не нужно дополнительно изучать API, нам нужно только настроить функцию.