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

1、代码实现

   
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        

            
                org.springframework.boot
                spring-boot-starter-data-redis-reactive
            
            
                org.springframework.boot
                spring-boot-starter-web
            
            
                org.springframework.session
                spring-session-data-redis
            
            
                org.springframework.boot
                spring-boot-starter-test
                test
            
    

简例:
 

@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;
    }
}

结果:

SSO-使用Spring session解决session共享问题_第1张图片

SSO-使用Spring session解决session共享问题_第2张图片

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 httpSessionListeners = new ArrayList();
	@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  SessionRepositoryFilter springSessionRepositoryFilter(
			SessionRepository sessionRepository) {
		SessionRepositoryFilter sessionRepositoryFilter = new SessionRepositoryFilter(
				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 listeners) {
		this.httpSessionListeners = listeners;
	}
}

单看springSessionRepositoryFilter方法

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

  1. 传入参数SessionRepository的实现类RedisOperationsSessionRepository在RedisHttpSessionConfiguration被进行创建所以sessionRepository使用的就是RedisOperationsSessionRepository用来做于存储
@Bean
public  SessionRepositoryFilter springSessionRepositoryFilter(
		SessionRepository sessionRepository) {
	SessionRepositoryFilter sessionRepositoryFilter = new SessionRepositoryFilter(
			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也进行了封装

几张图

SSO-使用Spring session解决session共享问题_第3张图片

SSO-使用Spring session解决session共享问题_第4张图片

SSO-使用Spring session解决session共享问题_第5张图片

你可能感兴趣的:(SSO,Tomcat)