SSO-使用Spring session解决session共享问题

1、代码实现

   <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session-data-redis</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    </dependencies>

简例:
 

@RestController
@RequestMapping(value = "/session")
public class SessionShareController {
    @Value("${server.port}")
    Integer port;
    @GetMapping(value = "/set")
    public String set(HttpSession session){
        session.setAttribute("user","wangwq8");
        return String.valueOf(port);
    }
    @GetMapping(value = "get")
    public String get(HttpSession session){
        return "用户:"+session.getAttribute("user")+",端口:"+port;
    }
}

结果:

2、Spring session整个实现流程和源码详细介绍

主要两个配置类RedisHttpSessionConfigurationSpringHttpSessionConfiguration

 

查看@EnableRedisHttpSession

@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target({ java.lang.annotation.ElementType.TYPE })
@Documented
@Import(RedisHttpSessionConfiguration.class)
@Configuration
public @interface EnableRedisHttpSession {
...
}

查看RedisHttpSessionConfiguration

主要就是将操纵redis的redisTemplate放入sessionRepository中,而sessionRepository中统一规定了获取session的方法名。即可通过sessionRepository从redis里获取session。

@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
		implements EmbeddedValueResolverAware, ImportAware {
...
}

发现其继承自SpringHttpSessionConfiguration查看

  1. 发现其session默认的策略是使用 defaultHttpSessionStrategy=new CookieHttpSessionStrategy();cookie来实现
public class SpringHttpSessionConfiguration implements ApplicationContextAware {
	private CookieHttpSessionStrategy defaultHttpSessionStrategy = new CookieHttpSessionStrategy();
	private boolean usesSpringSessionRememberMeServices;
	private ServletContext servletContext;
	private CookieSerializer cookieSerializer;
	private HttpSessionStrategy httpSessionStrategy = this.defaultHttpSessionStrategy;
	private List<HttpSessionListener> httpSessionListeners = new ArrayList<HttpSessionListener>();
	@PostConstruct
	public void init() {
		if (this.cookieSerializer != null) {
			this.defaultHttpSessionStrategy.setCookieSerializer(this.cookieSerializer);
		}
		else if (this.usesSpringSessionRememberMeServices) {
			DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
			cookieSerializer.setRememberMeRequestAttribute(
					SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
			this.defaultHttpSessionStrategy.setCookieSerializer(cookieSerializer);
		}
	}
	@Bean
	public SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter() {
		return new SessionEventHttpSessionListenerAdapter(this.httpSessionListeners);
	}
	@Bean
	public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(
			SessionRepository<S> sessionRepository) {
		SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(
				sessionRepository);
		sessionRepositoryFilter.setServletContext(this.servletContext);
		if (this.httpSessionStrategy instanceof MultiHttpSessionStrategy) {
			sessionRepositoryFilter.setHttpSessionStrategy(
					(MultiHttpSessionStrategy) this.httpSessionStrategy);
		}
		else {
			sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);
		}
		return sessionRepositoryFilter;
	}
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		if (ClassUtils.isPresent(
				"org.springframework.security.web.authentication.RememberMeServices",
				null)) {
			this.usesSpringSessionRememberMeServices = !ObjectUtils
					.isEmpty(applicationContext
							.getBeanNamesForType(SpringSessionRememberMeServices.class));
		}
	}
	@Autowired(required = false)
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}
	@Autowired(required = false)
	public void setCookieSerializer(CookieSerializer cookieSerializer) {
		this.cookieSerializer = cookieSerializer;
	}
	@Autowired(required = false)
	public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
		this.httpSessionStrategy = httpSessionStrategy;
	}
	@Autowired(required = false)
	public void setHttpSessionListeners(List<HttpSessionListener> listeners) {
		this.httpSessionListeners = listeners;
	}
}

单看springSessionRepositoryFilter方法

这里生成了一个SessionRepositoryFilter过滤器,并将上一个配置类中的sessionRepository放入到这个过滤器中。所以这个过滤器就是替换服务器session的关键。

  1. 传入参数SessionRepository的实现类RedisOperationsSessionRepository在RedisHttpSessionConfiguration被进行创建所以sessionRepository使用的就是RedisOperationsSessionRepository用来做于存储
@Bean
public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(
		SessionRepository<S> sessionRepository) {
	SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(
			sessionRepository);
	sessionRepositoryFilter.setServletContext(this.servletContext);
	if (this.httpSessionStrategy instanceof MultiHttpSessionStrategy) {
		sessionRepositoryFilter.setHttpSessionStrategy(
				(MultiHttpSessionStrategy) this.httpSessionStrategy);
	}
	else {
		sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);
	}
	return sessionRepositoryFilter;
}

当执行过滤器

@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
 
		SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
				request, response, this.servletContext);
		SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
				wrappedRequest, response);
 
		HttpServletRequest strategyRequest = this.httpSessionStrategy
				.wrapRequest(wrappedRequest, wrappedResponse);
		HttpServletResponse strategyResponse = this.httpSessionStrategy
				.wrapResponse(wrappedRequest, wrappedResponse);
 
		try {
			filterChain.doFilter(strategyRequest, strategyResponse);
		}
		finally {
			wrappedRequest.commitSession();
		}
	}
  • 这个doFilterInternal方法重写的是OncePerRequestFilter抽象类的方法,而OncePerRequestFilter实现的是Filter接口,在doFilter方法中会调用doFilterInternal。
  • 这里先是将sessionRepository放入到了request的attribute中,然后又用框架中的类SessionRepositoryRequestWrapper对request进行了封装。这个类里有利用sessionRepository创建,获取,删除session的方法。
  • 同时对response也进行了封装

几张图

image

image

image

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值