I'm exploring the new features in JSF 2.2 (pretty cool so far) but I still don't understand how Protected Views works, I created a facelet1 with a link to facelet2, like this:
<h:link styleClass="link" value="Go to protected page" id="link1"
outcome="/protected/facelet2.xhtml"></h:link>
and in my faces-config.xml I added this:
<protected-views>
<url-pattern>/protected/facelet2.xhtml</url-pattern>
</protected-views>
Now when I run the page a token is added in the url:
http://localhost:8080/<project>/protected/facelet2.faces?javax.faces.Token=1426608965211
According to the documentation, if the token does not match with the one in the server, the GET request is not processed (is my understanding correct?).
But if I modify the token (using Firebug or the dev tools included in the browser) the request is still processed, even if the token was modified.
Am I doing something wrong?
This is caused because your FacesServlet is apparently mapped on JSF 1.0-style URL pattern of *.faces instead of JSF 2.0-style URL pattern of *.xhtml. The <protected-views><url-pattern> must match the actual URL pattern as you see in browser's address bar.
So, given an actual URL of /protected/facelet2.faces, you need to configure it as below:
<protected-views>
<url-pattern>/protected/facelet2.faces</url-pattern>
</protected-views>
However, while at it, I discovered some nasty issues in current Mojarra 2.2.10 implementation:
It does actually not do a prefix/suffix match as per Servlet 12.1 specification (there's even a vague comment in source code indicating that!). It does merely an exact match. This means, you can't use wildcard URL patterns like /protected/*.
When generating the <h:link>, it does not compare the protected view URL pattern to the resolved URL, but to the JSF view ID. And, when checking an incoming request, it does not compare the request URL to the JSF view ID (like as during link generation), but to the <url-pattern>. This thus never matches, totally explaining why you could simply access it without a valid token.
Basically, you need the following configuration if you need to keep the JSF 1.0-style URL pattern of *.faces.
<protected-views>
<url-pattern>/protected/facelet2.xhtml</url-pattern>
<url-pattern>/protected/facelet2.faces</url-pattern>
</protected-views>
It'll then properly throw javax.faces.application.ProtectedViewException when being accessed without a valid token. Much better is to just map the FacesServlet explicitly on *.xhtml in web.xml.
<servlet-mapping>
<servlet-name>facesServlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
This way you never need to deal with virtual URLs.
I've reported this to Mojarra guys as issue 3837.
See also:
Sometimes I see JSF URL is *.jsf, sometimes *.xhtml and sometimes /faces/*. Why?
Related
I tried to upgrade my Java EE 7 / JSF 2.2 application to Omnifaces 2.6. Currently I am running version 2.4.
After that, I have noticed a strange behavior when using #ConversationScoped and Ajax-Requests. When calling, the area, which should get rendered after the request, gets cleared (no exception on the server, response status code 200).
Next, I have a kind of wizard implementation, based on #ConversationScoped. It holds a class called ViewManager which has itself a List of Views. Initializing works fine and this list gets filled. But somehow it gets cleared (set to null) when the first form/view gets submitted. The setter for this is never called after the initialization, so it is not changed by my code. Somehow the view manager instance is still available, only this list of view within the view manager is null, which is kind of strange.
With omnifaces 2.4, everything did work fine (this is why I did not add some code of my wizard). I checked the changelog and noticed the MultiViews configuration when using ExtensionlessURLs. Don't know why this could effect my problem, but i tried it...with no success.
I have no idea what could be the problem, so maybe you can help me.
Thanks in advance :)
In OmniFaces, the FacesViews extensionless URLs feature got in version 2.5 an overhaul in order to support the so-called MultiViews as you can read on this blog.
During this overhaul I made a backwards compatibility mistake in the FacesViewsViewHandler where the <h:form> action URL is being manipulated in order to include the virtual folders of the MultiViews feature. The query string parameters were dropped from the original action URL and not added back.
The #ConversationScoped relies on the cid request parameter being present in the <h:form> action URL as in /context/page?cid=1. This thus became /context/page and therefore the conversation isn't retained across postbacks.
I will fix this in next OmniFaces release, for now you can get back the desired behavior by adding the below context parameter to web.xml.
<context-param>
<!-- Workaround for disappearing #ConversationScoped ?cid= parameter -->
<!-- This can be removed in next OmniFaces version after 2.6 -->
<param-name>org.omnifaces.FACES_VIEWS_VIEW_HANDLER_MODE</param-name>
<param-value>BUILD_WITH_PARENT_QUERY_PARAMETERS</param-value>
</context-param>
This parameter triggers a different way of building the URL whereby the entire query string from the original action URL is explicitly retained.
In the source of the class AjaxBehaviorRenderer (line 260) there is a line that apparently appends the NamingContainer Id to the list of optional parameters of mojarra.ab(...). I've never come across it so I'm curious as to when it is used:
RenderKitUtils.appendProperty(ajaxCommand, "com.sun.faces.namingContainerId", namingContainerId, true);
line 260
While working on spec issue 790 last week, which should solve a.o. Rendering other form by ajax causes its view state to be lost, how do I add this back?, this was explained to me by Neil Griffin, a portlet guy.
It appears that portlets can have multiple JSF views rendering to the same HTML document, each with its own view state. In portlets, there's a special UIViewRoot instance which implements NamingContainer. During regular rendering, all forms, inputs and commands will have IDs and names prefixed with the view's own client ID. This will work fine during synchronous postbacks. The portlet can this way identify the exact view to restore.
However, during asynchronous postbacks, the jsf.js will create a bunch of additional ajax-specific request parameters such as javax.faces.source, javax.faces.partial.event, etc. Those request parameter names are not prefixed with the view's own client ID. Therefore the portlet cannot associate them with a specific view. Hence the impl issue 3031.
There was another problem of view state identifiers in ajax responses not being properly namespaced this way. Therefore the portlet implementation had to customize the partial response writer in the so-called "JSF bridge". This will be taken into account during implementing spec issue 790. Instead of sniffing a "portlet environment" as in current implementation, there will be checks on UIViewRoot instanceof NamingContainer which is more flexible and portlet-independent. The Mojarra-specific com.sun.faces.namingContainerId will also be removed. Instead, this value will be rendered to <partial-response id="..."> so that the jsf.js can just extract from there.
All in all, not really important if you're only targeting servlet based environments.
As per balusC comment :
It's only interesting for portlet based apps (not servlet based apps).
I can't exactly explain why and what it is used for (a portlet/liferay
guy might), but the portlet specific feature is called "namespaced
parameters". See https://web.liferay.com/web/meera.success/blog/-/blogs/liferay-requires-name-spaced-parameters
I am having trouble navigating between my JSF pages. Most of my navigation happens when you click a command button. The action of the command button returns a string.
My log in page is my welcome page. Here it is in my web.xml:
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>faces/pages/index.xhtml</welcome-file>
</welcome-file-list>
In my browser's address bar, the page appears as:
http://localhost:8080/ui/faces/pages/index.xhtml
Once authentication happens, the function returns this String:
"/ui/faces/pages/home.xhtml"
The file I want to navigate to is located at:
pages/home.xhtml
However when navigation is supposed to happen, I get this error:
Unable to find matching navigation case with from-view-id '/pages/index.xhtml' for action '#{indexPageController.login()}' with outcome '/ui/faces/pages/home.xhtml'
Can anyone help me understand the relative path I need to correctly navigate to the page?
You should not include the context path /ui nor the FacesServlet mapping /faces in the navigation case outcome. It should just represent the sole view ID, which is basically just the path to the physical view file absolute to the webcontent root or relative to the current view ID.
So, absolute (starting with /) to the webcontent root:
/pages/home.xhtml
Or relative (not starting with /) to the current view ID (assuming that you're in /pages/index.xhtml):
home.xhtml
Do note that dot-slash ./ and double-dot-slash ../ notations are not supported.
Or even without file extension; JSF will imply the Facelets default suffix which defaults to .xhtml and is configureable by javax.faces.DEFAULT_SUFFIX context parameter:
/pages/home
home
It makes after all also sense if you realize that the context path /ui and the FacesServlet mapping /faces/* are not controllable from inside the webapp on! If they ever change externally, then you'd theoretically need to change all navigation case outcomes in the entire codebase and rebuild the webapp. This would not make any sense. JSF takes thus already care of them for you.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
Difference between h:button and h:commandButton
When should I use h:outputLink instead of h:commandLink?
What URL to use to link / navigate to other JSF pages
I am having trouble navigating between my JSF pages. Most of my navigation happens when you click a command button. The action of the command button returns a string.
My log in page is my welcome page. Here it is in my web.xml:
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>faces/pages/index.xhtml</welcome-file>
</welcome-file-list>
In my browser's address bar, the page appears as:
http://localhost:8080/ui/faces/pages/index.xhtml
Once authentication happens, the function returns this String:
"/ui/faces/pages/home.xhtml"
The file I want to navigate to is located at:
pages/home.xhtml
However when navigation is supposed to happen, I get this error:
Unable to find matching navigation case with from-view-id '/pages/index.xhtml' for action '#{indexPageController.login()}' with outcome '/ui/faces/pages/home.xhtml'
Can anyone help me understand the relative path I need to correctly navigate to the page?
You should not include the context path /ui nor the FacesServlet mapping /faces in the navigation case outcome. It should just represent the sole view ID, which is basically just the path to the physical view file absolute to the webcontent root or relative to the current view ID.
So, absolute (starting with /) to the webcontent root:
/pages/home.xhtml
Or relative (not starting with /) to the current view ID (assuming that you're in /pages/index.xhtml):
home.xhtml
Do note that dot-slash ./ and double-dot-slash ../ notations are not supported.
Or even without file extension; JSF will imply the Facelets default suffix which defaults to .xhtml and is configureable by javax.faces.DEFAULT_SUFFIX context parameter:
/pages/home
home
It makes after all also sense if you realize that the context path /ui and the FacesServlet mapping /faces/* are not controllable from inside the webapp on! If they ever change externally, then you'd theoretically need to change all navigation case outcomes in the entire codebase and rebuild the webapp. This would not make any sense. JSF takes thus already care of them for you.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
Difference between h:button and h:commandButton
When should I use h:outputLink instead of h:commandLink?
What URL to use to link / navigate to other JSF pages
In this beginner's JSF tuorial
section 1.1 says:
JSF UI components and their state are represented on the server with a defined life-cycle of the UI components.
But in the example that follows, I am unable to see how the state of an UI component is managed by the server? The example looks like a standard servlet jsp example minus the servlet mappings.
My other question is that in the example, we are accessing the jsp directly. Is this the standard thing to do in JSF as opposed to using servlet mappings?
First of all, if you're a beginner, I encourage you not to look at that old tutorials and find a good JSF 2.x one. JSF 2 was released in 2009 and you should consider it as the branch to learn, as it brings several advantages comparing with 1.x old versions.
JSF has its own lifecycle for any request you make from the browser which can be a GET or POST request, even an ajax based one. What you basically have to understand about JSF comparing with other frameworks is that it's stateful. In other words, you can keep a component's state from one request to another (you actually have a view state, which can be kept no matter how many requests you do, until you change the view).
Appart from that, about your last statement, in old JSF ages the servlet mapping used to be done over .*jsf suffix. It means, when you make a request for that in the browser, jsf will convert the matching jsp page and display it.
JSF 2 however introduced facelets, which are based in .xhtml view pages. It's now also possible to do the mapping as .xhtml having the source code in an .xhtml too and JSF will make the conversion. The main advantage for this is that end user will not be allowed to see the sources, as browser's request matches source page's url, so JSF servlet will always be invoked.