AbstractSecurityWebApplicationInitializer vs. Abst

2020-07-09 03:10发布

问题:

I'm trying to add security to my Spring 3.2.8-based pure Java-configured application. I'm following the instructions http://docs.spring.io/spring-security/site/docs/3.2.2.RELEASE/reference/htmlsingle/#jc

I've completed section 3.1, and the documentation says at this point that every URL should require authentication, but none do (at least, I can load every URL). It says it creates a Servlet filter, etc.

It's evident that by itself, that WebSecurityConfigurerAdapter subclass is not enough. So I look at section 3.1.1, which says the next step is to register the springSecurityFilterChain with the WAR, and goes on to say how in a Servlet 3+ environment, I need to subclass AbstractSecurityWebApplicationInitializer. But I'm already subclassing AbstractAnnotationConfigDispatcherServletInitializer. Am I supposed to have one of each? There is some discussion of ordering in the AbstractSecurityWebApplicationInitializer JavaDoc, implying I should have more than one initializer class.

In all this, it also said to add the WebSecurityConfigurerAdapter subclass to getRootConfigClasses() (although the example doesn't show the "AppConfig" that the other Spring getting started docs have you create; also, this alone wasn't enough).

So I tried adding another initializer class. All my other classes are public static inner classes of my AbstractAnnotationConfigDispatcherServletInitializer subclass, so I put another in there to be the AbstractSecurityWebApplicationInitializer subclass (rather than creating a separate .java file).

WARNING com.caucho.server.webapp.WebApp setConfigException: java.lang.UnsupportedOperationException: unimplemented
at com.caucho.server.webapp.ServletContextImpl.setSessionTrackingModes(ServletContextImpl.java:552)
at org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer.onStartup(AbstractSecurityWebApplicationInitializer.java:120)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:174)
at com.caucho.server.webapp.WebApp.callInitializer(WebApp.java:3471)
at com.caucho.server.webapp.WebApp.callInitializers(WebApp.java:3439)
at com.caucho.server.webapp.WebApp.startImpl(WebApp.java:3661)
at com.caucho.server.webapp.WebApp$StartupTask.run(WebApp.java:5196)
at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:173)
at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118)

I tried adding ordering to no avail. My entire config:

    package com.latencyzero.satdb.web;


//
//  Java Imports
//

import java.util.Properties;
import java.util.ResourceBundle;

import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.sql.DataSource;

//
//  Library Imports
//


import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.xml.DOMConfigurator;

import org.hibernate.SessionFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

import org.springframework.stereotype.Controller;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

//
//  Project Imports
//


/**
    There are still some things that get configured in the container. This app
    was developed using Resin. Things configured in resin's XML config for this
    app include the data source, error page, key store password for Apple
    Push Notifications (which should move to the DB).
*/

public
class
WebappInitializer
    extends
        org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
{
    @Override
    protected
    Class<?>[]
    getRootConfigClasses()
    {
        Class<?>[] classes = { AppConfig.class, SecurityConfig.class };
        return classes;
    }

    @Override
    protected
    Class<?>[]
    getServletConfigClasses()
    {
        Class<?>[] classes = { WebConfig.class };
        return classes;
    }

    @Override
    protected
    java.lang.String[]
    getServletMappings()
    {
        String[] mappings = { "/" };
        return mappings;
    }

    @Override
    protected
    javax.servlet.Filter[]
    getServletFilters()
    {
        return new javax.servlet.Filter[]
        {
            new org.springframework.orm.hibernate3.support.OpenSessionInViewFilter(),
        };
    }

    /** *******************************************************************************************************************
        App context configuration.

        Hibernate config (data access, transactions, data model).
    */

    @Configuration
    @EnableAsync
    @EnableTransactionManagement
    @ComponentScan(basePackages = { "com.mymodelanddao", })
    public
    static
    class
    AppConfig
    {
        @Bean
        public
        org.springframework.jndi.JndiObjectFactoryBean
        dataSource()
        {
            org.springframework.jndi.JndiObjectFactoryBean bean = new org.springframework.jndi.JndiObjectFactoryBean();
            bean.setJndiName("java:comp/env/jdbc/db");
            return bean;
        }

        @Bean
        public
        org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean
        sessionFactory()
        {
            DataSource dataSource = (DataSource) dataSource().getObject();

            org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean bean = new org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean();
            bean.setDataSource(dataSource);
            //  TODO: Do we need to scan this, since it's done as part of @ComponentScan?
            bean.setPackagesToScan(new String[] {"com.latencyzero.satdb.model"});

            Properties props = new Properties();
            props.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
            props.setProperty("hibernate.cache.provider_class", "org.hibernate.cache.NoCacheProvider");
            props.setProperty("hibernate.jdbc.batch_size", "200");
            props.setProperty("hibernate.show_sql", "false");
            props.setProperty("hibernate.format_sql", "true");
            props.setProperty("hibernate.use_sql_comments", "false");
            props.setProperty("hibernate.generate_statistics", "true");
            bean.setHibernateProperties(props);

            return bean;
        }

        @Bean
        public
        PlatformTransactionManager
        transactionManager()
        {
            SessionFactory sf = sessionFactory().getObject();
            org.springframework.orm.hibernate3.HibernateTransactionManager bean = new org.springframework.orm.hibernate3.HibernateTransactionManager();
            bean.setSessionFactory(sf);
            return bean;
        }

        private static  Logger              sLogger                     =   Logger.getLogger(AppConfig.class);
    }

    /** *******************************************************************************************************************
        Web context configuration.
    */

    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackageClasses = { com.mycontrollerclasses })
    public
    static
    class
    WebConfig
        extends
            org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
    {
        @Bean
        public
        InternalResourceViewResolver
        getInternalResourceViewResolver()
        {
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix("/WEB-INF/jsp/");
            resolver.setSuffix(".jsp");
            return resolver;
        }

        @Bean
        public
        org.springframework.web.multipart.commons.CommonsMultipartResolver
        multipartResolver()
        {
            return new org.springframework.web.multipart.commons.CommonsMultipartResolver();
        }

        @Override
        public
        void
        addResourceHandlers(ResourceHandlerRegistry inRegistry)
        {
            inRegistry.addResourceHandler("/assets/**").addResourceLocations("/assets/").setCachePeriod(31556926);
            inRegistry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
            inRegistry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
        }

        private static  Logger              sLogger                     =   Logger.getLogger(WebConfig.class);
    }

    /** *******************************************************************************************************************
        Security configuration.
    */

    @Configuration
    @EnableWebMvcSecurity
    public
    static
    class
    SecurityConfig
        extends
            org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
    {
        @Autowired
        public
        void
        configureGlobal(AuthenticationManagerBuilder inAuth)
            throws
                Exception
        {
            sLogger.warn("configureGlobal =================================================");
            inAuth
                .inMemoryAuthentication()
                .withUser("user")
                .password("password")
                .roles("USER");
        }

        private static  Logger              sLogger                     =   Logger.getLogger(SecurityConfig.class);
    }

    public
    static
    class
    SecurityWebApplicationInitializer
        extends
            org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer
    {
    }
}

回答1:

According to https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#abstractsecuritywebapplicationinitializer-with-spring-mvc, you have to introduce a second WebApplicationInitializer, typically derivating from AbstractSecurityWebApplicationInitializer

This second WebApplicationInitializer would register property the springFilterChain



回答2:

Answering using 5.0.6 Spring Security version:

  1. @EnableWebSecurity already contains @Configuration,
    therefore specifying both is redundant
  2. You are indeed required to have a second appinitializer, which extends AbstractSecurityWebApplicationInitializer. Your one seems OK
  3. Your getRootConfigClasses is also OK.
    You really should register SecurityConfig.class there.

Your thoughts and configuration seems right, except I'm not really sure if keeping your SecurityWebApplicationInitializer as static inner class allows Spring to find it. Maybe it can be a problem. I did all the configuration acc to above mentioned instruction and everything is working like a charm, redirecting me to "/login" page.