Using FORM authentication, I created a security constraint with a web resource collection that has a url-patter of /web/*
However, I can't use this pattern with a "jsp:forward", according to the documentation I'm reading. If I try to navigate to http://hostname/sample I get the welcome.jsp page without Tomcat ever showing me the login form, which makes sense since the security pattern doesn't work with "jsp:forward".
In JSF, I need the "jsp:forward", don't I? In the web resource collection I tried explicity listing the index.jsp, but Tomcat still didn't show the login form.
If I put all jsp under the sample directory, I get the login form, authenticate and then see welcome.jsp. Am I missing something?
I'm suffering from Morning Confusion again. Meaning I think I've an idea what you're getting at, but I'm too thick-witted to be sure.
So I'll work backwards and hope it helps. Forgive me if I repeat things you already know. Not everyone here may.
First of all, there's the constraint that a login page cannot be JSF. That's because container-managed security authenticator (j_security_check) doesn't route a normal web request, if routes a "fast-track" internal request. So login pages need to be able to operate without controller servlets, including the FacesServlet.
Secondly, the container URL filter is exactly that. If looks at the original page request URL to determine what roles are permitted for that URL. Notice I didn't say anything about the page. It's the URL that's vetted, not the actual content request. That's why you need a <redirect/;> navigation element in faces-config (or, for JSF2, you can also declare via annotation).
Thirdly, (and I think this is your actual problem), the welcome page request, like login requests are "fast-path" dispatched. I think, in particular that this may be interfering with the URL screening for container-managed security, although there are reasons why I hope not.
Normally, my welcome page itself is public. It may say something like "warning: trespassers beyond this point will be shot", but I still prefer that anyone can tell when they've reached my site and whom to contact if they need admittance.
I don't know whether any of this helps at all, but I figured it probably can't hurt.
An IDE is no substitute for an Intelligent Developer.
Joined: Apr 17, 2003
First of all, there's the constraint that a login page cannot be JSF.
Correct. My login.jsp is just a jsp page. It has no JSF components. It uses the magical j_security_check action and j_username and j_password input elements. I was reading an older servlet book that said form based auth fails when using a jsp:forward, which I am unfortunately to get the Faces servlet to work.
I believe you are correct in that my problem lies in the welcome page request. However. Reading the servlet 2.4 spec, Security, SRV 12.5.3, the process is described as:
1. When a user attempts to access a protected web resource (my welcome.jsp - which really starts with index.jsp), the container checks the user's authentication. If the authentication works, then the client is redirected to the resource using the stored URL path.
Just from a superficial reading, I think that this is, in fact, a typo. A protected resource is a protected resource no matter if it's the first request of the 5-millionth.
What can be confusing when you're into redirection, however, is the fact that there are multiple redirection techniques. If you use one that's entirely internal to the appserver you generally won't get further security checks. However, anything that goes out to the client and comes back in requires authorization testing (based on the come-back-in URL), if for no other reason than that external requests can be hacked.
One thing that confuses people - and doubly so with JSF - is that the role-checking is applied to the URL, not to the resource location. But I think I'm repeating myself here.
Joined: Apr 17, 2003
Regardless of the info on the in the Java EE 6 tutorial. I believe I have figured this out.
My problem: reading old reference material that was confusing me.
Normally, when you create a JSF page, your web.xml (aka deployment descriptor) maps the "Faces Servlet" to a url pattern of: /faces/*
I was not doing this. Instead, the reference material I was reading suggested that you could use a different pattern to match directly to the .jsf file by using: *.jsf
If you chose to go this route, the ref suggested you would need a second jsp page, as the welcome-file-list, that forwards the request to the actual .jsp you wanted. And according to yet a second old reference book, using a jsp:forward does not work with a security contraint url pattern of /*
The way I fixed this was to go back to using the JSF /faces/* url pattern.
Now, when I navigate to http://servername/sample - Tomcat now finds the login form and displays it so that I can authenticate.
I don't use the '/faces/' mapping myself. I've pretty much always used the "*.jsf" one. Then again, I'm probably more tuned into the nuances. Plus, since I'm using Facelets these days, the only actual JSP files I use are "real" JSP files, since the facelets templates are all "*.xhtml" files.
My standard architecture also finds it convenient to group secured functions under a logical subdirectory related to the role(s). For example: "http://www.mousetech.com/monsterapp/admin/editUser.jsf" (not a working URL, in case you click on it). So I have security mappings for things like "/admin/*".
Joined: Apr 17, 2003
I feel like I just slipped a rung on my already lagging ladder of current technology.
JSF 1.2 vs Facelets 1.1.14 - I just downloaded Facelets and I see what you mean about the Faces Servlet Mapping, it's *.jsf
That must just be the way JSF 1.2 handled the Faces Servlet mapping, to use the /faces/* pattern. I need to read up on this more.
Both mappings work and always have. There's nothing new there.
The mechanism is as follows: First, you set up one or more servlet mappings in web.xml. These are wild-card patterns that are compared against each incoming URL (internal requests like login don't count). If the incoming URL matches (one of the) pattern(s) then the container will route that URL to the servlet whose logical name has been associated with that URL. In this case, it's the FacesServlet.
FacesServlet dissects the URL to help it find the resources it needs to render a JSF view. There's a little extra baggage attached when using Facelets, but the same net effect. Those resources are located based on the remnant of the URL that's still there after removing the front-end part of the URL (protocol, hostname, port, and context name), and for URLs ending with "jsf", converting what's left from "jsf" to "jsp" (or xhtml for Facelets) and using that to synthesize a WAR resource path. So the security framework doesn't get into the application code, which includes the Faces Servlet. Security is all done before routing the URL to the application.
My login pages are bog-standard JSPs. Like I said, the dispatcher for "fast-path" requests doesn't work for servlets, so there's no way to do a JSF-based login page, since the essential part of JSF (the Faces Servlet) can't be called. Struts has a similar problem, incidentally, since they also use a dispatcher servlet.
Joined: Apr 17, 2003
I think I'm misunderstanding you Tim.
My login page is also regular JSP. The page that I'm requesting, which is governed by a security constraint, is a JSF page (1.2) called welcome.jsp
When I use the JSF 1.2 framework, and try to map a url-pattern to *.jsf, inside a restricted web-resource-collection, Tomcat will serve the welcome.jsp page, without ever showing the login page. And I believe that has to do with the fact that index.jsp does a jsp:foward to welcome.jsp. Unless I use the /faces/* url-pattern and skip the index.jsp that does the forward, Tomcat won't show the login page.
I just downloaded the Facelets jars, plugged them into NetBeans, generated a sample webapp.
I noticed, in NetBeans, that Facelets uses a servlet mapping url-pattern of *.jsf. The sample webapp NetBeans generates using the Facelets framework also generates a "forward.jsp" page that forwards requests on to "template-client.jsf".
No, it's not so much that you misunderstand me as I misunderstand you. I'm afraid I'm taking the lazy way out and tossing out answers based on what I think is going on rather than actually looking at the details. Unfortunately, I've got a lot of work backed up so I can't spare the effort.
Use of the "/faces/" versus "*.jsf" patterns shouldn't be making a difference. All they do is select the URL for processing by the FacesServlet. Which, as far as it goes, isn't going to matter for login, loginfail or welcome pages, since the container itself routes directly to those pages without applying the filters or routing. Whether a forward will direct traffic towards the filters, and thus the FacesServlet depends on whether the forwarding technique in use is working purely internally or not.
I actually don't have your problem, since my welcome page is almost always a public page and doesn't forward. It may contain links to JSF pages, which may or may not be secured, but I'm not actually trying to redirect a welcome to a protected page.
Joined: Apr 17, 2003
When you get a chance, can you recommend a good reference for the things you mentioned regarding webapp structure, such as not using a JSF page as a welcome-file page, etc?
I have a solution to my problem - see *solution*, although I'm not sure the design is what is generally put into practice.
My original goal: Requesting a JSF page that is in a webapp where the entire webapp directory is protected by a security constraint.
A Sun JSF 1.2 page requires a separate index.jsp that does a jsp:forward to the page I want and I was using that index.jsp for the welcome-file-list welcome-file in web.xml. That was what was causing my problem with respect to the login form being bypassed. I originally fixed that problem by sticking to using the "faces" servlet prefix in my url-patterns. However, then I had problems with my JSF page not reading my css files because when the JSF page first loads, the url is still http://servername/webapp The css didn't know about the faces prefix in the url, yet. I tried using local and relative paths to the css but that wasn't working very well due to the fact that some of the JSF components post back to the same page, then you see the full url.
I kept the *.jsf mapping and added a second jsp called redirect.jsp that redirects to welcome.jsp.
So now the flow is: forward.jsp --> redirect.jsp --> welcome.jsp
This solution helped the css problem because now the complete url appears in the browser on the first call to welcome.jsp This doesn't seem like the right approach though. It seems like a hack.
The one thing I did change was in the DEFAULT_SUFFIX context-param. By default, NetBeans uses .jsf for a param value, I changed it to .jsp
I'm afraid I don't have a good reference, other than scar tissue. I learned it the hard way, and deduced a lot of it based on what did and didn't work. I'm still not 100% certain on some of these items, just certain enough that they work.
You did let slip one critical error in your thinking, however. As I've said before, J2EE's container-managed security applies to URLs. You can't "secure a directory". J2EE doesn't support this concept. That's a key difference between J2EE, which is based primarily on the idea that URLs will be used to dispatch and control processes and Apache httpd, which, by default serves up files whose names and locations are computed from the URLs. There's no J2EE equivalent to the Apache ".htaccess" facility.
While I have an "admin" directory in many of my webapps, which contains the templates for views related to administration, that's merely a convenience to accomodate JSF's resource resolver. The critical feature from my point of view is that if a function is filed under an "admin" URL, that URL is available only to administrators.
This is where internally-dispatched resource requests are different. They are not fetched directly by URL. In fact, the welcome page is more accurately fetched by the lack of a(n explicit) URL. And even if login form requests weren't generated internal to the appserver, applying a security URL pattern to the login pages would lead to chicken-and-egg issues (you can't login, only logged-in users can log in!).