在 Spring 中使用多个 HttpSessionIdResolver

Kar*_*mba 5 java spring spring-security

我想将 HTTPSessionIdResolver 用于“/api**”下的所有内容以及标准 CookieResolver 下的所有内容。

这怎么可能,以便两种配置使用不同的解析器?以我目前的方法,一切都使用 X-AUTH。

我试图理解 Spring 中的实现,最终在 SessionRepositoryFilter 中,但是这个过滤器只创建了一个实例,所以 der 只存在一个解析器。

    @EnableWebSecurity
    public class TestConfig {

    @EnableSpringHttpSession
    @Configuration
    @Order(1)
    public static class Abc extends WebSecurityConfigurerAdapter {

        @Bean
        @Primary
        public HeaderHttpSessionIdResolver xAuth() {
            return HeaderHttpSessionIdResolver.xAuthToken();
        }

        @Bean
        @Primary
        public MapSessionRepository mapSessionRepository(){
            return new MapSessionRepository(new HashMap<>());
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/service/json/**")
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .httpBasic()
                .and()
                .csrf()
                .disable();
        }

    }

    @EnableSpringHttpSession
    @Configuration
    @Order(2)
    public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

        @ConfigurationProperties(prefix = "spring.datasource")
        @Bean
        @Primary
        public DataSource dataSource() {
            return DataSourceBuilder
                    .create()
                    .build();
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .antMatchers("/css/**", "/user/registration", "/webfonts/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll();
        }

        @Bean
        public BCryptPasswordEncoder bcrypt() {
            return new BCryptPasswordEncoder();
        }

        @Bean
        public JdbcUserDetailsManager userDetailsManager() {
            JdbcUserDetailsManager manager = new UserDetailsManager(dataSource());
            manager.setUsersByUsernameQuery("select username,password,enabled from users where username=?");
            manager.setAuthoritiesByUsernameQuery("select username,authority from authorities where username = ?");
            return manager;
        }

        @Autowired
        public void initialize(AuthenticationManagerBuilder builder) throws Exception {
            builder.userDetailsService(userDetailsManager()).passwordEncoder(bcrypt());
        }
     }
  }
Run Code Online (Sandbox Code Playgroud)

我可以将逻辑移到一个解析器中,该解析器将工作委托给现有的解析器,但这似乎很糟糕?

public class SmartHttpSessionIdResolver implements HttpSessionIdResolver {

    private static final String HEADER_X_AUTH_TOKEN = "X-Auth-Token";
    private static final CookieHttpSessionIdResolver cookie = new CookieHttpSessionIdResolver();
    private static final HeaderHttpSessionIdResolver xauth = HeaderHttpSessionIdResolver.xAuthToken();

    @Override
    public List<String> resolveSessionIds(HttpServletRequest request) {
        if (isXAuth(request)) {
            return xauth.resolveSessionIds(request);
        }
        return cookie.resolveSessionIds(request);
    }

    @Override
    public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {
        if (isXAuth(request)) {
            xauth.setSessionId(request, response, sessionId);
        } else {
            cookie.setSessionId(request, response, sessionId);
        }
    }

    @Override
    public void expireSession(HttpServletRequest request, HttpServletResponse response) {
        if (isXAuth(request)) {
            xauth.expireSession(request, response);
        } else {
            cookie.expireSession(request, response);
        }
    }

    private boolean isXAuth(HttpServletRequest request) {
        return request.getHeader(HEADER_X_AUTH_TOKEN) != null;
    }
}
Run Code Online (Sandbox Code Playgroud)

fas*_*xes 1

在尝试了问题中提供的解决方案(说实话,效果很好)之后,我还尝试通过提供两个不同的过滤器来做到这一点。然而,当@EnableSpringHttpSession添加 时,SessionRepositoryFilter会自动添加 a,并且在 servlet 过滤器链中再添加两个看起来很奇怪。因此,我认为它们必须进入安全过滤器链,这很好,因为这样我们也可以使用在那里进行的 URL 匹配(而不必在其他地方实现)。

由于其他安全过滤器使用HttpSession,我们必须手动将SessionRepositoryFilter第一个过滤器放入此链中。这是我想出的(在 Kotlin 中),它对我来说很有效:

@EnableWebSecurity
class SecurityConfig() {

    private val sessionStore = ConcurrentHashMap<String, Session>()
    private val sessionRepo = MapSessionRepository(sessionStore)

    @Configuration
    @Order(1)
    inner class XAuthConfig(): WebSecurityConfigurerAdapter() {

        override fun configure(http: HttpSecurity) {
            http
                .requestMatchers()
                    .antMatchers("/api**")
                    .and()
                .addFilterBefore(
                    SessionRepositoryFilter(sessionRepo).apply{
                        setHttpSessionIdResolver(
                            HeaderHttpSessionIdResolver.xAuthToken();
                        )
                    }, WebAsyncManagerIntegrationFilter::class.java)
        }
    }

    @Configuration
    @Order(2)
    inner class DefaultConfig(): WebSecurityConfigurerAdapter() {

        override fun configure(http: HttpSecurity) {
            http
                .addFilterBefore(
                    SessionRepositoryFilter(sessionRepo).apply{
                        setHttpSessionIdResolver(
                            CookieHttpSessionIdResolver()
                        )
                    }, WebAsyncManagerIntegrationFilter::class.java)
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

请注意,注释@EnableSpringHttpSession已被删除。相反,我们在 sSessionRepositoryFilter之前手动添加 s WebAsyncManagerIntegrationFilter(安全过滤器链中的第一个过滤器)。它的作用就是用 Spring 来SessionRepositoryFilter替换现有的,无论是手动放置还是通过自动配置自动放置都可以。只要安全过滤器链之前没有其他过滤器使用会话,就应该可以解决问题。否则,一些过滤器重新排列可能仍然有效。HttpSessionHttpSession