Настройка Spring + Security + Tiles + Thymeleaf с использованием JavaConfig

Я пытаюсь настроить веб-приложение для использования Spring 3.2 + Spring Security + Apache Tiles + Apache Thymeleaf, но я продолжаю получать ошибку 404, хотя журналы (catalina.out) не показывают ошибок. Что может быть не так с моей конфигурацией?? Моя конфигурация выглядит следующим образом:


Конфигурация тимьяна

@Configuration
public class ThymeleafConfig {

    @Bean
    public BeanNameViewResolver beanViewResolver() {
        BeanNameViewResolver resolver = new BeanNameViewResolver();
        resolver.setOrder(1);
        return resolver;
    }

    @Bean
    public MarshallingHttpMessageConverter marshallingMessageConverter() {
        MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter(
                jaxb2Marshaller(),
                jaxb2Marshaller()
        );
        List<MediaType> mediaTypes = new ArrayList<MediaType>();
        mediaTypes.add(MediaType.APPLICATION_XML);
        converter.setSupportedMediaTypes(mediaTypes);
        return converter;
    }

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();

        converters.add(stringHttpMessageConverter());
        converters.add(marshallingJsonConverter());
        converters.add(marshallingMessageConverter());
        restTemplate.setMessageConverters(converters);

        return restTemplate;
    }

    @Bean
    public StringHttpMessageConverter stringHttpMessageConverter() {
        return new StringHttpMessageConverter();
    }

    @Bean
    public MappingJacksonHttpMessageConverter marshallingJsonConverter() {
        List<MediaType> mediaTypes = new ArrayList<MediaType>();
        mediaTypes.add(MediaType.APPLICATION_JSON);
        MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
        converter.setSupportedMediaTypes(mediaTypes);
        return converter;
    }

    @Bean
    public Jaxb2Marshaller jaxb2Marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setPackagesToScan(new String[]{
                "org.tutiworks.orm.*"
        });
        return marshaller;
    }

    @Bean
    public ThymeleafTilesConfigurer tilesConfigurer() {
        ThymeleafTilesConfigurer tilesConfigurer = new ThymeleafTilesConfigurer();
        tilesConfigurer.setDefinitions(new String[]{"/WEB-INF/tiles.xml"});
        return tilesConfigurer;
    }

    @Bean
    public TilesDialect tilesDialect() {
        TilesDialect dialect = new TilesDialect();
        return dialect;
    }

    @Bean
    public SpringSecurityDialect springSecurityDialect() {
        SpringSecurityDialect dialect = new SpringSecurityDialect();
        return dialect;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        Set<IDialect> dialects = new HashSet<IDialect>();
        dialects.add(springSecurityDialect());
        dialects.add(tilesDialect());
        engine.setAdditionalDialects(dialects);
        return engine;
    }

    @Bean
    public ThymeleafViewResolver thymeleafViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        resolver.setViewClass(ThymeleafTilesView.class);
        resolver.setOrder(2);
        resolver.setCharacterEncoding("UTF-8");
        String[] views = {"*.html", "*.xhtml"};
        resolver.setViewNames(views);
        return resolver;
    }

    /**
     * Create the CNVR. Get Spring to inject the ContentNegotiationManager
     * created by the configurer (see previous method).
     */
    @Bean
    public ViewResolver contentNegotiatingViewResolver(
            ContentNegotiationManager manager) {
        ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
        resolver.setContentNegotiationManager(manager);
        List<ViewResolver> viewResolvers = new ArrayList<ViewResolver>();
        viewResolvers.add(beanViewResolver());
        viewResolvers.add(thymeleafViewResolver());
        resolver.setViewResolvers(viewResolvers);
        return resolver;
    }

}

Контекст приложения

@Configuration
@ComponentScan(basePackages = {"org.tutiworks"})
@EnableWebMvc
@Import({SpringDataConfig.class, ThymeleafConfig.class, SecurityConfig.class})
@PropertySource("classpath:spring.properties")
public class ApplicationContext extends WebMvcConfigurerAdapter {

    // Maps resources path to webapp/resources
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations(
                "/resources/");
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("login");
    }

    /**
     * Setup a simple strategy: 1. Only path extension is taken into account,
     * Accept headers are ignored. 2. Return HTML by default when not sure.
     */
    @Override
    public void configureContentNegotiation(
            ContentNegotiationConfigurer configurer) {
        configurer.ignoreAcceptHeader(true);
        Map<String, MediaType> mediaTypes = new HashMap<String, MediaType>();
        mediaTypes.put("json", MediaType.APPLICATION_JSON);
        mediaTypes.put("xml", MediaType.APPLICATION_XML);
        mediaTypes.put("xml2", MediaType.TEXT_XML);
        mediaTypes.put("xhtml", MediaType.APPLICATION_XHTML_XML);
        mediaTypes.put("html", MediaType.TEXT_HTML);
        configurer.mediaTypes(mediaTypes);
        configurer.defaultContentType(MediaType.TEXT_HTML);
    }


    // Only needed if we are using @Value and ${...} when referencing properties
    @Bean
    public static PropertySourcesPlaceholderConfigurer properties() {
        PropertySourcesPlaceholderConfigurer propertySources =
                new PropertySourcesPlaceholderConfigurer();
        Resource[] resources = new ClassPathResource[]{new ClassPathResource(
                "spring.properties")};
        propertySources.setLocations(resources);
        propertySources.setIgnoreUnresolvablePlaceholders(true);
        return propertySources;
    }

    // Provides internationalization of messages
    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {
        ReloadableResourceBundleMessageSource source = new ReloadableResourceBundleMessageSource();
        source.setBasename("messages");
        source.setDefaultEncoding("UTF-8");
        return source;
    }
}

Инициализатор приложения

public class ApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {

        // Load application context
        AnnotationConfigWebApplicationContext rootContext =
                new AnnotationConfigWebApplicationContext();
        rootContext.register(ApplicationContext.class);
        rootContext.setDisplayName("Citizen Police");
        rootContext.setConfigLocation("org.tutiworks.config");

        FilterRegistration.Dynamic securityFilter = servletContext.addFilter("securityFilter",
                new DelegatingFilterProxy("springSecurityFilterChain"));
        securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");

        FilterRegistration.Dynamic characterEncodingFilter = servletContext.addFilter
                ("characterEncodingFilter", new CharacterEncodingFilter());
        characterEncodingFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class),
                true, "/*");
        characterEncodingFilter.setInitParameter("encoding", "UTF-8");
        characterEncodingFilter.setInitParameter("forceEncoding", "true");

        servletContext.setInitParameter("defaultHtmlEscape", "true");
        // Context loader listener
        servletContext.addListener(new ContextLoaderListener(rootContext));

        // Dispatcher servlet
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
                "dispatcher", new DispatcherServlet(rootContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.setAsyncSupported(true);
    }
}

Конфигурация безопасности Spring

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeUrls()
                .antMatchers("/**").authenticated()
                .antMatchers("/resources/**", "/forgot.html", "/signup.html").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .permitAll()
                .defaultSuccessUrl("/home.html")
                .failureUrl("/login.html?authfailed=true")
                .and()
                .logout()
                .invalidateHttpSession(true)
                .logoutUrl("/logout.html")
                .deleteCookies("JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE")
                .logoutSuccessUrl("/");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

    @Bean
    public UserDetailsService loginService() {
        UserDetailsService service = new LoginService();
        return service;
    }

    @Override
    protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .jdbcAuthentication().and()
                .eraseCredentials(true)
                .userDetailsService(loginService())
                .passwordEncoder(passwordEncoder());
    }
}

Плитки 2 Конфигурация

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC
    "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
    "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">

<tiles-definitions>

    <definition name="indexLayout" extends="standardLayout" template="/WEB-INF/templates/layouts/template.html" templateType="thymeleaf">
        <put-attribute name="title" value="Afrikana - Sign in ::title" type="thymeleaf" />
        <put-attribute name="head" value="/WEB-INF/templates/fragments/head.html ::head" type="thymeleaf" />        
        <put-attribute name="footer" value="/WEB-INF/templates/fragments/footer.html ::footer" type="thymeleaf" />
    </definition>

    <!-- Index page -->
    <definition name="login" extends="indexLayout">
        <put-attribute name="content" value="/WEB-INF/templates/pages/login.html :: content" type="thymeleaf" />
    </definition>   

    <!--  
    <definition name="configure" 
        template="/WEB-INF/pages/configuration/configureDhis2.jsp" />
    --> 

</tiles-definitions>

Макет шаблона

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:tiles="http://www.thymeleaf.org">
    <head tiles:fragment="head">
        <title tiles:string="title">
        </title>
    </head>
    <body>
        <div tiles:fragment="content"></div>
        <div tiles:fragment="footer"></div>
    </body>
</html>

Что может быть не так? Почему я получаю сообщение об ошибке 404, когда приложение пытается загрузить файл login.html?


person Timothy Tuti    schedule 04.07.2013    source источник
comment
Благодаря тому, как вы разбили свои контексты на модули, вам не нужен файл ContextLoaderListener.   -  person Sotirios Delimanolis    schedule 04.07.2013


Ответы (1)


Вы не установили сопоставление URL-адресов для своего сервлета.

// Dispatcher servlet
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.setAsyncSupported(true);   
// add this
dispatcher.addMapping("/"); // or whatever you want/need
person Sotirios Delimanolis    schedule 04.07.2013