【问题标题】:Spring Boot Basic Authentication without Session (Stateless Session)Spring Boot Basic Authentication without Session(无状态会话)
【发布时间】:2017-07-15 23:40:02
【问题描述】:

我已经为我的 Spring-Boot 应用程序配置了基本身份验证。一切都是 Java Config,没有 xml。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // Authenticate username -> admin, password -> admin & set role as "ROLE_USER"
        auth.inMemoryAuthentication().withUser("admin").password("admin").roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
            .antMatchers("/login").permitAll()
            // All Requests should be Authenticated
            .anyRequest().authenticated()
            .and()
            // Enable Basic Authentication
            .httpBasic()
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/main", true)
                .loginProcessingUrl("/session")
                .usernameParameter("Username").passwordParameter("Password")
            .and()
            .logout().logoutUrl("/logout").permitAll()
            .and().csrf().disable();
    }

}

它被配置为基本身份验证和普通表单登录。当我在 Firefox 上测试来自 Rest-Client 的基本身份验证时,我可以访问安全 URL“/main”。但在响应标头中,我收到了Set-Cookie: JSESSIONID=301225C7AE7C74B0892887389996785D;

我不希望为基本身份验证生成 cookie。我想要真正的Stateless session 进行基本身份验证。请注意,我需要生成 cookie 才能使表单登录工作,因此禁用 cookie 不是一个选项。我知道 xml 配置中的create-session="stateless",但是有没有办法在 Java 配置中做同样的事情,以便基本身份验证是无状态的,而表单身份验证是有状态的..?

【问题讨论】:

    标签: java spring spring-boot stateless-session


    【解决方案1】:
    I know about the create-session="stateless" in xml configuration, but is there any way to do the same in Java config so that Basic Authentication is Stateless and Form-Authentication is Statefull..?
    

    您可以执行以下操作。

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
    

    对于您的问题,可以使用自定义 Java Config。

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class SecurityConfig {
    
        @Inject
        UserDetailsService userService;
    
        @Bean
        public AuthenticationManager authenticationManager() throws Exception {
            AuthenticationManager authenticationManager = new ProviderManager(
                    Arrays.asList(authenticationProvider()));
            return authenticationManager;
        }
    
        @Bean
        public AuthenticationProvider authenticationProvider() throws Exception {
            CustomAuthenticationProvider authenticationProvider = new CustomAuthenticationProvider();
            authenticationProvider.setUserDetailsService(userService);
            authenticationProvider.setSaltSource(saltSource());
            authenticationProvider.setPasswordEncoder(passwordEncoder());
            authenticationProvider.afterPropertiesSet();
            return authenticationProvider;
        }
    
        @Bean
        public SaltSource saltSource() throws Exception {
            ReflectionSaltSource saltSource = new ReflectionSaltSource();
            saltSource.setUserPropertyToUse("salt");
            saltSource.afterPropertiesSet();
            return saltSource;
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new Md5PasswordEncoder();
        }
    
        @Bean
        public FilterChainProxy springSecurityFilterChain()
                throws ServletException, Exception {
            List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>();
            securityFilterChains.add(new DefaultSecurityFilterChain(
                    new AntPathRequestMatcher("/login**")));
            securityFilterChains.add(new DefaultSecurityFilterChain(
                    new AntPathRequestMatcher("/resources/**")));
            securityFilterChains.add(new DefaultSecurityFilterChain(
                    new AntPathRequestMatcher("/api/**"),
                    securityContextPersistenceFilterASCFalse(),
                    basicAuthenticationFilter(), exceptionTranslationFilter(),
                    filterSecurityInterceptor()));
            securityFilterChains.add(new DefaultSecurityFilterChain(
                    new AntPathRequestMatcher("/**"),
                    securityContextPersistenceFilterASCTrue(), logoutFilter(),
                    usernamePasswordAuthenticationFilter(),
                    exceptionTranslationFilter(), filterSecurityInterceptor()));
            return new FilterChainProxy(securityFilterChains);
        }
    
        @Bean
        public SecurityContextPersistenceFilter securityContextPersistenceFilterASCTrue() {
            return new SecurityContextPersistenceFilter(
                    new HttpSessionSecurityContextRepository());
        }
    
        @Bean
        public SecurityContextPersistenceFilter securityContextPersistenceFilterASCFalse() {
            HttpSessionSecurityContextRepository httpSessionSecurityContextRepository = new HttpSessionSecurityContextRepository();
            httpSessionSecurityContextRepository.setAllowSessionCreation(false);
            return new SecurityContextPersistenceFilter(
                    httpSessionSecurityContextRepository);
        }
    
        @Bean
        public ExceptionTranslationFilter exceptionTranslationFilter() {
            ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
                    new LoginUrlAuthenticationEntryPoint("/login"));
            AccessDeniedHandlerImpl accessDeniedHandlerImpl = new AccessDeniedHandlerImpl();
            accessDeniedHandlerImpl.setErrorPage("/exception");
            exceptionTranslationFilter
                    .setAccessDeniedHandler(accessDeniedHandlerImpl);
            exceptionTranslationFilter.afterPropertiesSet();
            return exceptionTranslationFilter;
        }
    
        @Bean
        public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter()
                throws Exception {
            UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter();
            usernamePasswordAuthenticationFilter
                    .setAuthenticationManager(authenticationManager());
            usernamePasswordAuthenticationFilter.setAllowSessionCreation(true);
            SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler(
                    "/index");
            successHandler.setAlwaysUseDefaultTargetUrl(true);
            usernamePasswordAuthenticationFilter
                    .setAuthenticationSuccessHandler(successHandler);
            usernamePasswordAuthenticationFilter
                    .setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(
                            "/login?error=true"));
            usernamePasswordAuthenticationFilter
                    .setAuthenticationDetailsSource(new CustomWebAuthenticationDetailsSource());
            usernamePasswordAuthenticationFilter.afterPropertiesSet();
    
            return usernamePasswordAuthenticationFilter;
    
        }
    
        @Bean
        public FilterSecurityInterceptor filterSecurityInterceptor()
                throws Exception {
            FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
            filterSecurityInterceptor
                    .setAuthenticationManager(authenticationManager());
            filterSecurityInterceptor
                    .setAccessDecisionManager(accessDecisionManager());
            filterSecurityInterceptor.setRunAsManager(runAsManager());
            LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
            List<ConfigAttribute> configs = new ArrayList<ConfigAttribute>();
            configs.add(new org.springframework.security.access.SecurityConfig(
                    "isAuthenticated()"));
            requestMap.put(new AntPathRequestMatcher("/**"), configs);
            FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource = new ExpressionBasedFilterInvocationSecurityMetadataSource(
                    requestMap, new DefaultWebSecurityExpressionHandler());
            filterSecurityInterceptor
                    .setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
            filterSecurityInterceptor.afterPropertiesSet();
    
            return filterSecurityInterceptor;
        }
    
        public AffirmativeBased accessDecisionManager() throws Exception {
            List<AccessDecisionVoter> voters = new ArrayList<AccessDecisionVoter>();
            voters.add(new WebExpressionVoter());
            voters.add(new RoleVoter());
            AffirmativeBased affirmativeBased = new AffirmativeBased(voters);
            affirmativeBased.setAllowIfAllAbstainDecisions(false);
            affirmativeBased.afterPropertiesSet();
    
            return affirmativeBased;
        }
    
        @Bean
        public RunAsManager runAsManager() throws Exception {
            RunAsManagerImpl runAsManager = new RunAsManagerImpl();
            runAsManager.setKey("V_RUN_AS");
            runAsManager.afterPropertiesSet();
            return runAsManager;
        }
    
        @Bean
        public LogoutFilter logoutFilter() throws ServletException {
            List<LogoutHandler> handlers = new ArrayList<LogoutHandler>();
            handlers.add(new CookieClearingLogoutHandler("JSESSIONID"));
            handlers.add(new SecurityContextLogoutHandler());
            LogoutFilter logoutFilter = new LogoutFilter("/login",
                    handlers.toArray(new LogoutHandler[] {}));
            logoutFilter.afterPropertiesSet();
            return logoutFilter;
        }
    
        @Bean
        public RequestContextFilter requestContextFilter() {
            return new RequestContextFilter();
        }
    
        @Bean
        public BasicAuthenticationFilter basicAuthenticationFilter()
                throws Exception {
            BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
            basicAuthenticationEntryPoint.setRealmName("V_REALM");
            basicAuthenticationEntryPoint.afterPropertiesSet();
            BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(
                    authenticationManager(), basicAuthenticationEntryPoint);
            basicAuthenticationFilter
                    .setAuthenticationDetailsSource(new CustomWebAuthenticationDetailsSource());
            basicAuthenticationFilter.afterPropertiesSet();
            return basicAuthenticationFilter;
        }
    }
    

    此配置创建了两种不同的身份验证机制。

    对于以/api/* 开头的任何请求,它将使用basicAuthenticationFiltersecurityContextPersistenceFilterASCFalse 会话创建为False。

    对于以/* 开头的任何请求,它将使用usernamePasswordAuthenticationFiltersecurityContextPersistenceFilterASCTrue 并确保会话创建为真。

    您可以利用它并对其进行更改以满足您的问题。

    【讨论】:

      【解决方案2】:

      对于遇到此问题的任何其他人,这里还有其他内容需要检查。

      我在使用 Spring Boot 时遇到了同样的问题,甚至使用了

         sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
      

      我仍然看到设置了 JSESSIONID cookie。在我的情况下(使用 JWT),缺少的部分似乎是在 HttpSessionSecurityContextRepository 对象上设置 setAllowSessionCreation,如下所示:

          public class StatelessAuthenticationFilter extends GenericFilterBean {
              private final MyTokenAuthService authenticationService;
              private SecurityContextRepository repository = new HttpSessionSecurityContextRepository();
              protected final Logger logger = LoggerFactory.getLogger(getClass().getName());
      
              public StatelessAuthenticationFilter(MyTokenAuthService authenticationService) {
                  this.authenticationService = authenticationService;
                  ((HttpSessionSecurityContextRepository) repository).setAllowSessionCreation(false);
              }
           }
      

      告诉我的是HttpSessionSecurityContextRepository中的这些行:

      private boolean allowSessionCreation = true;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-17
        • 2018-10-08
        • 1970-01-01
        • 2015-12-23
        • 2018-12-16
        • 2012-09-05
        • 2015-09-17
        • 1970-01-01
        相关资源
        最近更新 更多