spring之session
Spring Session的目标是从存储在服务器中的HTTP会话的限制中释放会话管理。
该解决方案可以轻松地在独立部署的服务之间共享会话数据,而不必绑定到单个容器(即Tomcat)。 此外,它支持在同一浏览器中的多个会话并在标头中发送会话。
在本文中,我们将使用Spring Session来管理Web应用程序中的身份验证信息。 虽然Spring Session可以使用JDBC或MongoDB来持久保存数据,但我们将使用Redis。
首先创建一个简单的Spring Boot项目,方便在后边的会话示例中使用:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
我们的应用程序使用Spring Boot运行,父pom为每个条目提供版本。 可以在Maven中心仓库找到每个依赖项的最新版本。
还需要在application.properties中为我们的Redis服务器添加一些配置属性:
spring.redis.database=0
spring.redis.host=host
spring.redis.port=6379
spring.redis.password=password
首先,演示使用Boot配置Spring Session。
注意:不需要完成第3节和第4节。只需选择一个,具体取决于您是否使用SpringBoot配置Spring Session。
3.1:依赖管理
添加以下依赖到我们的项目中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
</dependency>
我们使用启动父pom在这里设置版本,因此这些版本可以保证与我们的其他依赖项一起使用。
3.2:Spring会话配置
现在为Spring Session添加一个配置类:
@Configuration
@EnableRedisHttpSession
public
class
SessionConfig
extends
AbstractHttpSessionApplicationInitializer
{
}
我们看一下不使用Spring Boot,使用传统方式实现spring集成和配置spring-session。
4.1:依赖管理
首先,如果我们将spring-session添加到标准的Spring项目中,我们需要明确定义:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
4.2:spring session配置
现在为Spring Session添加一个配置类:
@Configuration
@EnableRedisHttpSession
public
class
SessionConfig
extends
AbstractHttpSessionApplicationInitializer
{
@Bean
public
JedisConnectionFactory connectionFactory()
{
return
new
JedisConnectionFactory();
}
}
正如你所看到的最小差异-我们现在必须明确定义JedisConnectionFactory bean,而SpringBoot帮我们做了。
在两种类型中,@EnableRedisHttpSession和AbstractHttpSessionApplicationInitializer的扩展实现,将在所有安全基础结构之前创建并连接一个过滤器,
用以查找活跃会话,并用存储在Redis中的值来填充安全上下文。
接下来我们用控制器和安全配置来完成这个应用程序。
添加一个控制器:
@RestController
public
class
SessionController
{
@RequestMapping("/")
public
String helloAdmin()
{
return
"hello admin";
}
}
这个控制器为我们提供一个测试端点。
接下来,添加我们的安全配置类:
@Configuration
@EnableWebSecurity
public
class
SecurityConfig
extends
WebSecurityConfigurerAdapter
{
@Autowired
public
void configureGlobal(AuthenticationManagerBuilder auth)
throws
Exception
{
auth
.inMemoryAuthentication()
.withUser("admin").password("password").roles("ADMIN");
}
@Override
protected
void configure(HttpSecurity http)
throws
Exception
{
http
.httpBasic().and()
.authorizeRequests()
.antMatchers("/").hasRole("ADMIN")
.anyRequest().authenticated();
}
}
这可以通过基本身份验证保护我们的端点,并设置要测试的用户。
最后,对上述内容进行测试,我们将在这里定义一个简单的测试,这将允许我们做两件事:
测试准备:
public
class
SessionControllerTest
{
private
Jedis jedis;
private
TestRestTemplate testRestTemplate;
private
TestRestTemplate testRestTemplateWithAuth;
private
String testUrl =
"http://localhost:8080/";
@Before
public
void clearRedisData()
{
testRestTemplate =
new
TestRestTemplate();
testRestTemplateWithAuth =
new
TestRestTemplate("admin",
"password",
null);
jedis =
new
Jedis("host",
6379);
jedis.flushAll();
}
}
需要注意的是,我们如何设置这两个客户端-HTTP客户端和Redis客户端。 当然,此时服务器(和Redis)应该启动并运行-这样我们就可以通过这些测试与它们进行通信。
从测试Redis是空的开始:
@Test
public
void testRedisIsEmpty()
{
Set<String> result = jedis.keys("*");
assertEquals(0, result.size());
}
现在测试我们的安全保护为未经身份验证的请求返回401:
@Test
public
void testUnauthenticatedCantAccess()
{
ResponseEntity<String> result = testRestTemplate.getForEntity(testUrl,
String.class);
assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode());
}
接下来,我们测试Spring Session是否在管理我们的身份验证令牌:
@Test
public
void testRedisControlsSession()
{
ResponseEntity<String> result = testRestTemplateWithAuth.getForEntity(testUrl,
String.class);
assertEquals("hello admin", result.getBody());
//login worked
Set<String> redisResult = jedis.keys("*");
assertTrue(redisResult.size()
>
0);
//redis is populated with session data
String sessionCookie = result.getHeaders().get("Set-Cookie").get(0).split(";")[0];
HttpHeaders headers =
new
HttpHeaders();
headers.add("Cookie", sessionCookie);
HttpEntity<String> httpEntity =
new
HttpEntity<>(headers);
result = testRestTemplate.exchange(testUrl,
HttpMethod.GET, httpEntity,
String.class);
assertEquals("hello admin", result.getBody());
//access with session works worked
jedis.flushAll();
//clear all keys in redis
result = testRestTemplate.exchange(testUrl,
HttpMethod.GET, httpEntity,
String.class);
assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode());
//access denied after sessions are removed in redis
}
首先,我们的测试使用管理员身份验证凭据确认我们的请求成功。
然后我们从响应头中提取会话值,并在第二个请求中将其用作我们的身份验证。 验证之后清除Redis中的所有数据。
最后,我们使用会话cookie发出另一个请求并确认已注销。 这证实了Spring Session正在管理我们的会话。
总结
Spring Session是一个用于管理HTTP会话的强大工具。 通过将会话存储简化为配置类和几个Maven依赖项,我们现在可以将多个应用程序连接到同一个Redis实例并共享身份验证信息。
比如为了应对高并发请求,应用集群部署,那么应用的多个部署实例可以共享同一份session信息。
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!