Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring security 如何防止Spring生成默认simpSessionId?_Spring Security_Spring Websocket_Spring Messaging - Fatal编程技术网

Spring security 如何防止Spring生成默认simpSessionId?

Spring security 如何防止Spring生成默认simpSessionId?,spring-security,spring-websocket,spring-messaging,Spring Security,Spring Websocket,Spring Messaging,我正试着用网袋和脚踩来设置spring 在客户机上,我发送一个头变量 “simpSessionId”:%session_id% 但是,在接收到消息时,spring始终将提供的头放在名为nativeHeaders的键中,并将默认simpSessionId放在头根中 {simpMessageType=MESSAGE, stompCommand=SEND, nativeHeaders={SPRING.SESSION.ID=[5b1f11d0-ad92-4855-ae44-b2052ecd76d8],

我正试着用网袋和脚踩来设置spring

在客户机上,我发送一个头变量 “simpSessionId”:%session_id%

但是,在接收到消息时,spring始终将提供的头放在名为nativeHeaders的键中,并将默认simpSessionId放在头根中

{simpMessageType=MESSAGE, stompCommand=SEND, nativeHeaders={SPRING.SESSION.ID=[5b1f11d0-ad92-4855-ae44-b2052ecd76d8], Content-Type=[application/json], X-Requested-With=[XMLHttpRequest], simpSessionId=[5b1f11d0-ad92-4855-ae44-b2052ecd76d8], accept-version=[1.2,1.1,1.0], heart-beat=[0,0], destination=[/mobile-server/ping], content-length=[15]}, simpSessionAttributes={}, simpSessionId=1, simpDestination=/mobile-server/ping}
您知道如何让spring取而代之地获取提供的会话id吗

已编辑

好的,我有一个手机应用程序和一个网站在同一台服务器上运行。我需要能够在手机应用程序上设置webocket

在移动电话应用程序上,我通过传统的REST端点登录到服务器,如果成功,我会在响应中收到会话id

我在手机上使用WebTomp客户端,Spring 4.1.9,Spring Security 4.1,Spring Session 1.2.0

理想情况下,我会使用令牌登录socket CONNECT上的STOMP websocket,但我知道这目前是不可能的,因为WebTomp客户端不会在CONNECT上传递自定义头

我有两个问题:

  • 如何在后续请求中传递在REST登录时检索到的会话id?我尝试过添加诸如SPRING.SESSION.ID之类的头文件,但在代码中,我总是看到消息处理返回到simpSessionId,它总是默认为1、2等。我尝试过扩展AbstractSessionWebSocketMessageBrokerConfigure,但它没有获取我的会话ID,它总是在SimpSessionAttribute中查找,它总是空的


  • 代码似乎还试图获取http会话,这是一种web浏览器场景。我想我应该忽略这个

  • 会话过期。可能已过期的会话的策略应该是什么?我是否也应该传递“记住我”样式的身份验证令牌?或者我应该依靠一些永久的无状态会话吗?这对我来说并不清楚,这方面似乎没有记录

显然,我做错了什么。这是我的配置:

@配置 @启用RedistpSession(maxInactiveIntervalInSeconds=1200) 公共类会话配置{

@Inject
ContentNegotiationManager contentNegotiationManager;

@Bean
public RedisConnectionFactory redisConnectionFactory(
        @Value("${spring.redis.host}") String host,
        @Value("${spring.redis.password}") String password,
        @Value("${spring.redis.port}") Integer port) {
    JedisConnectionFactory redis = new JedisConnectionFactory();
    redis.setUsePool(true);
    redis.setHostName(host);
    redis.setPort(port);
    redis.setPassword(password);
    redis.afterPropertiesSet();
    return redis;
}

@Bean
  public RedisTemplate<String,ExpiringSession> redisTemplate(RedisConnectionFactory connectionFactory) {
      RedisTemplate<String, ExpiringSession> template = new RedisTemplate<String, ExpiringSession>();
      template.setKeySerializer(new StringRedisSerializer());
      template.setHashKeySerializer(new StringRedisSerializer());
      template.setConnectionFactory(connectionFactory);
      return template;
  }

@Bean
public <S extends ExpiringSession>SessionRepositoryFilter<? extends ExpiringSession> sessionRepositoryFilter(SessionRepository<S> sessionRepository) {
    return new SessionRepositoryFilter<S>(sessionRepository);
}

@Bean
  public HttpSessionEventPublisher httpSessionEventPublisher() {
          return new HttpSessionEventPublisher();
  }

@Bean
public HttpSessionStrategy httpSessionStrategy(){
    return new SmartSessionStrategy();
}

@Bean
  public CookieSerializer cookieSerializer() {
          DefaultCookieSerializer serializer = new DefaultCookieSerializer();
          serializer.setCookieName("JSESSIONID"); 
          serializer.setCookiePath("/");
          serializer.setUseSecureCookie(true);
          serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$"); 
          return serializer;
  }
=== 为了简洁起见,我删除了一些附加的安全配置

@配置 @启用Web安全性 @订单(100) 公共类SecurityConfig扩展了WebSecurity配置适配器{

private static final String REMEMBER_ME_COOKIE = "SPRING_SECURITY_REMEMBER_ME_COOKIE";

@Inject
FilterInvocationSecurityMetadataSource securityMetadataSource;

@Inject
SessionRepositoryFilter<? extends ExpiringSession> sessionRepositoryFilter;

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {

    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setSaltSource(saltSource);
    provider.setUserDetailsService(userMgr);
    provider.setPasswordEncoder(passwordEncoder);
    provider.setMessageSource(messages);
    auth.authenticationProvider(provider);

}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
}

@Bean
public AuthenticationTokenProcessingFilter authenticationTokenProcessingFilter() throws Exception{
    return new AuthenticationTokenProcessingFilter(authenticationManagerBean());
}

@Bean
public FilterSecurityInterceptor myFilterSecurityInterceptor(
        AuthenticationManager authenticationManager, 
        AccessDecisionManager accessDecisionManager,
        FilterInvocationSecurityMetadataSource metadataSource){
    FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
    interceptor.setAuthenticationManager(authenticationManager);
    interceptor.setAccessDecisionManager(accessDecisionManager);
    interceptor.setSecurityMetadataSource(securityMetadataSource);
    interceptor.setSecurityMetadataSource(metadataSource);
    return interceptor;
}

@Bean
public AccessDecisionManager accessDecisionManager(SiteConfig siteConfig){
    URLBasedSecurityExpressionHandler expressionHandler = new URLBasedSecurityExpressionHandler();
    expressionHandler.setSiteConfig(siteConfig);

    WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
    webExpressionVoter.setExpressionHandler(expressionHandler);

    return new AffirmativeBased(Lists.newArrayList(
            webExpressionVoter,
            new RoleVoter(),
            new AuthenticatedVoter()
    ));
}

public PasswordFixingAuthenticationProvider customAuthenticationProvider(PasswordEncoder passwordEncoder, SaltSource saltSource){
    PasswordFixingAuthenticationProvider provider = new PasswordFixingAuthenticationProvider();
    provider.setUserDetailsService(userMgr);
    provider.setPasswordEncoder(passwordEncoder);
    provider.setSaltSource(saltSource);

    return provider;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .addFilterBefore(sessionRepositoryFilter, ChannelProcessingFilter.class)
        .antMatcher("/ws/**")
        .exceptionHandling()
            .accessDeniedPage("/mobile/403")
            .and()
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
        .csrf().disable()
        .authorizeRequests()
            .antMatchers("/ws").permitAll()
            .antMatchers("/ws/websocket").permitAll()
            .antMatchers("/ws/**").denyAll();           
       .anyRequest().requiresSecure()

    ;
}

我认为这个问题首先是基于无效的期望。你不能传递会话id,也不应该传递它。你不能在STOMP协议级别登录,这不是它的设计工作方式

尽管STOMP协议允许在连接帧中传递用户凭据,这对于TCP上的STOMP更为有用。在HTTP场景中,我们已经有了可依赖的身份验证和授权机制。当您到达STOMP连接时,您必须通过对ebSocket握手URL

如果您还没有阅读过STOMP/WebSocket消息传递的Spring参考文档,我将首先介绍它:

进行WebSocket握手并启动新的WebSocket会话时 创建后,Spring的WebSocket支持会自动传播 从HTTP请求到WebSocket的java.security.Principal 会话。在此之后,在 WebSocket会话中包含了丰富的用户信息 作为标题出现在消息中

换句话说,身份验证与现有web应用程序相同。暴露WebSocket端点的URL只是应用程序的另一个HTTP端点。所有其他HTTP端点的安全方式与WebSocket握手的安全方式相同。与其他HTTP端点一样,您不传递会话id。Instead您位于通过cookie维护的现有HTTP会话中

除非Spring Security首先对HTTP URL进行身份验证和授权,否则无法建立握手。从那里,STOMP会话将接收经过身份验证的用户,Spring Security提供了进一步的方式来授权单个STOMP消息


这一切都可以无缝工作。无需通过STOMP登录或在任何时候传递Spring会话id。

在主要问题“代码似乎也试图获取http会话,这是一种web浏览器场景。我假设我应该忽略这一点”中添加了配置不,你不应该忽略它。这是代码所期望的。身份验证信息是从HTTP握手中获取的。好的,配置中是否有任何特定的错误?因此,有两个级别:身份验证和会话。到目前为止,我一直关注通过头传递会话令牌。我如何通过在服务器上创建一个标头并对其进行处理,以便在HTTP握手中正确传输该标头,并根据传递的会话id实例化会话?此外,setSessionCookieEded(false)配置的一部分几乎肯定是错误的,正如Javadoc中为该方法指出的那样,大多数Java应用程序都依赖该配置为服务器端的用户维护HTTP会话。
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig<S extends ExpiringSession> extends AbstractSessionWebsocketMessageBrokerConfigurer<S>{

    @Inject
    SessionRepository<S> sessionRepository;

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic", "/queue");

        config.setApplicationDestinationPrefixes("/mobile-server");

        config.setUserDestinationPrefix("/mobile-user");

    }

    @Override
    public void configureStompEndpoints(StompEndpointRegistry registry) {
        registry
            .addEndpoint("/ws")
            .setHandshakeHandler(new SessionHandShakeHandler(new TomcatRequestUpgradeStrategy()))
            .setAllowedOrigins("*")
            .withSockJS()
            .setSessionCookieNeeded(false)
            ;
    }

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        registration.setMessageSizeLimit(512 * 1024);
        registration.setSendBufferSizeLimit(1024 * 1024);
        registration.setSendTimeLimit(40000);
    }

    @Bean
    public WebSocketConnectHandler<S> webSocketConnectHandler(SimpMessageSendingOperations messagingTemplate, UsorManager userMgr) {
        return new WebSocketConnectHandler<S>(messagingTemplate, userMgr);
    }

    @Bean
    public WebSocketDisconnectHandler<S> webSocketDisconnectHandler(SimpMessageSendingOperations messagingTemplate, WebSocketManager repository) {
        return new WebSocketDisconnectHandler<S>(messagingTemplate, repository);
    }

}
@Configuration
public class WebSocketSecurity extends AbstractSecurityWebSocketMessageBrokerConfigurer{

    ApplicationContext context = null;

    public void setApplicationContext(ApplicationContext context) {
        this.context = context;
    }

    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }

    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        messages
            .nullDestMatcher().permitAll()
            .simpSubscribeDestMatchers("/user/queue/errors").permitAll()
            .simpDestMatchers("/mobile-server/ping").authenticated()
            .simpDestMatchers("/mobile-server/csrf").authenticated()
            .simpDestMatchers("/mobile-server/**").hasRole("ENDUSER")
            .simpSubscribeDestMatchers("/user/**", "/topic/**").hasRole("ENDUSER")
            .anyMessage().denyAll();
    }

}
private static final String REMEMBER_ME_COOKIE = "SPRING_SECURITY_REMEMBER_ME_COOKIE";

@Inject
FilterInvocationSecurityMetadataSource securityMetadataSource;

@Inject
SessionRepositoryFilter<? extends ExpiringSession> sessionRepositoryFilter;

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {

    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setSaltSource(saltSource);
    provider.setUserDetailsService(userMgr);
    provider.setPasswordEncoder(passwordEncoder);
    provider.setMessageSource(messages);
    auth.authenticationProvider(provider);

}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
}

@Bean
public AuthenticationTokenProcessingFilter authenticationTokenProcessingFilter() throws Exception{
    return new AuthenticationTokenProcessingFilter(authenticationManagerBean());
}

@Bean
public FilterSecurityInterceptor myFilterSecurityInterceptor(
        AuthenticationManager authenticationManager, 
        AccessDecisionManager accessDecisionManager,
        FilterInvocationSecurityMetadataSource metadataSource){
    FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
    interceptor.setAuthenticationManager(authenticationManager);
    interceptor.setAccessDecisionManager(accessDecisionManager);
    interceptor.setSecurityMetadataSource(securityMetadataSource);
    interceptor.setSecurityMetadataSource(metadataSource);
    return interceptor;
}

@Bean
public AccessDecisionManager accessDecisionManager(SiteConfig siteConfig){
    URLBasedSecurityExpressionHandler expressionHandler = new URLBasedSecurityExpressionHandler();
    expressionHandler.setSiteConfig(siteConfig);

    WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
    webExpressionVoter.setExpressionHandler(expressionHandler);

    return new AffirmativeBased(Lists.newArrayList(
            webExpressionVoter,
            new RoleVoter(),
            new AuthenticatedVoter()
    ));
}

public PasswordFixingAuthenticationProvider customAuthenticationProvider(PasswordEncoder passwordEncoder, SaltSource saltSource){
    PasswordFixingAuthenticationProvider provider = new PasswordFixingAuthenticationProvider();
    provider.setUserDetailsService(userMgr);
    provider.setPasswordEncoder(passwordEncoder);
    provider.setSaltSource(saltSource);

    return provider;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .addFilterBefore(sessionRepositoryFilter, ChannelProcessingFilter.class)
        .antMatcher("/ws/**")
        .exceptionHandling()
            .accessDeniedPage("/mobile/403")
            .and()
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
        .csrf().disable()
        .authorizeRequests()
            .antMatchers("/ws").permitAll()
            .antMatchers("/ws/websocket").permitAll()
            .antMatchers("/ws/**").denyAll();           
       .anyRequest().requiresSecure()

    ;
}
  public class SmartSessionStrategy implements HttpSessionStrategy {

    private HttpSessionStrategy browser;

    private HttpSessionStrategy api;

    private RequestMatcher browserMatcher = null;

    public SmartSessionStrategy(){
        this.browser = new CookieHttpSessionStrategy();
        HeaderHttpSessionStrategy headerSessionStrategy = new HeaderHttpSessionStrategy();
        headerSessionStrategy.setHeaderName(CustomSessionRepositoryMessageInterceptor.SPRING_SESSION_ID_ATTR_NAME);
        this.api = headerSessionStrategy;
    }

    @Override
    public String getRequestedSessionId(HttpServletRequest request) {
        return getStrategy(request).getRequestedSessionId(request);
    }

    @Override
    public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
        getStrategy(request).onNewSession(session, request, response);
    }

    @Override
    public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
        getStrategy(request).onInvalidateSession(request, response);
    }

    private HttpSessionStrategy getStrategy(HttpServletRequest request) {
        if(this.browserMatcher != null)
            return this.browserMatcher.matches(request) ? this.browser : this.api;

        return SecurityRequestUtils.isApiRequest(request) ? this.api : this.browser;
    }
  }