Unable to run Spring Boot WebMVC with Thymeleaf support - java

I have search a lot but I did not find answer to my question, So I am posting my question here. Please look and suggest me the solution where I am mistaken.
I have created spring boot web mvc project with thymeleaf support using Spring Tool Suite(STS). When I run it give me "Whitelabel Error Page" page. Which means mapping not found.
Efforts:
WebConfig.java
package com.springthymeleaf.config;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
#Configuration
#ComponentScan("com.springthymeleaf")
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
ServletRegistrationBean servletRegistration(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean();
registrationBean.addUrlMappings("/console/*");
return registrationBean;
}
//start Thymeleaf specific configuration
#Bean(name ="templateResolver")
public ServletContextTemplateResolver getTemplateResolver() {
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
// templateResolver.setPrefix("/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("XHTML");
return templateResolver;
}
#Bean(name ="templateEngine")
public SpringTemplateEngine getTemplateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(getTemplateResolver());
return templateEngine;
}
#Bean(name="viewResolver")
public ThymeleafViewResolver getViewResolver(){
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(getTemplateEngine());
return viewResolver;
}
//end Thymeleaf specific configuration
#Bean(name ="messageSource")
public MessageSource getMessageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("/WEB-INF/i18/thymeleafResource");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
SecurityConfiguration.java
package com.springthymeleaf.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/").permitAll();
}
}
ServletInitializer.java
package com.springthymeleaf;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringThymeLeafApplication.class);
}
}
SpringThymeLeafApplication.java
package com.springthymeleaf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringThymeLeafApplication {
public static void main(String[] args) {
SpringApplication.run(SpringThymeLeafApplication.class, args);
}
}
IndexController.java
package com.springthymeleaf.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class IndexController {
#RequestMapping("/")
public String index(){
return "index";
}
}
I have created index.html file in resources/templates folder. Still I am getting that error. I have searched a lot on web, but did not get clue. Please somebody help me.

Actually Spring Boot configures Thymeleaf out of the box. It should work with the following setup:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").defaultSuccessUrl("/").permitAll() // http://docs.spring.io/spring-security/site/docs/4.0.3.RELEASE/reference/htmlsingle/#jc-form
.and()
.logout().permitAll(); // http://docs.spring.io/spring-security/site/docs/4.0.3.RELEASE/reference/htmlsingle/#jc-logout
}
#Override
public void configure(WebSecurity web) throws Exception
{
web
.ignoring()
.antMatchers("/resources/**"/*, ... */);
}
}
#Controller
public class LoginController
{
#RequestMapping("/login")
static String login(Model model)
{
return "login";
}
}

Spring Boot already configures Thymeleaf for you, so no need to configure that manually. Remove all Thymeleaf related configuration, also remove #EnableWebMvc as that interferes with the Spring Boot auto configuration. The #ComponentScan is also redundant.
Spring Boot also registered a MessageSource for you so no need to configure that. Not sure what the servlet registration is you do but that is the only thing you need.
Also I suggest to remove your controller and use a view controller which you can configure in your WebConfig class. Saves you a controller.
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
ServletRegistrationBean servletRegistration(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean();
registrationBean.addUrlMappings("/console/*");
return registrationBean;
}
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
}
To let the auto configured message source pickup your custom bundles add the following to src/main/resources/application.properties.
spring.messages.basename=/WEB-INF/i18/thymeleafResource
I would also suggest to simply let the SpringThymeLeafApplication extend the SpringBootServletInitializer.
#SpringBootApplication
public class SpringThymeLeafApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringThymeLeafApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringThymeLeafApplication.class);
}
}
Also make sure that your templates are in src/main/resources/templates and not in src/main/resources/resources/templates else those will not be found.

Spring boot does all the automatic configuration when you add the thymeleaf dependency. then you should do the following.
Remove all the thymeleaf configuration you have on your WebConfig.java
Make sure you have the following dependency your pom.xml if you are using Maven, otherwise check the spring website for the equivalent if you are using gradle:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
thirdly you make sure that you scanning where your controllers are, add the following on your SpringThymeLeafApplication.java:
#ComponentScan(basePackages = "your.path.to.controllers")
Finally you have to add your .html files to resources/templates

Related

Cannot get my endpoints list from controller in swagger UI

I am new to Spring boot with Swagger UI. I'm just trying to configure my Rest controller endpoints to show on swagger UI screen but it shows No operations for specs defined. Pretty sure, its a configuration issue.
I have tried #EnableAutoConfiguration, still it can't find the controller
SwaggerDemoApplication.java
package com.example.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SwaggerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SwaggerDemoApplication.class, args);
}
}
SwaggerConfig.java
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import static springfox.documentation.builders.PathSelectors.regex;
#EnableSwagger2
#Configuration
public class SwaggerConfig {
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.controller"))
.paths(regex("/test.*"))
.build();
}
}
TestController.java
package com.example.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
#RestController
#RequestMapping(value = "/test")
#Api(value="onlinestore", description="Operations pertaining to products in Online Store")
public class TestController {
#RequestMapping(value = "/test-swagger", method= RequestMethod.GET)
public String home() {
return "Spring is here!";
}
}
Expected: Rest endpoint
Actual: No operations defined in spec
I had exactly the same problem like you and it was caused with the wrong placement of my #SpringBootApplication class, just like you have it.
(In the code snippets, I am intentionally omitting less relevant annotations, they still must be there as you have them in your post.
I also quote the words like "under", "root package" etc., as technically Java does not recognize anything like a "sub-package". All the packages in Java are at the same "level", even if the dots and the resemblance of the web domains "mislead" us to think about them hierarchically. However Spring extensively works with "sub-packages" and "root-packages".)
package com.example.config;
#SpringBootApplication
public class SwaggerDemoApplication {
...
}
package com.example.config;
#EnableSwagger2
public class SwaggerConfig {
...
.apis(RequestHandlerSelectors.basePackage("com.example.controller"))
...
}
#RestController
package com.example.controller; // notice that this is not "under" com.example.config where SwaggerDemoApplication resides
public class TestController {
....
}
I observed that if the #SpringBootApplication class is not in the "root package" of the #RestController class, it breaks the automagical behaviour and the package name set in the call of apis(...) is ignored and the package is not scanned. To be honest, I am not very much sure how the apis() is supposed to work and whether it is a bug or a feature.
To fix it without adding #ComponentScan, your package organisation should be something like this:
package com.example.myapplication; // the main class is placed in the "root" package
#SpringBootApplication
public class SwaggerDemoApplication {
...
}
package com.example.myapplication.config;
#EnableSwagger2
public class SwaggerConfig {
...
.apis(RequestHandlerSelectors.basePackage("com.example.myapplication"))
...
}
package com.example.myapplication.controller;
#RestController
public class TestController {
...
}
You may also further filter the API scanning by specifying
.apis(
withClassAnnotation(RestController.class)
.and(basePackage("com.example.myapplication.controller))
)
The problem is not your Swagger Configuration but your Controller is not scanned as Spring resource class.
Because your application startup class (Main class) has no #ComponentScan annotation.
So, your class should be like this:
#EnableSwagger2
#SpringBootApplication
#ComponentScan("com.example")
public class SwaggerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SwaggerDemoApplication.class, args);
}
}
Now you can access your API documentation from here http://localhost:8098/swagger-ui.html#
You can try it in this way.
Path you specified can be the problematic.
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("<your-package>"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("API Documentation")
.description("This API documentation is related <something>")
.version("1.0.0")
.build();
}
}
SwaggerConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#Configuration
#EnableSwagger2
public class SwaggerConfig implements WebMvcConfigurer{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
#Bean
public Docket apiDocket() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example"))
.paths(PathSelectors.any())
.build();
return docket;
}
}
SwaggerDemoApplication.java
#EnableSwagger2
#SpringBootApplication
public class SwaggerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SwaggerDemoApplication.class, args);
}
pom.xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-core</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
In Case you are using Spring Security you have to add below Configurations
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**")
.permitAll()
.anyRequest().authenticated();
}

Spring Security: Rest endpoint is not found after securing with #Secured

I am trying to secure a REST endpoint via the #Secured annotation of Spring Security. My main application (Spring Boot App) with the security config and the rest controller are in different packages and project.
Main app package: com.myapp.api.web
Rest controller packge: com.myapp.api.rest
Mainapp:
#SpringBootApplication
#ComponentScan(basePackages = "com.myapp.api")
#EntityScan("com.myapp.api")
#RestController
public class ApiApplication extends SpringBootServletInitializer
{
public static void main(String[] args)
{
SpringApplication.run(ApiApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
return application.sources(ApiApplication.class);
}
}
Security Config:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true,
securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
private static final String USERS_CONFIG_FILE_NAME = "users.yml";
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.httpBasic()
.and()
.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
InMemoryUserDetailsManager inMemoryUserDetailsManager, PasswordEncoder passwordEncoder) throws Exception
{
auth.userDetailsService(inMemoryUserDetailsManager).passwordEncoder(passwordEncoder);
}
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() throws IOException
{
return new InMemoryUserDetailsManager(
PropertiesLoaderUtils.loadAllProperties(USERS_CONFIG_FILE_NAME, getClass().getClassLoader()));
}
}
Rest controller:
#RestController
public class RestController
{
private final RestService service;
#PostMapping("/rest/v1")
#Secured({"ROLE_ADMIN"})
public List<String> getStates(#RequestBody List<String> Ids)
{
...
}
My rest endpoint is working as long as I am not setting securedEnabled = true. After setting it true I am getting a 404 Not Found as respond message. I've already debugged it and found out that the Spring Security somewhen stops in the filter chain and that the request never reaches the controller.
As far as I tested it, as long as the rest controller is in a different project this error will occure. After moving it to the same project it is working as it should.
Is there something missing in my Securityconfig or what could the problem be?
I am able to get values by your code , I have only changed Password Encoder to default and changed inMemoryAuthentication .
I did it as I don't have your file "users.yml" If you can share a sample , we will look in it , But below is my code. I kept all logic in 2 files just to verify.
Configuration Class
package com.myapp.api.web.Api;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true,
securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
private static final String USERS_CONFIG_FILE_NAME = "users.yml";
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.httpBasic()
.and()
.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
#Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
};
}
#Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.inMemoryAuthentication()
.withUser("user").password("user").roles("USER")
.and().withUser("admin").password("admin").roles("ADMIN");
}
/*
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() throws IOException
{
return new InMemoryUserDetailsManager(
PropertiesLoaderUtils.loadAllProperties(USERS_CONFIG_FILE_NAME, getClass().getClassLoader()));
}*/
}
Main Class
package com.myapp.api.web.Api;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
#ComponentScan(basePackages = "com.myapp.api")
#RestController
#SpringBootApplication
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
#PostMapping("/rest/v1")
#Secured({"ROLE_ADMIN"})
public List<String> getStates(#RequestBody List<String> Ids)
{
return Ids;
// ...
}
}

Unable to load html pages when using Spring annotation config

I'm working on a POC using Spring with Annotation based configuration. While working on it, I'm facing below issues:
When I am using dispatcher servlet mapping as /, I am able to access the controllers but not html page.
When I change the mapping to /**, then I am able to access the html page but not the controllers.
I am not sure if I should add another dispatcher servlet and add one mapping in it. I also tried passing both the mappings in the dispatcher servlet, but it didn't work.
Maybe someone can help me with the issue.
Below is the code:
AppConfig
package com.upload.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Configuration
#Import(WebConfig.class)
#ComponentScan(basePackages= "com.upload")
public class AppConfig {
}
WebConfig
package com.upload.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter{
}
ServletInitializer
public class ServletInitializer extends AbstractDispatcherServletInitializer {
#Override
protected WebApplicationContext createServletApplicationContext() {
final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
context.getEnvironment().setActiveProfiles("prod");
return context;
}
#Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
#Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
FileUploadController
#Controller
#RequestMapping("/ws")
public class FileUploadController {
#RequestMapping(value="hello",method=RequestMethod.GET)
public String hello(){
return "Hello";
}
}
Use Following Approach:
You need to add addResourceHandler to pick up the html page from the web-inf structure WEB-INF/views/viewer where all your pages like: js/ html /css/ images etc are present. You can remove your AppConfig .java shown above.
package com.Configuration.si.config;
#Configuration
#ComponentScan("com.Configuration.si")
#EnableWebMvc
public class Configuration extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/viewer/**","/lib/**","/js/**","/images/**","/css/**","/swf/**")
.addResourceLocations("/WEB-INF/views/viewer/","/WEB-INF/views/lib/","/WEB-INF/views/js/","/WEB-INF/views/images/","/WEB-INF/views/css/","/WEB-INF/views/swf/")
.setCachePeriod(315569126);
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
Also Add this class to your structure
package com.Configuration.si.config;
#Configuration
public class WebMvcConfiguration {
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setViewClass(JstlView.class);
internalResourceViewResolver.setPrefix("/WEB-INF/views/");
internalResourceViewResolver.setSuffix(".jsp");
return internalResourceViewResolver;
}
}
Internal view resolver will resolve the page you need using model.setViewName("login"); in controller. Hope it will help you

AbstractSecurityWebApplicationInitializer vs. AbstractAnnotationConfigDispatcherServletInitializer

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
{
}
}
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
Answering using 5.0.6 Spring Security version:
#EnableWebSecurity already contains #Configuration, therefore
specifying both is redundant
You are indeed required to have a second appinitializer, which extends AbstractSecurityWebApplicationInitializer. Your one seems OK
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.

How to use spring MVC's <mvc:resources> tag in a java application context?

I have created 'for now' a simple and basic spring web application. I am used to have a deployment descriptor as a simple web.xml file, and then an application context as a xml file.
Though, now i wanted to try to create my entire spring web application using only java files. Therefore i have created my WebApplicationInitializer instead of the normal deployment descriptor, and my application context which uses the #Configuration annotation.
Deployment Descriptor
package dk.chakula.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
/**
*
* #author martin
* #since 12-1-2012
* #version 1.0
*/
public class Initializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext)
throws ServletException {
registerDispatcherServlet(servletContext);
}
private void registerDispatcherServlet(final ServletContext servletContext) {
WebApplicationContext dispatcherContext = createContext(ChakulaWebConfigurationContext.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(dispatcherContext);
Dynamic dispatcher = servletContext.addServlet("dispatcher", dispatcherServlet);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
private WebApplicationContext createContext(final Class<?>... annotatedClasses) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(annotatedClasses);
return context;
}
} //End of class Initializer
Application context
package dk.chakula.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import org.springframework.web.servlet.view.tiles2.TilesConfigurer;
import org.springframework.web.servlet.view.tiles2.TilesView;
/**
*
* #author martin
* #since 12-01-2013
* #version 1.0
*/
#Configuration
#EnableWebMvc
#ComponentScan("dk.chakula.web")
public class ChakulaWebConfigurationContext {
#Bean
public TilesConfigurer setupTilesConfigurer() {
TilesConfigurer configurer = new TilesConfigurer();
String[] definitions = {"/layout/layout.xml"};
configurer.setDefinitions(definitions);
return configurer;
}
#Bean
public UrlBasedViewResolver setupTilesViewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(TilesView.class);
return viewResolver;
}
} //End of class ChakulaWebConfigurationContext
My problem is that I can't seem to find a way 'isolate' my mapping to resources folder which contains images, css javascript etc. When my application context is in java.
With the normal XML application context I used this tag to isolate the mapping to /resources/
<mvc:resources mapping="/resources/**" location="/resources/" />
How can I do this, so my web application can use my images, css etc.
To be able to serve static resources in Spring MVC application you need two XML-tags: <mvc:resources/> and <mvc:default-servlet-handler/>. The same in the Java-based Spring configuration will be:
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
// equivalents for <mvc:resources/> tags
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
registry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
}
// equivalent for <mvc:default-servlet-handler/> tag
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// ... other stuff ...
}
Note that since #EnableWebMvc annotation is used there's no need to extend directly WebMvcConfigurationSupport, and you should just extend WebMvcConfigurerAdapter. See JavaDoc for #EnableWebMvc for details.
After using hours searching on the internet reading about Spring MVC 3 using only java files I fell over some articles which used an approach by extending from WebMvcConfigurationSupport class, and then overriding 2 methods - addResourceHandler( ResourceHandlerRegistry ) and ResourceHandlerMapping().
My new Application context now look like this.
package dk.chakula.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import org.springframework.web.servlet.view.tiles2.TilesConfigurer;
import org.springframework.web.servlet.view.tiles2.TilesView;
/**
*
* #author martin
* #since 12-01-2013
* #version 1.0
*/
#Configuration
#EnableWebMvc
#ComponentScan("dk.chakula.web")
public class ChakulaWebConfigurationContext extends WebMvcConfigurationSupport {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
#Bean
public HandlerMapping resourceHandlerMapping() {
AbstractHandlerMapping handlerMapping = (AbstractHandlerMapping) super.resourceHandlerMapping();
handlerMapping.setOrder(-1);
return handlerMapping;
}
#Bean
public TilesConfigurer setupTilesConfigurer() {
TilesConfigurer configurer = new TilesConfigurer();
String[] definitions = {"/layout/layout.xml"};
configurer.setDefinitions(definitions);
return configurer;
}
#Bean
public UrlBasedViewResolver setupTilesViewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(TilesView.class);
return viewResolver;
}
} //End of class ChakulaWebConfigurationContext
As I understood We had to override addResourceHandler, to add the location and the mapping of resources to the registry. Thereafter we needed a bean which returned an object of HandlerMapping. The order of this HandlerMapping should be set to -1, because as I could read from the spring documentation, then -1 means
HandlerMapping ordered at Integer.MAX_VALUE-1 to serve static
resource requests.
My application can now load the css files and images into their views, and I wanted to enlighten you others with the answer so, people in the future could get benefit of this.
Try this:
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}

Categories

Resources