Freemarker template error handling with Spring MVC - spring-mvc

I am using Freemarker template with Spring MVC. Is there a way to return HTTP Status 500 if there is any error rendering the template?
Currently I am using attempt block to handle error, but would like to throw Internal Server error and allow web server to redirect to a default error page
<#attempt>
attempt block
<#recover>
recover block
</#attempt>
Below is my servlet-context.xml
<beans:bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<beans:property name="cache" value="true" />
<beans:property name="prefix" value="" />
<beans:property name="contentType" value="text/html; charset=UTF-8" />
<beans:property name="suffix" value=".ftl" />
<beans:property name="exposeSessionAttributes" value="true" />
</beans:bean>

Below change in servlet-context.xml resolved the issue. Now it throws HTTP 500 error which is intercepted by web server and I am able to mast the message by redirecting to pre-defined error page.
<beans:bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<beans:property name="templateLoaderPath" value="/WEB-INF/ftl/" />
<beans:property name="freemarkerSettings">
<beans:props>
<beans:prop key="template_exception_handler">rethrow</beans:prop>
</beans:props>
</beans:property>
</beans:bean>

This is the java-based equivalent configuration:
#Bean
public FreeMarkerConfigurer freemarkerConfig() throws TemplateException {
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
freeMarkerConfigurer.setTemplateLoaderPath("/WEB-INF/views/ftl/");
Properties settings = new Properties();
settings.setProperty(freemarker.template.Configuration.TEMPLATE_EXCEPTION_HANDLER_KEY, "rethrow");
freeMarkerConfigurer.setFreemarkerSettings(settings);
return freeMarkerConfigurer;
}

A simple way to change the mode from "debug" to "rethrow" is to configure it in your application.properties:
spring.freemarker.settings.template_exception_handler=rethrow

Related

How to configure ldap with spring 4.0

I am facing issue in authenticating with openldap, I dont know how to configure spring security, ldap with Spring 4.0.0 version. Kindly provide sample reference.
<beans:bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg
value="ldap://localhost:389/dc=test,dc=com" />
</beans:bean>
<security:ldap-server id="contextSource"
url="ldap://localhost:389/dc=test,dc=com" />
<beans:bean id="authMgr"
class="org.springframework.security.authentication.ProviderManager">
<beans:constructor-arg>
<beans:list>
<beans:bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<beans:constructor-arg ref="contextSource" />
<beans:property name="userDnPatterns">
<beans:list>
<beans:value>uid={0},ou=users</beans:value>
</beans:list>
</beans:property>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
</beans:list>
</beans:constructor-arg>
</beans:bean>
<security:authentication-manager>
<security:ldap-authentication-provider
server-ref="contextSource" user-search-base="ou=users"
user-search-filter="(uid={0})" group-search-filter="ou=groups">
<security:password-compare hash="{sha}"
password-attribute="userPassword" />
</security:ldap-authentication-provider>
</security:authentication-manager>
Spring Security 4.0 LDAP Reference
LDAP best practise would be to search for the DN of the entry, hence configure
<bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0" value=""/>
<constructor-arg index="1" value="(uid={0})"/>
<constructor-arg index="2" ref="contextSource" />
</bean>
Also typically the LDAP static group entries' naming attribute is cn, hence configure
<bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource"/>
<constructor-arg value="ou=groups"/>
<property name="groupRoleAttribute" value="cn"/>
</bean>
instead of the example shown in the guide

issue when enable CORS on Spring Security

I'm trying to enable CORS on the Spring Security 4.2.3.RELEASE.
spring-mvc.xml
<mvc:mapping path="/rest/**"
allowed-origins="*"
allowed-methods="GET, POST, HEAD, OPTIONS, PUT, DELETE"
allowed-headers="Content-Type, X-Requested-With,accept, Origin,Access-Control-Request-Method, Access-Control-Request-Headers, Authorization"
exposed-headers="Access-Control-Allow-Origin,Access-Control-Allow-Credentials"
allow-credentials="false"
max-age="10" />
</mvc:cors>
spring-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http pattern="/rest/**" use-expressions="true" entry-point-ref="unauthorizedEntryPoint" create-session="stateless">
<csrf disabled="true"/>
<cors/>
<custom-filter before="FORM_LOGIN_FILTER" ref="jwtAuthenticationFilter"/>
</http>
when try to deploy:
Caused by: org.springframework.beans.factory.BeanCreationException: Could not create CorsFilter
I was solving a similar issue. I tried several approaches and here is the setting that works best if anybody else needs it.
SpringSecurity: 4.2.0.RELEASE. All server's API calls are prefixed with /api/ path
Spring security context:
<http use-expressions='true'>
<!-- unrelated code skipped for brevity -->
<cors configuration-source-ref="corsSource"/>
</http>
<beans:bean id="corsSource" class="org.springframework.web.cors.UrlBasedCorsConfigurationSource">
<beans:property name="corsConfigurations">
<util:map>
<beans:entry key="/api/**">
<beans:bean class="org.springframework.web.cors.CorsConfiguration">
<beans:property name="allowCredentials" value="true"/>
<beans:property name="allowedHeaders">
<beans:list>
<beans:value>Authorization</beans:value>
<beans:value>Content-Type</beans:value>
</beans:list>
</beans:property>
<beans:property name="exposedHeaders">
<beans:list>
<beans:value>Account-Locked</beans:value>
<beans:value>Account-Disabled</beans:value>
<beans:value>Bad-Credentials</beans:value>
</beans:list>
</beans:property>
<beans:property name="allowedMethods">
<beans:list>
<beans:value>POST</beans:value>
<beans:value>GET</beans:value>
<beans:value>OPTIONS</beans:value>
</beans:list>
</beans:property>
<beans:property name="allowedOrigins" value="http://localhost:3000"/>
</beans:bean>
</beans:entry>
</util:map>
</beans:property>
</beans:bean>
And in my dispatcher-servlet.xml
<mvc:cors>
<mvc:mapping path="/api/**"
allowed-origins="http://localhost:3000"
allow-credentials="true"
allowed-methods="GET, OPTIONS"
/>
</mvc:cors>
Remember to change allowed-origins as well as allowedHeaders (= headers client is allowed to attach to request), exposedHeaders (= headers the server is allowed to attach to its response) and allowedMethods.
To keep sessions for authorized users I have to add allowCredentials property in Spring Security configuration and also to set withCredentials flag to true in JavaScript calls. I used axios so if you use another library, the flag may be named differently.

template might not exist or might not be accessible

I have been trying to use thymeleaf layout dialect with spring mvc as mentioned in https://github.com/ultraq/thymeleaf-layout-dialect. My spring-servlet as below
<beans:bean id="templateResolver"
class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<beans:property name="prefix" value="/WEB-INF/templates/" />
<beans:property name="suffix" value=".html" />
<beans:property name="templateMode" value="HTML5" />
</beans:bean>
<beans:bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
<beans:property name="templateResolver" ref="templateResolver" />
<!-- These lines add the dialect to Thymeleaf -->
<beans:property name="additionalDialects">
<beans:set>
<beans:bean class="nz.net.ultraq.thymeleaf.LayoutDialect" />
</beans:set>
</beans:property>
</beans:bean>
<beans:bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
<beans:property name="templateEngine" ref="templateEngine" />
<beans:property name="characterEncoding" value="UTF-8" />
</beans:bean>
I have kept all the template file in /WEB-INF/templates/. When I use following code to create and use tempting in thymeleaf
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="Layout.html">
I get following error:
[THYMELEAF] * Dialect [1 of 2]: org.thymeleaf.spring3.dialect.SpringStandardDialect
[THYMELEAF] * Prefix: "th"
[THYMELEAF] * Dialect [2 of 2]: nz.net.ultraq.thymeleaf.LayoutDialect
[THYMELEAF] * Prefix: "layout"
[THYMELEAF] TEMPLATE ENGINE CONFIGURED OK
19:32:40,992 INFO [org.thymeleaf.TemplateEngine] (http-localhost-127.0.0.1-8080-1) [THYMELEAF] TEMPLATE ENGINE INITIALIZED
19:32:41,199 ERROR [org.thymeleaf.TemplateEngine] (http-localhost-127.0.0.1-8080-1) [THYMELEAF][http-localhost-127.0.0.1-8080-1] Exception processing template "home": Error resolving template "Layout.html", template might not exist or might not be accessible by any of the configured Template Resolvers (home:4)
19:32:41,202 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/expensemanager].[appServlet]] (http-localhost-127.0.0.1-8080-1) Servlet.service() for servlet appServlet threw exception: org.thymeleaf.exceptions.TemplateInputException: Error resolving template "MainLayout.html", template might not exist or might not be accessible by any of the configured Template Resolvers (home:4)
If I don't layout:decorator="Layout.html" code works perfectly without any error.
Below is the image which shows my project structure
I usually do this:
<beans:bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
<beans:property name="characterEncoding" value="UTF-8" />
<beans:property name="templateEngine">
<beans:bean class="org.thymeleaf.spring3.SpringTemplateEngine">
<beans:property name="additionalDialects">
<beans:set>
<beans:bean class="nz.net.ultraq.thymeleaf.LayoutDialect" />
</beans:set>
</beans:property>
<beans:property name="templateResolvers">
<beans:bean class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<beans:property name="prefix" value="/WEB-INF/templates/" />
<beans:property name="suffix" value=".html" />
<beans:property name="templateMode" value="HTML5" />
</beans:bean>
</beans:property>
</beans:bean>
</beans:property>
</beans:bean>
If this does not work, can you post a screenshot of the file and folder structure?

spring security using kerberos/spnego authentication

I have got spring security using kerberos authentication successfully working. But it seems the spring framework is invoking KerberosServiceAuthenticationProvider.userDetailsService to get the roles, I would have thought that it gets the roles only once until the session is invalidated. My config looks like
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<http entry-point-ref="spnegoEntryPoint" auto-config="false">
<intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/j_spring_security_check*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<custom-filter ref="spnegoAuthenticationProcessingFilter" position="BASIC_AUTH_FILTER" />
<form-login login-page="/login.html" default-target-url="/" always-use-default-target="true"/>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="kerberosServiceAuthenticationProvider" />
<authentication-provider ref="kerberosAuthenticationProvider"/>
</authentication-manager>
<beans:bean id="spnegoEntryPoint"
class="org.springframework.security.extensions.kerberos.web.SpnegoEntryPoint" />
<beans:bean id="spnegoAuthenticationProcessingFilter"
class="org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter">
<beans:property name="failureHandler">
<beans:bean class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login.html" />
<beans:property name="allowSessionCreation" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="kerberosServiceAuthenticationProvider"
class="org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider">
<beans:property name="ticketValidator">
<beans:bean
class="org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator">
<beans:property name="servicePrincipal" value="HTTP/mywebserver.corpza.corp.co.za"/>
<beans:property name="keyTabLocation" value="classpath:mywebserver.keytab" />
<beans:property name="debug" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="userDetailsService" ref="dummyUserDetailsService" />
</beans:bean>
<beans:bean id="kerberosAuthenticationProvider" class="org.springframework.security.extensions.kerberos.KerberosAuthenticationProvider">
<beans:property name="kerberosClient">
<beans:bean class="org.springframework.security.extensions.kerberos.SunJaasKerberosClient">
<beans:property name="debug" value="true" />
</beans:bean>
</beans:property>
<beans:property name="userDetailsService" ref="dummyUserDetailsService" />
</beans:bean>
<beans:bean class="org.springframework.security.extensions.kerberos.GlobalSunJaasKerberosConfig">
<beans:property name="debug" value="true" />
<beans:property name="krbConfLocation" value="/etc/krb5.conf" />
</beans:bean>
<beans:bean id="dummyUserDetailsService" class="main.server.DummyUserDetailsService"/>
</beans:beans>
so my DummyUserDetailsService.loadUserByUsername(Styring username) is invoked each time a secure page is requested, I am loading the user roles from database and don't want to run the query each time a request is made, is there any configuration I need to do to prevent this?
thanks Michael, I got it working by extending SpnegoAuthenticationProcessingFilter class and overriding doFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (skipIfAlreadyAuthenticated) {
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
if (existingAuth != null && existingAuth.isAuthenticated()
&& (existingAuth instanceof AnonymousAuthenticationToken) == false) {
chain.doFilter(request, response);
return;
}
}
super.doFilter(req, res, chain);
}
Tell Spring Security to cache the authentication in the HTTP Session. Here is how.

Spring controller with #RequestBody and #ModelAttribute for the same parameter

I want to create a controller method that can be called from html form or from a standalone client using resttemplate.
The method has this signature:
#RequestMapping(method = RequestMethod.POST)
public ModelAndView save(#RequestBody #ModelAttribute(value = "itemCreateRequest")
ItemCreateRequest request, Model model) throws NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
...
}
The problem is, that if I use the HTML form, everything works fine, but when I try to use the standalone client the values in the request parameter come in null.
ItemCreateRequest icr = new ItemCreateRequest();
icr.setItem(new ItemDTO(null, "23", "bla", "bla bla"));
restTemplate.postForLocation("http://localhost:8080/ims_ui/items.xml", icr);
If I try the method without the #ModelAttribute the standalone client works but the HTML form not.
This is the configuration of my servlet-context.xml
<beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<beans:bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<beans:property name="messageConverters">
<beans:list>
<beans:bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<beans:property name="marshaller" ref="marshaller"/>
<beans:property name="unmarshaller" ref="marshaller"/>
</beans:bean>
<beans:bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<beans:bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<beans:property name="order" value="0" />
<beans:property name="mediaTypes">
<beans:map>
<beans:entry key="jsp" value="application/html" />
<beans:entry key="xml" value="application/xml" />
</beans:map>
</beans:property>
<beans:property name="defaultViews">
<beans:list>
<!-- XML View -->
<beans:bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<beans:constructor-arg ref="marshaller" />
</beans:bean>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<beans:property name="contextPaths">
<beans:array>
<beans:value>net.franciscovillegas.summa.ims.dto</beans:value>
<beans:value>net.franciscovillegas.summa.ims.service.requests</beans:value>
</beans:array>
</beans:property>
</beans:bean>
Thanks in advance!
The answer is in you're post:
If I try the method without the #ModelAttribute the standalone client
works but the HTML form not.
I think that, when you use the html application you send a request to the server with a map where all keys starts with itemCreateRequest.VARIABLE_NAME , but when you use stand alone application you send that request without the declared #ModelAttribute.
The declared ModelAttribute, in you case "itemCreateRequest", should stand in front of each information sent to that controller.
You can try something like this:
restTemplate.postForLocation("http://localhost:8080/ims_ui/items.xml", new HashMap<String, Object>(){
{
put("itemCreateRequest", icr);
}
});

Resources