Spring Security + Primefaces - can't handle viewexpiredeception - spring

I have problems with spring security and primefaces configuration. For my project, I needed the ability to log in, and it was decided to add spring security. Before I added the spring security, it was like this: if the user opens the page and is idle for half an hour, then the session dies, buttons on the page stop working and I get viewexpiredexception in the IDE's console when an button is pressed. Then I changed the web.xml and faces-config.xml files:
web.xml:
<!-- File(s) appended to a request for a URL that is not mapped to a web component -->
<welcome-file-list>
<welcome-file>mypage.xhtml</welcome-file>
</welcome-file-list>
<error-page>
<exception-type>
javax.faces.application.ViewExpiredException
</exception-type>
<location>/login.xhtml</location> <!-- type whatever suits your environment and requirements -->
</error-page>
<!-- Define the JSF servlet (manages the request processing life cycle for JavaServer Faces) -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map following files to the JSF servlet -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener</listener-class>
</listener>
faces-config.xml
<application>
<el-resolver>
org.primefaces.application.exceptionhandler.PrimeExceptionHandlerELResolver
</el-resolver>
</application>
<factory>
<exception-handler-factory>
org.primefaces.application.exceptionhandler.PrimeExceptionHandlerFactory
</exception-handler-factory>
</factory>
After that, if the session dies and the user presses any button, he will receive a message stating that the session has died and will be redirected to the page.
Problem: After I added spring security, it all stopped working. After half an hour of inactivity, the buttons stop working as before when I didn’t have the handler for viewexpiredexception, but there are no exceptions in the console and no redirecting, the user must refresh page himself. Here is my configuration:
SecurityConfig.java
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/javax.faces.resource/**")
.permitAll().anyRequest().authenticated();
// login
http.formLogin().loginPage("/login.xhtml").permitAll()
.failureUrl("/login.xhtml?error=true");
http.sessionManagement()
.maximumSessions(1)
.expiredUrl("/login.xhtml")
.and()
.invalidSessionUrl("/login.xhtml");
// logout
http.logout().logoutSuccessUrl("/login.xhtml");
// not needed as JSF 2.2 is implicitly protected against CSRF
http.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("john.doe")
.password("{noop}1234").roles("USER").and()
.withUser("jane.doe").password("{noop}5678").roles("ADMIN");
}
}
I could not find how to solve my problem.

Related

JSF with Prettyfaces returning cached error page

I'm developing a web application using JSF 2.2 and tomcat 8, but I'm facing a weird problem...
For example, I have a home page which will call a managedbean, connect to databse and return response. Everything is working, but, if some error happens, it's going to redirect to my error page (500.xhtml, which I set up in my web.xml file).
After the error happens, I fixed the error, but I can't access the home page anymore, it always redirect me for my 500 page. If, i change the the home page for something like home?anything it call my managebean again and works. Also, if i change the server, from localhost to my ip or 127.1.0.0, it also makes the request again.
Following, my web.xml. Also, I'm using prettyfaces 3.4, don't know if it's related but...
Tomcat server.xml and web.xml is default config.
web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>JSF Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JSF Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>home.xhtml</welcome-file>
</welcome-file-list>
<context-param>
<param-name>com.sun.faces.defaultResourceMaxAge</param-name>
<param-value>3628800000</param-value> <!-- 6 weeks. -->
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
<mime-mapping>
<extension>xhtml</extension>
<mime-type>application/xml</mime-type>
</mime-mapping>
<mime-mapping>
<extension>jsp</extension>
<mime-type>application/xml</mime-type>
</mime-mapping>
<error-page>
<error-code>403</error-code>
<location>/error/403.xhtml</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error/404.xhtml</location>
</error-page>
<error-page>
<error-code>405</error-code>
<location>/error/405.xhtml</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500.xhtml</location>
</error-page>
<error-page>
<error-code>503</error-code>
<location>/error/503.xhtml</location>
</error-page>
Prettyfaces:
#RewriteConfiguration
public class ConfigurationProvider extends HttpConfigurationProvider {
#Override
public Configuration getConfiguration(final ServletContext context) {
return ConfigurationBuilder.begin()
.addRule(Join.path("/home").to("/"))
.addRule(Join.path("/home").to("/home.xhtml").withInboundCorrection())
.addRule(Join.path("/company").to("/company.xhtml").withInboundCorrection())
.addRule(Join.path("/portfolio").to("/portfolio.xhtml").withInboundCorrection())
.addRule(Join.path("/contact").to("/contact.xhtml").withInboundCorrection())
.addRule(Join.path("/admin/login").to("/admin/login.xhtml").withInboundCorrection())
.addRule(Join.path("/admin/recovery").to("/admin/recovery.xhtml").withInboundCorrection())
.addRule(Join.path("/403").to("/error/403.xhtml").withInboundCorrection())
.addRule(Join.path("/404").to("/error/404.xhtml").withInboundCorrection())
.addRule(Join.path("/405").to("/error/405.xhtml").withInboundCorrection())
.addRule(Join.path("/500").to("/error/500.xhtml").withInboundCorrection())
.addRule(Join.path("/503").to("/error/503.xhtml").withInboundCorrection());
}
#Override
public int priority() {
return 10;
}
}
Looking at firefox debugger, network tab shows there was a request to home page (which was cached) and the a redirect with 301 code to 500.
#EDIT
It seems to be something with prettyfaces.
I just remove it, and tested with chrome browser (without cache), after the error, i can access the page again. Then, I activate prettyfaces again, and the problem returned.
Any idea why that?

spring rest project with client side in angular js - how to approach the rest service with ajax

So I have a spring rest project that includes client side app.
I can run the service on a local tomcat and get responses querying "http://:8080/books" for example.
I can set app an apache server and go to "http://" to see my client app (the apache htdocs dir points to the project client app dir).
What I can't manage to do is send ajax rest queries to the service.
I'm using angular js so it looks like:
$http.get("http://:8080/books").success(...).error(...);
and it always enters the error callback method.
In the debugger/network tab I see that the request status is "canceled". Looking at the request's details I see next to the "Request headers" title the message: "CAUTION: Provisional headers are shown".
Here is my web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Library Application</display-name>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.library.application</param-value>
</context-param>
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>prod</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>BooksServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.library.service.config.ControllerConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>BooksServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
My project structure is:
src
main
java
resources
webapp
resources
WEB-INF
test
Is it because the origin is different (mainly the port)?
Should I add some code to my web.xml to serve the client through tomcat? If so how?
thanks.
I guess you have a problem with cross-domain requests(yes, different port is another domain for the browser and it will cancel the request for security purposes if server will not include cross-domain headers). Try to add this headers to your REST response:
responseHeaders.add("Access-Control-Allow-Origin", "*");
if you need cookies you might need this as well:
responseHeaders.add("Access-Control-Allow-Credentials", "true");
in the second case you have to set an origin, '*' will not work in this case
more info here
Angular http uses relative urls.
For example using : www.stackoverflow.com
And
$http.get('/api/search?'
would call : www.stackoverflow.com/api/search
You can tests your rest api using your browser for gets requests, and one of the many plugins for post/put/delete.
(they are jsut made up urls btw)
Found it.
Thanks for all the answers, I was looking for the spring solution.
+1 JohnnyAW for your answer.
In my controller config class I've extended WebMvcConfigurerAdapter and overridden an addResourceHandlers method pointing to my client web app dir:
#Configuration
#EnableWebMvc
#ComponentScan("com.library.service")
public class ControllerConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
Note that it works because configuration is annotated with #EnableWebMvc.

Can Spring "live" alongside other servlets in the same webapp?

I've got the a WEB-INF/web.xml file with a couple of servlets, along with a context
listener which I use to bootstrap the application. I'd like to use Spring in this
web application. What's the best way to work Spring into this so I can use Spring's
injection mechanisms throughout the entire application - even in the servlets which
exists today?
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>Company's XMLRPC service</display-name>
<!-- Servlet Listeners -->
<listener>
<listener-class>com.company.download.context.DefaultServletContextListener</listener-class>
</listener>
<!-- Servlet Declarations -->
<servlet>
<servlet-name>DefaultTrackDownloadServlet</servlet-name>
<servlet-class>com.company.download.web.impl.DefaultTrackDownloadServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>DefaultXmlRpcServlet</servlet-name>
<servlet-class>com.company.download.web.impl.DefaultXmlRpcServlet</servlet-class>
</servlet>
<!-- Servlet Configurations -->
<servlet-mapping>
<servlet-name>DefaultTrackDownloadServlet</servlet-name>
<url-pattern>/track</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>DefaultXmlRpcServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>
Can Spring “live” alongside other servlets in the same webapp?
Yes, Spring MVC is basically just a DispatcherServlet that can make use of a ContextLoaderListener.
These two classes are already setup to interact with one or more ApplicationContext instances and have Spring manage the declared beans.
Your custom Servlet classes are not. If you need to inject a bean into your own Servlet instances, you need to get a reference to the ApplicationContext from the ContextLoaderListener and get the beans you want. There are a few options, whether you do it yourself or use built-in features.
The ContextLoaderListener stores the ApplicationContext it loads into a ServletContext attribute named
org.springframework.web.context.WebApplicationContext.ROOT
So you can retrieve it with that (there's a constant for easy use)
ApplicationContext ac = (ApplicationContext) config.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
Other options exist, see some of them here:
Autowiring in servlet
I want to inject an object in servlet using Spring

How do you make invalid urls ending in .jsp use the spring 404 page?

404 pages are working properly across my site for all url's unless it ends in .jsp. In that case I get the generic jboss 404 page instead of my styled Spring 404 page.
I have the following in my web.xml:
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/error/404.jsp</location>
</error-page>
Edited with full web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>mainServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mainServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml,
/WEB-INF/spring-security.xml
</param-value>
</context-param>
<welcome-file-list>
<welcome-file></welcome-file>
</welcome-file-list>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<error-page>
<error-code>400</error-code>
<location>/WEB-INF/error/400.jsp</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/WEB-INF/error/403.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/error/404.jsp</location>
</error-page>
<error-page>
<error-code>405</error-code>
<location>/WEB-INF/error/405.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/WEB-INF/error/error.jsp</location>
</error-page>
<context-param>
<param-name>defaultHtmlEscape</param-name>
<param-value>true</param-value>
</context-param>
</web-app>
The problem is probably that your web.xml is pointing directly to your jsp pages instead of directing to a spring controller that can then point to the appropriate view.
I'd be curious to also see your rootContext.xml to see how you have your view resolver set up. Cause maybe spring is getting confused... There's only a couple ways that the App Container (JBoss) would end up handling the error, and that is if either the request doesn't even match the Spring context servlet or if there is no error handler for the error type generated (is it possible that the attempt to handle "asdlfasdf.jsp" ends up causing another type of exception that is not getting logged and then is getting tossed up to JBoss and handled as a 404?).
But so you have something to compare to - Here's how I have mine set up:
I have a single error.jsp page in my views directory and set up the view resolver similar to the following:
<!-- Resolves views selected for rendering by #Controllers to .jsp resources in the /WEB-INF/views directory -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
Then, I have a base PageController class that I extend for all my other "user facing" controller classes. That class has a method that handles the "/error/404", "/error/403" style error page patterns:
#RequestMapping(value = "/error/{code}")
public String errorPage(Principal principal, Model model, #PathVariable("code") int code) {
model.addAttribute("code", code);
return "error";
}
Then, I set up the web.xml similar to yours, only redirecting to the spring controller endpoints along with the appropriate code:
<error-page>
<error-code>404</error-code>
<location>/error/404</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/error/403</location>
</error-page>
Now, I don't know if this is the best way, but it works really well for me. The web container routes those error types to the /error/code page which is just calling a controller method and then the controller method produces the error.jsp page (by returning the view name "error").
Since it puts the error code in the model, I have a single jsp that handles multiple error types, I just put a conditional that renders a different user message depending on the error code. In other words, "Page Not Found :(" vs "You aren't authorized to view this page!"
And then I do this in my spring security config to make sure an unauthenticated user can see the error page (you've probably got this part already since you can see the jsp pages):
<http pattern="/error/**" security="none" />
I combine this with the annotation style exception handlers to do all my server exception handling (handling data requests with a json error response and html/view requests with html error pages) because you can just annotate a controller method to handle Exception or RuntimeException. I'd recommend you check out this method as well because it is very powerful and makes it dramatically easier to log and handle exceptions gracefully:
#ExceptionHandler(Exception.class)
public ModelAndView exceptionHandler(Exception e){
ModelAndView modelAndView = new ModelAndView();
modelAndView.getModelMap().addAttribute("code", HttpStatus.INTERNAL_SERVER_ERROR);
modelAndView.setViewName("error");
logger.error("Error handling page request.", e);
return modelAndView;
}
This is also a good way to make sure you have a chance to log exceptions that go through your controllers without having to put exception handling into every single one.
I made two base classes... one for my page controllers and one for my rest data controllers. The base page controller return the jsp view that renders a nice error page for the user. The base rest data controller returns a json message with the error information in the model and then the jsp will print the full stack or just a generic error depending on if the server is running in development or production mode.

How to have spring security context in child context

I'm trying to have spring security context in child context, so I could have url security on the servlet context file.
I have:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/spring-security.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>myapp-soap</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
on web.xml, general security configuration on spring-security.xml and
<!-- Authorization configurations -->
<security:http auto-config="false" use-expressions="true"
create-session="never"
authentication-manager-ref="authenticationManager"
entry-point-ref="authenticationEntryPoint">
<security:custom-filter
position="PRE_AUTH_FILTER" ref="serviceAuthenticationFilter"/>
<security:intercept-url
pattern="/GetForbiddenUrl" access="hasRole('roleThatDoesntExist')" />
<security:intercept-url pattern="/**" access="permitAll" />
</security:http>
<!-- annotation security -->
<security:global-method-security pre-post-annotations="enabled"/>
on the myapp-soap-servlet.xml. It doesn't work but fails with
ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/my-app/v1/soap]] (ServerService Thread Pool -- 192) JBWEB000284: Exception starting filter springSecurityFilterChain:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' is defined
However, if I move <security:http> part to spring-security root context configuration, everything works. Shouldn't it work the way I try? How can I get url-based security in my child context?
I also tried combining the context files into one, but the same problem seems to occur.
The DelegatingFilterProxy will by default look in the root ApplicationContext which means by default you need to place your <http> configuration there (it is what creates the springSecurityFilterChain).
However, you can specify use that DelegatingFilterProxy a different ApplicationContext by specifying the contextAttribute for it to use. To do this update your web.xml as shown below
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.myapp-soap</param-value>
</init-param>
</filter>
A similar example using Spring Security 3.2+'s AbstractSecurityWebApplicationInitializer can be seen below:
public class SecurityApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
#Override
protected String getDispatcherWebApplicationContextSuffix() {
// NOTE: if you are using AbstractDispatcherServletInitializer or
// AbstractAnnotationConfigDispatcherServletInitializer You probably
// want this value to be "dispatcher"
return "myapp-soap";
}
}
This works because it modifies the name of the ServletContext attribute that DelegatingFilterProxy uses to lookup the ApplicationContext. Instead of using the default value which discovers the root ApplicationContext it now uses the attribute that your MessageDispatcherServlet is using (thus pointing to the child context).
Note that MessageDispatcherServlet's (or any subclass of FrameworkServlet such as DispatcherServlet) stores the ApplicationContext in the ServletContext using the attribute name "org.springframework.web.servlet.FrameworkServlet.CONTEXT." + <servlet-name> where <servlet-name> is the name of the servlet. So in this instance, the attribute that must be configured is org.springframework.web.servlet.FrameworkServlet.CONTEXT.myapp-soap. If you changed the servlet-name from myapp-soap to spring-servlet, then you would use org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring-servlet instead.
PS I think the subject should read "How to have spring security context as child context"
<security:http> will have to go to the main application context instead of the child (servlet) context, because this security namespace element creates the springSecurityFilterChain bean, which is looked up by DelegatingFilterProxy in the main context. As its javadoc clearly states:
web.xml will usually contain a DelegatingFilterProxy definition, with the specified filter-name corresponding to a bean name in Spring's root application context.

Resources