Spring Java Config and Thymeleaf - Dandelion Datatables config - spring-mvc

Attempting to get pagination on datatables using Thymeleaf and Dandelion. According to the docs I need to update a few things:
web.xml (javaconfig attempt further down)
<!-- Dandelion filter definition and mapping -->
<filter>
<filter-name>dandelionFilter</filter-name>
<filter-class>com.github.dandelion.core.web.DandelionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>dandelionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Dandelion servlet definition and mapping -->
<servlet>
<servlet-name>dandelionServlet</servlet-name>
<servlet-class>com.github.dandelion.core.web.DandelionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dandelionServlet</servlet-name>
<url-pattern>/dandelion-assets/*</url-pattern>
</servlet-mapping>
SpringTemplateEngine #Bean (skipped as I already have the Thymeleaf template Engine)
<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
<property name="additionalDialects">
<set>
<bean class="com.github.dandelion.datatables.thymeleaf.dialect.DataTablesDialect" />
</set>
</property>
</bean>
My knowledge of Spring is still extremely shaky but I have to replace the web.xml components (least I think I can do it this way):
public class Initializer extends
AbstractAnnotationConfigDispatcherServletInitializer...
#Override
protected Class<?>[] getServletConfigClasses() {
logger.debug("Entering getServletConfigClasses()");
return new Class<?>[] { ThymeleafConfig.class, WebAppConfig.class, DandelionServlet.class };
}
#Override
protected Filter[] getServletFilters() {
return new Filter[] { new DandelionFilter(),
new DelegatingFilterProxy("springSecurityFilterChain") };
}
My ThymeleafConfig:
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(dataTablesDialect());
return templateEngine;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
return resolver;
}
#Bean
public DataTablesDialect dataTablesDialect() {
return new DataTablesDialect();
}
My security settings:
.antMatchers("/dandelion-assets/**").permitAll()
.antMatchers("/datatablesController/**").permitAll()
I get the following in my logs after both dialects load:
[THYMELEAF] TEMPLATE ENGINE CONFIGURED OK
INFO org.thymeleaf.TemplateEngine - [THYMELEAF] TEMPLATE ENGINE INITIALIZED
When the page loads:
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - No custom configuration. Using default one.
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - Resolving groups for the locale en_US...
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - 1 groups declared [global].
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - Resolving configurations for the locale en_US...
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - Group 'global' initialized with 0 properties
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - 1 group(s) resolved [global] for the locale en_US
DEBUG com.github.dandelion.datatables.thymeleaf.processor.el.TableFinalizerElProcessor - No configuration to apply, i.e. no 'dt:conf' has been found in the current template.
DEBUG com.github.dandelion.datatables.core.generator.configuration.DatatablesGenerator - Generating DataTables configuration ..
DEBUG com.github.dandelion.datatables.core.generator.configuration.DatatablesGenerator - DataTables configuration generated
DEBUG com.github.dandelion.datatables.core.generator.WebResourceGenerator - Loading extensions...
DEBUG com.github.dandelion.datatables.core.extension.ExtensionLoader - Scanning built-in extensions...
DEBUG com.github.dandelion.datatables.core.generator.WebResourceGenerator - Transforming configuration to JSON...
DEBUG com.github.dandelion.datatables.thymeleaf.processor.el.TableFinalizerElProcessor - Web content generated successfully
DEBUG com.github.dandelion.datatables.core.configuration.DatatablesConfigurator - Initializing the Javascript generator...
Only warning I get:
WARN com.github.dandelion.core.asset.AssetMapper - No location found for delegate on AssetStorageUnit [name=dandelion-datatables, version=0.10.0, type=js, dom=null, locations={delegate=dandelion-datatables.js}, attributes=null, attributesOnlyName=[]]
DEBUG com.github.dandelion.core.asset.cache.AssetCacheManager - Retrieving asset with the key 6c075191955bbb1ecbd703380e648817806cf15b/dandelion-datatables-0.10.0.js
DEBUG com.github.dandelion.core.asset.cache.AssetCacheManager - Storing asset under the key 6c075191955bbb1ecbd703380e648817806cf15b/dandelion-datatables-0.10.0.js
WARN com.github.dandelion.core.asset.AssetMapper - No location found for delegate on AssetStorageUnit [name=dandelion-datatables, version=0.10.0, type=js, dom=null, locations={delegate=dandelion-datatables.js}, attributes=null, attributesOnlyName=[]]
DEBUG com.github.dandelion.core.asset.cache.AssetCacheManager - Retrieving asset with the key 6c075191955bbb1ecbd703380e648817806cf15b/dandelion-datatables-0.10.0.js
DEBUG com.github.dandelion.core.asset.cache.AssetCacheManager - Storing asset under the key 6c075191955bbb1ecbd703380e648817806cf15b/dandelion-datatables-0.10.0.js
And finally:
<table class="table table-striped table-bordered table-hover"
dt:table="true" id="myTable" dt:pagination="true">
When the page loads, the data loads but it isn't paginated; where'd I go wrong?

Just tried to set this up.
Here follows my working configuration.
WebConfig.java
#Configuration
#ComponentScan(basePackages = { "com.github.dandelion.datatables.web" })
#EnableWebMvc
#Import({ ThymeleafConfig.class })
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Here is the imported ThymeleafConfig.java class:
#Configuration
public class ThymeleafConfig {
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setCacheable(false);
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
engine.addDialect(new DandelionDialect());
engine.addDialect(new DataTablesDialect());
return engine;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
return resolver;
}
}
Then the root configuration. Quite hairy as you can see.
RootConfig.java
#Configuration
#ComponentScan(basePackages = { "com.github.dandelion.datatables.service", "com.github.dandelion.datatables.repository" })
public class RootConfig {
}
And finally, the app initializer. Make sure to have well configured the Dandelion components (DandelionFilter and DandelionServlet).
ApplicationInitializer.java
public class ApplicationInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Register the Root application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(RootConfig.class);
// Register the Web application context
AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
mvcContext.register(WebConfig.class);
// Context loader listener
servletContext.addListener(new ContextLoaderListener(rootContext));
// Register the Dandelion filter
FilterRegistration.Dynamic dandelionFilter = servletContext.addFilter("dandelionFilter", new DandelionFilter());
dandelionFilter.addMappingForUrlPatterns(null, false, "/*");
// Register the Spring dispatcher servlet
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("springServlet", new DispatcherServlet(mvcContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
// Register the Dandelion servlet
ServletRegistration.Dynamic dandelionServlet = servletContext.addServlet("dandelionServlet", new DandelionServlet());
dandelionServlet.setLoadOnStartup(2);
dandelionServlet.addMapping("/dandelion-assets/*");
}
}
You can see the full sample application here.
Hope this helps!
(Disclaimer required by StackOverflow: I'm the author of Dandelion)

Related

Spring Boot adding thymeleaf-layout-dialect

I am using spring boot (v1.5.3.BUILD-SNAPSHOT)
I am new to spring boot.
Using gradle
Note that the normal thymeleaf dialect works fine (th:...)
spring-boot-starter-thymeleaf\1.5.3.BUILD-SNAPSHOT
I want to add thymeleaf-layout-dialect
I added the dependency
compile('nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect')
the documentation says to add the dialect by doing the following
TemplateEngine templateEngine = new TemplateEngine(); // Or
SpringTemplateEngine for Spring config
templateEngine.addDialect(new LayoutDialect());
So i added a configuration class
#Configuration
public class MyConfiguration {
#Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addDialect(new LayoutDialect());
return templateEngine;
}
}
but when I try running the app I get the following error
org.thymeleaf.exceptions.ConfigurationException: Cannot initialize: no template resolvers have been set
at org.thymeleaf.Configuration.initialize(Configuration.java:203) ~[thymeleaf-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.thymeleaf.TemplateEngine.initialize(TemplateEngine.java:827) ~[thymeleaf-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:203) ~[thymeleaf-spring4-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:190) ~[thymeleaf-spring4-2.1.5.RELEASE.jar:2.1.5.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1282) ~[spring-webmvc-4.3.8.BUILD-SNAPSHOT.jar:4.3.8.BUILD-SNAPSHOT]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037) ~[spring-webmvc-4.3.8.BUILD-SNAPSHOT.jar:4.3.8.BUILD-SNAPSHOT]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980) ~[spring-webmvc-4.3.8.BUILD-SNAPSHOT.jar:4.3.8.BUILD-SNAPSHOT]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.8.BUILD-SNAPSHOT.jar:4.3.8.BUILD-SNAPSHOT]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.8.BUILD-SNAPSHOT.jar:4.3.8.BUILD-SNAPSHOT]
Can someone tell me how to add the thymeleaf-layout-dialect correctly?
The issue is:
org.thymeleaf.exceptions.ConfigurationException: Cannot initialize: no template resolvers have been set
To integrate Thymeleaf with Spring, you need to configure 3 beans:
ThymeleafViewResolver Bean - You would be set it with a template engine
SpringTemplateEngine Bean - You would be set it with a template resolver
TemplateResolver Bean
In your templateEngine bean you didn't set any template resolver, so you might change your templateEngine() method as following:
#Bean
public SpringTemplateEngine templateEngine(TemplateResolver templateResolver){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
templateEngine.addDialect(new LayoutDialect());
return templateEngine;
}
Spring will provide you with a templateResolver bean of SpringTemplateEngine.
BTW If you define spring-boot-starter-thymeleaf as dependency, it will provide thymeleaf-layout-dialect as a dependency with the convenient version, then Spring will use ThymeleafAutoConfiguration.java - Spring Boot 1.5.x to configure a default beans for the three required beans.
For example:
LayoutDialect bean is define here ThymeleafWebLayoutConfiguration.ThymeleafWebLayoutConfiguration():
#Configuration
#ConditionalOnClass(name = "nz.net.ultraq.thymeleaf.LayoutDialect")
protected static class ThymeleafWebLayoutConfiguration {
#Bean
#ConditionalOnMissingBean
public LayoutDialect layoutDialect() {
return new LayoutDialect();
}
}
SpringTemplateEngine bean is defined with a template resolver and dialect from here ThymeleafWebLayoutConfiguration.ThymeleafDefaultConfiguration():
#Configuration
#ConditionalOnMissingBean(SpringTemplateEngine.class)
protected static class ThymeleafDefaultConfiguration {
private final Collection<ITemplateResolver> templateResolvers;
private final Collection<IDialect> dialects;
public ThymeleafDefaultConfiguration(
Collection<ITemplateResolver> templateResolvers,
ObjectProvider<Collection<IDialect>> dialectsProvider) {
this.templateResolvers = templateResolvers;
this.dialects = dialectsProvider.getIfAvailable();
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
for (ITemplateResolver templateResolver : this.templateResolvers) {
engine.addTemplateResolver(templateResolver);
}
if (!CollectionUtils.isEmpty(this.dialects)) {
for (IDialect dialect : this.dialects) {
engine.addDialect(dialect);
}
}
return engine;
}
}
And finally a thymeleafViewResolver bean is defined here AbstractThymeleafViewResolverConfiguration.thymeleafViewResolver():
#Bean
#ConditionalOnMissingBean(name = "thymeleafViewResolver")
#ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
configureTemplateEngine(resolver, this.templateEngine);
resolver.setCharacterEncoding(this.properties.getEncoding().name());
resolver.setContentType(appendCharset(this.properties.getContentType(),
resolver.getCharacterEncoding()));
resolver.setExcludedViewNames(this.properties.getExcludedViewNames());
resolver.setViewNames(this.properties.getViewNames());
// This resolver acts as a fallback resolver (e.g. like a
// InternalResourceViewResolver) so it needs to have low precedence
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5);
resolver.setCache(this.properties.isCache());
return resolver;
}
which is extended by ThymeleafAutoConfiguration.Thymeleaf2ViewResolverConfiguration:
#Bean
#ConditionalOnMissingBean(name = "thymeleafViewResolver")
#ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
configureTemplateEngine(resolver, this.templateEngine);
resolver.setCharacterEncoding(this.properties.getEncoding().name());
resolver.setContentType(appendCharset(this.properties.getContentType(),
resolver.getCharacterEncoding()));
resolver.setExcludedViewNames(this.properties.getExcludedViewNames());
resolver.setViewNames(this.properties.getViewNames());
// This resolver acts as a fallback resolver (e.g. like a
// InternalResourceViewResolver) so it needs to have low precedence
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5);
resolver.setCache(this.properties.isCache());
return resolver;
}
Hope it is clear now.

Spring Boot & Thymeleaf with XML Templates

I have a Spring Boot application with a controller that returns a ModelAndView and Thymeleaf to render templates, where the templates live in /src/main/resources/templates/*.html
This works fine, but How can I configure Spring and/or Thymeleaf to look for xml files instead of html?
If it helps, I'm using Gradle with the org.springframework.boot:spring-boot-starter-web dependency to set things up. I am currently running the server using a class with a main method.
After trying and failing at various bean defs for viewResolver and related things, I finally got this working with a change to my application.yaml file:
spring:
thymeleaf:
suffix: .xml
content-type: text/xml
For those reading this later, you can do similar with your application.properties file (with dot notation in place of the yaml indentation).
This works too :
#Configuration
public class MyConfig
{
#Bean
SpringResourceTemplateResolver xmlTemplateResolver(ApplicationContext appCtx) {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(appCtx);
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".xml");
templateResolver.setTemplateMode("XML");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setCacheable(false);
return templateResolver;
}
#Bean(name="springTemplateEngine")
SpringTemplateEngine templateEngine(ApplicationContext appCtx) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(xmlTemplateResolver(appCtx));
return templateEngine;
}
}
And for the usage
#RestController
#RequestMapping("/v2/")
public class MenuV2Controller {
#Autowired
SpringTemplateEngine springTemplateEngine;
#GetMapping(value ="test",produces = {MediaType.APPLICATION_XML_VALUE})
#ResponseBody
public String test(){
Map<String, String> pinfo = new HashMap<>();
Context context = new Context();
context.setVariable("pinfo", pinfo);
pinfo.put("lastname", "Jordan");
pinfo.put("firstname", "Michael");
pinfo.put("country", "USA");
String content = springTemplateEngine.process("person-details",context);
return content;
}
}
Don't forget the template in resources/templates folder
<?xml version="1.0" encoding="UTF-8"?>
<persons >
<person>
<fname th:text="${pinfo['lastname']}"></fname>
<lname th:text="${pinfo['firstname']}"></lname>
<country th:text="${pinfo['country']}"></country>
</person>
</persons>

How to set context-param in spring-boot

In the classic web.xml type configuration you could configure context parameters like so
web.xml
...
<context-param>
<param-name>p-name</param-name>
<param-value>-value</param-value>
</context-param>
...
How is this achieved in spring-boot. I have a filter that requires parameters.
I'm using #EnableAutoConfiguration and have included <artifactId>spring-boot-starter-jetty</artifactId> in my pom.
You can set parameters using the server.servlet.context-parameters application property. For example:
server.servlet.context-parameters.p-name=p-value
In Spring Boot 1.x, which is no longer supported, this property was named server.context-parameters:
servlet.context-parameters=p-name=p-value
Alternatively, you can configure parameters programmatically by declaring a ServletContextInitializer bean:
#Bean
public ServletContextInitializer initializer() {
return new ServletContextInitializer() {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("p-name", "-value");
}
};
}
You can actually achieve this using Java config. If you have filter that requires some parameters, just put them in your application.yml (or .properties), inject them using #Value in your config class and register them in FilterRegistrationBean.
For example:
#Value("${myFilterParam}")
private String myFilterParam;
#Bean(name="myFilter")
public FilterRegistrationBean myFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());
filterRegistrationBean.setInitParameters(Collections.singletonMap("p-name", "p-value"));
return filterRegistrationBean;
}
Also JavaDoc for FilterRegistrationBean:
http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/embedded/FilterRegistrationBean.html
Update
You can register parameters for servlet context in SpringBootServletInitializer#onStartup() method. Your Application class can extend the SpringBootServletInitializer and you can override the onStartup method and set the parameters there. Example:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("p-name", "p-value");
super.onStartup(servletContext);
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
Other alternative is to define ServletContextInitializer bean as suggested by Andy Wilkinson.
Since Spring Boot 2.0.0 they updated the way to add context param:
server.servlet.context-parameters.yourProperty.
You can see more updates on this link
Also you can define InitParameterConfiguringServletContextInitializer in your configuration. Example:
#Bean
public InitParameterConfiguringServletContextInitializer initParamsInitializer() {
Map<String, String> contextParams = new HashMap<>();
contextParams.put("p-name", "-value");
return new InitParameterConfiguringServletContextInitializer(contextParams);
}

404 error using Spring MVC Security Java Config on JBoss

I wrote a small Spring MVC application with Java Config and secured it with Spring Security 3.2.5. It is working perfectly fine on Tomcat but not on JBoss EAP 6.2. It gets successfully deployed on JBoss but I get this warning when I request any page defined by Spring MVC and 404 error in browser.
WARN [org.springframework.web.servlet.PageNotFound] (http-/127.0.0.1:8080-1) No mapping found for HTTP request with URI [/example-web/pages/login.jsp] in DispatcherServlet with name 'dispatcher'
Since Spring Security is used, for any request that needs authenticated user e.g. http://localhost:8080/example-web/start, Spring Security redirects to https://localhost:8443/example-web/login That is when I see the warning and 404 error.
Here you can see my code:
public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfiguration.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebMvcConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/*" };
}
#Override
protected Filter[] getServletFilters() {
return new Filter[] { new HiddenHttpMethodFilter() };
}
}
Here is my Spring MVC configuration:
#EnableWebMvc
#ComponentScan("com.spring.example.w.controller")
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("login").setViewName("login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
#Bean
public InternalResourceViewResolver getInternalResourceViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/pages/");
resolver.setSuffix(".jsp");
return resolver;
}
}
Below is my Spring Security configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.addFilter(myUsernamePasswordAuthenticationFilter())
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/admin/**").hasRole("Admin")
.antMatchers("/start/**").hasRole("Viewer")
.antMatchers("/user/**").hasRole("User")
.antMatchers("/**").hasRole("User")
.and()
.httpBasic().authenticationEntryPoint(loginUrlAuthenticationEntryPoint())
.and()
.logout()
.permitAll()
.and()
.requiresChannel()
.anyRequest().requiresSecure();
}
#Bean
public LoginUrlAuthenticationEntryPoint loginUrlAuthenticationEntryPoint(){
LoginUrlAuthenticationEntryPoint loginUrlAuthenticationEntryPoint = new LoginUrlAuthenticationEntryPoint("/login");
return loginUrlAuthenticationEntryPoint;
}
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider(){
ActiveDirectoryLdapAuthenticationProvider authProvider = new ActiveDirectoryLdapAuthenticationProvider(my_domain, my_url);
authProvider.setConvertSubErrorCodesToExceptions(true);
authProvider.setUseAuthenticationRequestCredentials(true);
authProvider.setUseAuthenticationRequestCredentials(true);
authProvider.setUserDetailsContextMapper(userDetailsContextMapper());
return authProvider;
}
#Bean
public UserDetailsContextMapper userDetailsContextMapper(){
CustomUserDetailsContextMapper myAuthoritiesPopulator = new CustomUserDetailsContextMapper();
return myAuthoritiesPopulator;
}
#Bean
public CustomUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter()
throws Exception {
CustomUsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new CustomUsernamePasswordAuthenticationFilter();
usernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager());
usernamePasswordAuthenticationFilter.setAllowSessionCreation(true);
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
usernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(successHandler);
usernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/login?error"));
usernamePasswordAuthenticationFilter.afterPropertiesSet();
return usernamePasswordAuthenticationFilter;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
}
Then SecurityWebApplicationInitializer:
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
And RootConfig:
#Configuration
#ComponentScan
public class RootConfiguration {
}
And below is how I configured JBoss for SSL:
<subsystem xmlns="urn:jboss:domain:web:1.5" default-virtual-server="default-host" native="false">
<connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http" redirect-port="8443"/>
<connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" enable-lookups="false" secure="true">
<ssl name="ssl" password="<my_password>" certificate-key-file="c:\mykeystore.jks" protocol="TLSv1" verify-client="false"/>
</connector>
<virtual-server name="default-host" enable-welcome-root="true">
<alias name="localhost"/>
<alias name="example.com"/>
</virtual-server>
</subsystem>
During deployment, I can see in the log that the requests do get mapped:
INFO [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] (ServerService Thread Pool -- 71) Mapped "{[/start],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView com.spring.example.w.controller.StartController.handleStart() throws javax.servlet.ServletException,java.io.IOException
INFO [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping] (ServerService Thread Pool -- 71) Mapped URL path [/login] onto handler of type [class org.springframework.web.servlet.mvc.ParameterizableViewController]
INFO [org.springframework.web.context.ContextLoader] (ServerService Thread Pool -- 71) Root WebApplicationContext: initialization completed in 2530 ms
INFO [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/example-web]] (ServerService Thread Pool -- 71) Initializing Spring FrameworkServlet 'dispatcher'
INFO [org.springframework.web.servlet.DispatcherServlet] (ServerService Thread Pool -- 71) FrameworkServlet 'dispatcher': initialization started
Any help about why I get 404 error and this warning is highly appreciated. Once again I should emphasize that it is working on Tomcat. Thanks in advance.
Can you check if the connector port is enabled for SSL. This link has some details.
Redirecting from non ssl port 8080 to ssl port 8443
hope it helps

Spring Security without web.xml

What is the recommended way to add Spring Security to a web application that is using Spring's new WebApplicationInitializer interface instead of the web.xml file? I'm looking for the equivalent of:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
UPDATE
The provided answers are reasonable but they both assume that I've got a servletContext instance. I've looked through the hierarchy of WebApplicationInitializers and I don't see any access to the servlet context unless I choose to override one of Spring's initializer methods. AbstractDispatcherServletInitializer.registerServletFilter seems like the sensible choice but it doesn't default to URL pattern mapping and I'd hate to change filter registration for everything if there is a better way.
This is the way that I have done it:
container.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
container is an instance of ServletContext
The Spring Security Reference answers this question and the solution depends on whether or not you are using Spring Security in conjunction with Spring or Spring MVC.
Using Spring Security without Spring or Spring MVC
If you are not using Spring Security with Spring or Spring MVC (i.e. you do not have an existing WebApplicationInitializer) then you need to provide the following additional class:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
Where SecurityConfig is your Spring Security Java configuration class.
Using Spring Security with Spring or Spring MVC
If you are using Spring Security with Spring or Spring MVC (i.e. you have an existing WebApplicationInitializer) then firstly you need to provide the following additional class:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
Then you need to ensure that your Spring Security Java configuration class, SecurityConfig in this example, is declared in your existing Spring or Spring MVC WebApplicationInitializer. For example:
import org.springframework.web.servlet.support.*;
public class MvcWebApplicationInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SecurityConfig.class};
}
// ... other overrides ...
}
Dynamic securityFilter = servletContext.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
EnumSet.allOf(DispatcherType.class) to be sure that you add a mapping not only for default DispatcherType.REQUEST, but for DispatcherType.FORWARD, etc...
After a bit of work, I've discovered that it's actually quite simple:
public class Initialiser extends AbstractAnnotationConfigDispatcherServletInitializer implements WebApplicationInitializer {
#Override
protected Class< ? >[] getRootConfigClasses() {
return new Class[] { RootConfig.class };
}
#Override
protected Class< ? >[] getServletConfigClasses() {
return new Class[] { WebAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Filter[] getServletFilters() {
return new Filter[] { new DelegatingFilterProxy("springSecurityFilterChain") };
}
}
The most important thing, though, is that you must have a root context (e.g. RootConfig in this case), and that must contain a reference to all the spring security information.
Thus, my RootConfig class:
#ImportResource("classpath:spring/securityContext.xml")
#ComponentScan({ "com.example.authentication", "com.example.config" })
#Configuration
public class RootConfig {
#Bean
public DatabaseService databaseService() {
return new DefaultDatabaseService();
}
#Bean
public ExceptionMappingAuthenticationFailureHandler authExceptionMapping() {
final ExceptionMappingAuthenticationFailureHandler emafh = new ExceptionMappingAuthenticationFailureHandler();
emafh.setDefaultFailureUrl("/loginFailed");
final Map<String, String> mappings = new HashMap<>();
mappings.put(CredentialsExpiredException.class.getCanonicalName(), "/change_password");
emafh.setExceptionMappings(mappings);
return emafh;
}
}
And spring/securityContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:noNamespaceSchemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<security:http security="none" pattern="/favicon.ico"/>
<!-- Secured pages -->
<security:http use-expressions="true">
<security:intercept-url pattern="/login" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:form-login default-target-url="/index" login-processing-url="/login_form" login-page="/login" authentication-failure-handler-ref="authExceptionMapping" />
</security:http>
<security:authentication-manager>
<security:authentication-provider ref="customAuthProvider" />
</security:authentication-manager>
</beans>
I could not get it to work if I merged the RootConfig and WebAppConfig classes into just WebAppConfig and had the following:
#Override
protected Class< ? >[] getRootConfigClasses() {
return null;
}
#Override
protected Class< ? >[] getServletConfigClasses() {
return new Class[] { WebAppConfig.class };
}
public class SIServerSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
Dynamic registration = servletContext.addFilter("TenantServletFilter", TenantServletFilter.class);
EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
}
}
This scenario is for executing a filter before executing other filters.
If you want to execute a filter after other filers pass true in registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");. Also check the DispatcherType ASYNC, FORWARD etc.
Faced with the same problem. Merge RootConfig and WebAppConfig - not best way - because this i did not try this solution. Tried all other solutions - everty time got "org.apache.catalina.core.StandardContext.startInternal Error filterStart". After some work, got something like this:
FilterRegistration.Dynamic enc= servletContext.addFilter("encodingFilter",
new CharacterEncodingFilter());
encodingFilter .setInitParameter("encoding", "UTF-8");
encodingFilter .setInitParameter("forceEncoding", "true");
encodingFilter .addMappingForUrlPatterns(null, true, "/*");
But is not working with DelegatingFilterProxy(). Continue finding for best common solution for all filters.
UPDATE:
I did it.
So, the main issue is: if you want add filters using java config, especially if you want to add security filter, such as DelegatingFilterProxy, then you have to create WebAppSecurityConfig:
#Configuration
#EnableWebSecurity
#ImportResource("classpath:security.xml")
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
}
In this case i create WebAppSecurityConfig and make import resource ("security.xml").
This let me to do that in Initializer class:
servletContext.addFilter("securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
This is related to those interested in Spring Boot with security: You don't need anything, Spring Boot picks up the #components and solves the other issues

Resources