# springboot-session-redis
**Repository Path**: mirrors_gspandy/springboot-session-redis
## Basic Information
- **Project Name**: springboot-session-redis
- **Description**: springboot+springsession+redis实现session共享
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-09-24
- **Last Updated**: 2025-10-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# springboot-session-redis
springboot+springsession+redis实现session共
* [1、springboot+springsession+redis](#session)
* [2、feign框架导致session共享失效](#feign)
springboot+springsession+redis
# 1、引入springsession和springredis的依赖
```
org.springframework.boot
spring-boot-starter-redis
org.springframework.session
spring-session-data-redis
```
# 2、在启动Application中添加@EnableRedisHttpSession注解
该注解的作用,就是引入springsession管理,同时实现是采用redis管理session的方式。
```
@SpringBootApplication
@EnableRedisHttpSession//增加redissession缓存支持
public class ServiceOneApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceOneApplication.class,args);
}
}
```
# 3、修改application.yml,添加redis的配置信息
```
spring:
redis:
host: localhost
port: 6379
server:
port: 8080
```
# 4、测试controller,分别设置session属性和获取
```
/**
* session设置
* @param key
* @param value
* @param request
* @return
*/
@ResponseBody
@RequestMapping("/setSession/{key}/{value}")
public String setSession(@PathVariable String key , @PathVariable String value,
HttpServletRequest request){
request.getSession().setAttribute(key,value);
Enumeration headers = request.getHeaderNames();
while (headers.hasMoreElements()){
String name = headers.nextElement();
System.out.println(name + ":"+ request.getHeader(name));
}
System.out.println(request.getSession().getId());
return request.getSession().getId();
}
/**
* 读取session
* @param key
* @param request
* @return
*/
@ResponseBody
@RequestMapping("/getSession/{key}")
public String getSession(@PathVariable String key ,HttpServletRequest request){
return request.getSession().getAttribute(key) + "---- sessionId:" + request.getSession().getId() ;
}
```
启动两个实例,分别访问发现获取到的sessionid是一致的。
# 5、说明
经过源码分析,springsession+redis关键是通过@EnableRedisHttpSession注解引入的,主要是通过SessionRepositoryFilter进行session的预处理,整个过程还是通过
原生的Cookie中获取SessionID实现。因此我们需要对session做一些特殊操作的时候,需要考虑SessionRepositoryFilter的级别和顺序。
feign框架导致session丢失问题
# 1、引入feign依赖
```
org.springframework.cloud
spring-cloud-starter-feign
```
# 2、声明feignrpc接口
注意:这里feign接口声明只需要能够表达出来调用的url/path/params等信息就可以了,不要携带额外的信息,否则会报错。因为feign本身
是一个rpc框架,目的是为了实现rpc调用,与dubbo等不同,不是接口声明式的,它是restful风格的,因此它要模拟的是浏览器,不是javabean
```aidl
//这里不要出现与调用无关的任何信息,哪怕这些信息是真正实现时要用到的。
@FeignClient(name = "session",url = "http://localhost:8090")
public interface ControllerInterface {
@RequestMapping("/setSession/{key}/{value}")
public String setSession(@PathVariable("key") String key , @PathVariable("value") String value);
@RequestMapping("/getSession/{key}")
public String getSession(@PathVariable(name = "key") String key );
}
```
# 3、在application中增加@EnableFeignClients注解,支持feign
```aidl
@SpringBootApplication
@EnableRedisHttpSession//增加redissession缓存支持
@EnableFeignClients//增加feign支持,引入feign注解,feign扫描路径可以单独指定(basePackages = ),默认是spring的扫描路径
public class ServiceOneApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceOneApplication.class,args);
}
}
```
# 4、在用到的地方,像注入bean一样使用它
```aidl
@Autowired
private ControllerInterface controllerInterface;
/**
* 测试feign的session问题。
* @param key
* @return
*/
@ResponseBody
@RequestMapping("/testFeign/{key}")
public String testFeign(@PathVariable String key,HttpServletRequest request) {
return controllerInterface.getSession(key);
}
```
到这一步,一个普通的feign调用示例就结束了,因为它倾向于restful风格的,因此默认的是无状态的,不会向下游携带额外状态信息,实际开发中我们又需要携带如登录
信息一样的状态信息,该怎么办呢?Feign早就想到了,我们看下源码中的RequestInterceptor怎么说的
```aidl
/**
*
* Zero or more {@code RequestInterceptors} may be configured for purposes such as adding headers to
* all requests. No guarantees are give with regards to the order that interceptors are applied.
* Once interceptors are applied, {@link Target#apply(RequestTemplate)} is called to create the
* immutable http request sent via {@link Client#execute(Request, feign.Request.Options)}.
* For example:
*
* public void apply(RequestTemplate input) {
* input.replaceHeader("X-Auth", currentToken);
* }
*
*
*
Configuration
{@code RequestInterceptors} are configured via {@link
* Feign.Builder#requestInterceptors}.
Implementation notes
Do not add
* parameters, such as {@code /path/{foo}/bar } in your implementation of {@link
* #apply(RequestTemplate)}.
Interceptors are applied after the template's parameters are
* {@link RequestTemplate#resolve(java.util.Map) resolved}. This is to ensure that you can
* implement signatures are interceptors.
Relationship to Retrofit 1.x
* This class is similar to {@code RequestInterceptor.intercept()}, except that the implementation
* can read, remove, or otherwise mutate any part of the request template.
*/
public interface RequestInterceptor {
/**
* Called for every request. Add data using methods on the supplied {@link RequestTemplate}.
*/
void apply(RequestTemplate template);
}
```
* Zero or more {@code RequestInterceptors} may be configured for purposes such as adding headers to
* all requests. No guarantees are give with regards to the order that interceptors are applied.
* Once interceptors are applied, {@link Target#apply(RequestTemplate)} is called to create the
* immutable http request sent via {@link Client#execute(Request, feign.Request.Options)}.
* For example:
*
* public void apply(RequestTemplate input) {
* input.replaceHeader("X-Auth", currentToken);
* }
注意标红的部分,大意是我们有时候需要一个或者多个RequestInterceptor去配置诸如请求头信息,还给出了授权的头部配置。
到这里我们就明白了,我们需要实现一个RequestInterceptor,在里面将原来的请求头信息付给下游请求,实际上就是Cookie信息,
这样sessionId就传到下游了,也就实现了共享。
# 5、实现RequestInterceptor
```aidl
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
/**
* 实现RequestInterceptor,用于设置feign全局请求模板
*/
@Component
public class FeignRequestIntercepter implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
//通过RequestContextHolder获取本地请求
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null){
return;
}
//获取本地线程绑定的请求对象
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
//给请求模板附加本地线程头部信息,主要是cookie信息
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()){
String name = headerNames.nextElement();
requestTemplate.header(name,request.getHeader(name));
}
}
}
```
# 6、验证
在浏览器中分别输入
http://localhost:8080/setSession/key/hello
http://localhost:8090/getSession/key
http://localhost:8080/testFeign/key
可以看到,打出的sessionID是相同的,而且都可以获取到session属性。
到这一步就基本解决了session丢失问题。至于网上说的熔断的异步化,导致session信息获取不到的问题,我们这里不做熔断,因此可以不考虑。