简体   繁体   English

没有会话的Spring Boot基本认证(无状态会话)

[英]Spring Boot Basic Authentication without Session (Stateless Session)

I have configured Basic Authentication my Spring-Boot application. 我已将基本身份验证配置为Spring-Boot应用程序。 Everything is Java Config, no xml. 一切都是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();
    }

}

It's configured for both Basic authentication and normal form login. 它配置为基本身份验证和普通表单登录。 When I tested the basic authentication from Rest-Client on Firefox, I can access the secure url "/main". 当我在Firefox上测试Rest-Client的基本身份验证时,我可以访问安全URL“/ main”。 But in the response headers, I'm getting Set-Cookie: JSESSIONID=301225C7AE7C74B0892887389996785D; 但是在响应头中,我得到了Set-Cookie: JSESSIONID=301225C7AE7C74B0892887389996785D; .

I don't want cookies to be generated for basic authentication. 我不希望为基本身份验证生成cookie。 I want true Stateless session for Basic Authentication. 我想要真正的Stateless session进行基本身份验证。 Do note that I need cookies to be generated for form-login to work, so disabling cookies is not an option. 请注意,我需要为表单登录生成cookie才能工作,因此禁用cookie不是一种选择。 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..? 我知道xml配置中的create-session="stateless" ,但有没有办法在Java配置中执行相同的操作,以便基本身份验证是无状态的,而表单身份验证是状态良好的?

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..?

You can do the following. 您可以执行以下操作。

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

And For your problem following custom Java Config can be used. 对于您的问题,可以使用自定义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;
    }
}

This configuration creates two different authentication mechanism. 此配置创建两种不同的身份验证机制

For any request starting with /api/* it will be using a basicAuthenticationFilter and securityContextPersistenceFilterASCFalse with Session Creation False. 对于以/api/*开头的任何请求,它将使用basicAuthenticationFiltersecurityContextPersistenceFilterASCFalse以及Session Creation False。

For any request starting with /* it will be using a usernamePasswordAuthenticationFilter and securityContextPersistenceFilterASCTrue with Session Creation True. 对于以/*开头的任何请求,它将使用带有Session Creation True的usernamePasswordAuthenticationFiltersecurityContextPersistenceFilterASCTrue

You can make use of this and alter it to cater your problem. 你可以利用它并改变它来迎合你的问题。

For anyone else that comes across this, here's something else to check. 对于遇到此问题的其他人来说,还有其他需要检查的内容。

I was hitting this same problem with Spring Boot and even with 我使用Spring Boot遇到了同样的问题

   sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

I was still seeing JSESSIONID cookies being set. 我仍然看到设置了JSESSIONID cookie。 In my case (using JWT), the missing piece seemed to be setting setAllowSessionCreation on the HttpSessionSecurityContextRepository object, like this: 在我的情况下(使用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);
        }
     }

What pointed me this were these lines in HttpSessionSecurityContextRepository : 我指出这是HttpSessionSecurityContextRepository中的这些行:

private boolean allowSessionCreation = true;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM