【问题标题】:Session not replicated on session creation with Spring Boot, Session, and Redis使用 Spring Boot、Session 和 Redis 创建会话时未复制会话
【发布时间】:2015-12-23 13:37:53
【问题描述】:

我正在尝试使用 Spring Cloud 的 Zuul、Eureka 和我自己的服务来实现微服务架构。我有多个具有 UI 和服务的服务,每个服务都可以使用 x509 安全性对用户进行身份验证。现在我正试图将 Zuul 放在这些服务的前面。由于 Zuul 无法将客户端证书转发到后端,我认为下一个最好的方法是在 Zuul 的前门对用户进行身份验证,然后使用 Spring Session 在后端服务中复制他们的身份验证状态。我遵循了 Dave Syer 的教程here,它几乎可以工作,但不是第一次请求。这是我的基本设置:

  • Zuul 代理在它自己的应用程序集中路由到后端服务。启用了 Spring 安全性来执行 x509 身份验证。成功认证用户。还有带有 @EnableRedisHttpSession 的 Spring Session
  • 后端服务还启用了 spring 安全性。我在这里尝试过启用/禁用 x509,但始终要求用户针对特定端点进行身份验证。还使用 Spring Session 和 @EnableRedisHttpSession。

如果您清除所有会话并重新开始并尝试访问代理,那么它会使用 zuul 服务器的证书将请求发送到后端。然后后端服务根据该用户证书查找用户,并认为该用户是服务器,而不是在 Zuul 代理中经过身份验证的用户。如果你只是刷新页面,那么你突然变成了后端的正确用户(在 Zuul 代理中认证的用户)。我正在检查的方式是在后端控制器中打印出 Principal 用户。所以在第一次请求时,我看到了服务器用户,在第二次请求时,我看到了真实用户。如果我在后端禁用 x509,在第一个请求时,我会得到 403,然后在刷新时,它会让我进入。

似乎会话复制到后端的速度不够快,因此当用户在前端进行身份验证时,Zuul 转发请求时它还没有到达后端。

有没有办法保证会话在第一次请求时被复制(即会话创建)?还是我错过了确保其正常工作的步骤?

这里是一些重要的sn-ps代码:

Zuul 代理:

@SpringBootApplication
@Controller
@EnableAutoConfiguration
@EnableZuulProxy
@EnableRedisHttpSession
public class ZuulEdgeServer {
    public static void main(String[] args) {
        new SpringApplicationBuilder(ZuulEdgeServer.class).web(true).run(args);
    }
}

Zuul 配置:

info:
  component: Zuul Server

endpoints:
  restart:
    enabled: true
  shutdown:
    enabled: true
  health:
    sensitive: false

zuul:
  routes:
    service1: /**

logging:
  level:
    ROOT: INFO
#    org.springframework.web: DEBUG
    net.acesinc: DEBUG

security.sessions: ALWAYS
server:
  port: 8443
  ssl:
      key-store: classpath:dev/localhost.jks
      key-store-password: thepassword
      keyStoreType: JKS
      keyAlias: localhost
      clientAuth: want
      trust-store: classpath:dev/localhost.jks

ribbon:
    IsSecure: true

后端服务:

@SpringBootApplication
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, ThymeleafAutoConfiguration.class, org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class })
@EnableEurekaClient
@EnableRedisHttpSession
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

后端服务配置:

spring.jmx.default-domain: ${spring.application.name}

server:
  port: 8444
  ssl:
      key-store: classpath:dev/localhost.jks
      key-store-password: thepassword
      keyStoreType: JKS
      keyAlias: localhost
      clientAuth: want
      trust-store: classpath:dev/localhost.jks

#Change the base url of all REST endpoints to be under /rest
spring.data.rest.base-uri: /rest

security.sessions: NEVER

logging:
  level:
    ROOT: INFO
#    org.springframework.web: INFO
#    org.springframework.security: DEBUG
    net.acesinc: DEBUG

eureka:
  instance: 
    nonSecurePortEnabled: false
    securePortEnabled: true
    securePort: ${server.port}
    homePageUrl: https://${eureka.instance.hostname}:${server.port}/
    secureVirtualHostName: ${spring.application.name}

后端控制器之一:

@Controller
public class SecureContent1Controller {
    private static final Logger log = LoggerFactory.getLogger(SecureContent1Controller.class);

    @RequestMapping(value = {"/secure1"}, method = RequestMethod.GET)
    @PreAuthorize("isAuthenticated()")
    public @ResponseBody String getHomepage(ModelMap model, Principal p) {
        log.debug("Secure Content for user [ " + p.getName() + " ]");
        model.addAttribute("pageName", "secure1");
        return "You are: [ " + p.getName() + " ] and here is your secure content: secure1";
    }
}

【问题讨论】:

  • 您好 Andrew,您的问题的解决方案在另一个问题 stackoverflow.com/questions/34751700/… 中进行了描述
  • 太棒了!它似乎正在工作!我已经有一半的解决方案,并认为我已经尝试了第二部分,但我想不是因为它现在正在工作!再次感谢您指出这一点!

标签: spring spring-security spring-cloud spring-session


【解决方案1】:

感谢 shobull 将我指向 Justin Taylor's answer 来解决这个问题。为了完整起见,我也想把完整的答案放在这里。这是一个两部分的解决方案:

  1. 让 Spring Session 热切提交 - 从 spring-session v1.0 开始,有注释属性@EnableRedisHttpSession(redisFlushMode = RedisFlushMode.IMMEDIATE) 可以立即将会话数据保存到 Redis。文档here。
  2. 用于将会话添加到当前请求标头的简单 Zuul 过滤器:

    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import javax.servlet.http.HttpSession;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.session.Session;
    import org.springframework.session.SessionRepository;
    import org.springframework.stereotype.Component;
    
    @Component
    public class SessionSavingZuulPreFilter extends ZuulFilter {
        @Autowired
        private SessionRepository repository;
    
        private static final Logger log = LoggerFactory.getLogger(SessionSavingZuulPreFilter.class);
    
        @Override
        public String filterType() {
            return "pre";
        }
    
        @Override
        public int filterOrder() {
            return 1;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() {
            RequestContext context = RequestContext.getCurrentContext();
    
            HttpSession httpSession = context.getRequest().getSession();
            Session session = repository.getSession(httpSession.getId());
    
            context.addZuulRequestHeader("Cookie", "SESSION=" + httpSession.getId());
    
            log.trace("ZuulPreFilter session proxy: {}", session.getId());
    
            return null;
        }
    }
    

这两个都应该在您的 Zuul 代理中。

【讨论】:

    【解决方案2】:

    Spring Session 支持当前在提交请求时写入数据存储。这是为了尝试通过一次写入所有属性来减少“喋喋不休的流量”。

    众所周知,这对于某些情况(例如您所面临的情况)并不理想。对于这些我们有spring-session/issues/250。解决方法是复制RedisOperationsSessionRepository 并在任何时候更改属性时调用saveDelta。

    【讨论】:

    • 我猜你的字面意思是“复制” RedisOperationsSessionRepository 因为 RedisSession 不能被覆盖?此外,您尝试链接到 saveDelta 的行号不存在。我假设您的意思是在 setAttribute() 方法的末尾调用 saveDelta ?然后,你只是注册一个 RedisOperationsSessionRepository 类型的 bean 来覆盖默认值吗?
    猜你喜欢
    • 2018-03-14
    • 2017-05-23
    • 2018-12-16
    • 2015-05-07
    • 2022-10-13
    • 1970-01-01
    • 2021-11-10
    • 2019-08-08
    • 2016-03-03
    相关资源
    最近更新 更多