What is the relation between session scoped beans and HttpServletRequest? - jsf

While trying to figure out how to implement a login filter for a JSF app I saw these 2 lines of code that I didn't understand that much :
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
LoginBean login = (LoginBean) req.getSession().getAttribute("login");
}
Assuming that LoginBean class is a session scoped bean named "login" , as I noticed the bean is an attribute for the request , what is the relation between them ? are all session scoped bean saved as "attributes" in request sessions ?

are all session scoped bean saved as "attributes" in request sessions ?
That's correct. JSF is just a MVC framework which is built on top of the bare Servlet API, not an entirely standalone framework which can run without the Servlet API. Even more, the JSF core controller FacesServlet is a fullworthy Servlet, so it definitely requires a servlet container to run. The concept "session" is in the Servlet API provided by HttpSession, so it would make fully sense to store JSF session scoped beans in there instead of reinventing it.
Note that JSF request scoped beans are stored as HttpServletRequest attributes and that JSF application scoped beans are stored as ServletContext attributes.
See also:
Get JSF managed bean by name in any Servlet related class
How do servlets work? Instantiation, sessions, shared variables and multithreading

Related

JSF 1.2 get a request scope managed bean in a regular servlet

How can I get a managed bean in a pleain servlet with JSF 1.2?
I tried several solutions like those proposed here JSF Managed Beans in a Servlet but without success.
I send an XMLHttpRequest from a facelet where the managed bean is well instanciated, but when I tried this:
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Bean bean = (Bean) req.getAttribute("bean");//null
Bean bean = (Bean) getServletContext().getAttribute("bean");//null
}
Is there a way on JSF 1.2?
Thanks a lot

Access session scoped JSF managed bean in web filter

I have SessionScoped bean called userSession to keep track of the user ( username, ifLogged, etc). I want to filter some pages and therefore I need to access the bean from the webFilter I created. How do I do that? I looks like its even impossible to import the bean to be potenitally visible.
Under the covers, JSF stores session scoped managed beans as an attribute of the HttpSession with the managed bean name as key.
So, provided that you've a #ManagedBean #SessionScoped public class User {}, just this should do inside the doFilter() method:
HttpSession session = ((HttpServletRequest) request).getSession(false);
User user = (session != null) ? (User) session.getAttribute("user") : null;
if (user != null && user.isLoggedIn()) {
// Logged in.
}
Or, if you're actually using CDI instead of JSF to manage beans, then just use #Inject directly in the filter.
See also:
Get JSF managed bean by name in any Servlet related class
Prevent accessing restricted page without login in Jsf2
As an alternative you can use CDI-beans and inject your sessionbean normally.

Access ViewScoped ManagedBean from Servlet

Background information: I have a file upload applet in my jsf page. This applet expects an adress where it can send it's POST request. (I can't edit this post request to add more fields or something). The post method of my servlet then stores the file. This job can't be done by a managed bean because the servlet has to be annotated with #MultiPartConfig and I can't add this annotation to the jsf managed bean. In order to force the upload applet to use the same session I added an URL attribute named jsessionId to the post request according to this post. In my servlet I am now able to access session scoped beans.
Now I have a ViewScoped bean where I store some form input data which I want to use in the servlet, since adding those inputs to the post request doesn't work (Applet is a third party project (JUploadApplet) and for some reason it doesn't work to add additional form data).
Now is it possible to access the ViewScoped bean from within the servlet ? If I change the scope into SessionScope I am able to process the input but with ViewScoped I get a NullPointerException if I try to access the bean like this :
UploadBean uploadBean = (UploadBean)request.getSession().getAttribute("uploadBean");
This is not possible. Your best bet is to let the view scoped bean generate an unique key, store itself in the session scope by that key and pass that key as additional parameter to the applet and finally let the servlet access the session attribute by that key.
E.g.
private String sessionKey;
#PostConstruct
public void init() {
sessionKey = UUID.randomUUID().toString();
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(sessionKey, this);
}
#PreDestroy
public void destroy() {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(sessionKey);
}
Let the applet pass the sessionKey as request parameter to the servlet, so that the servlet can do
String sessionKey = request.getParameter("sessionKey");
Bean bean = (Bean) request.getSession().getAttribute(sessionKey);
// ...
Note that instead of the bean itself, you can also just store an arbitrary bean/valueobject/etc.

How could I read a JSF session bean from a filter?

I'm searching but I can't find the answer, I need secure resources based on permissions, I can't use a filter because FacesContext is not initialized before and I need load the permissions in my session bean. Some solution avoiding use a filter? PhaseListener, ViewHandler and ResourceHandler can't capture an URL resource request, for example I need denied this direct access: http://127.0.0.1:8080/test/resources/images/image.jpg
Thx in advance...
JSF stores session scoped managed beans as an attribute of the HttpSession, which in turn is just available in a Filter by HttpServletRequest#getSession().
HttpSession session = ((HttpServletRequest) request).getSession();
SessionBean sessionBean = session.getAttribute("sessionBean");
// ...
Update: as per the comment you seem to be actually using CDI:
my filter is triggered before than JSF, I always get a null value when I use getAttribute. I'm using CDI with 'Named' and 'SessionScoped' annotations on my Bean because I need use a interceptor to implement security
I understood that you were using JSF's own #ManagedBean and the initial answer only applies to that. If your bean is already managed by CDI's #Named, then just use CDI's own #Inject the usual way in the Filter.
#Inject
private SessionBean sessionBean;
In case of JSF #ManagedBean you should just add a if (sessionBean != null) check. It's irrelevant whether the filter is invoked before JSF servlet or not. Once the session bean has been created by JSF, it won't be null in the filter.

Is there any easy way to preprocess and redirect GET requests?

I'm looking for a best practise answer. I want to do some preprocessing for GET requests. So e.g. if the user is not allowed to see the page, redirect him to another page. But I don't want to use normal servlet filter, because I would like to express this behavior in the faces-config.xml. Is this possible and how is that called, how can it be done?
Can I define some Filter bean that also returns a String telling the faces-config.xml where to go next?
I googled for this but only hit on the normal filters. If I use filters, can a #WebFilter be a #ManagedBean at the same time? Or is that bad style?
If you're homegrowing HTTP request authentication on top of JSF, then a servlet filter is really the best approach. JSF is "just" a MVC framework and nothing in the JSF API is been specified to filter incoming HTTP requests to check user authentication. On normal GET requests, a JSF managed bean is usually only constructed when the HTTP response is about to be created and sent, or maybe already is been committed. This is not controllable from inside the managed bean. If the response is already been committed, you would not be able anymore to change (redirect) it. Authentication and changing the request/response really needs to be done far before the response is about to be sent.
If you were not homegrowing authentication, then you could have used the Java EE provided container managed authentication for this which is to be declared by <security-constraint> entries in web.xml. Note that this is also decoupled from JSF, but it at least saves you from homegrowing a servlet filter and a managed bean.
The general approach is to group the restricted pages behind a certain URL pattern like /app/*, /private/*, /secured/*, etc and to take the advantage of the fact that JSF stores session scoped beans as HttpSession attributes. Imagine that you've a JSF session scoped managed bean UserManager which holds the logged-in user, then you could check for it as follows:
#WebFilter(urlPatterns={"/app/*"})
public class AuthenticationFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
UserManager userManager = (session != null) ? (UserManager) session.getAttribute("userManager") : null;
if (userManager == null || !userManager.isLoggedIn()) {
response.sendRedirect(request.getContextPath() + "/login.xhtml"); // No logged-in user found, so redirect to login page.
} else {
chain.doFilter(req, res); // Logged-in user found, so just continue request.
}
}
// ...
}
If you're using JSF 2.2+, there's another way to control the response right before it is been sent. You can make use of the <f:viewAction>. Put the following somewhere in your view:
<f:metadata>
<f:viewAction action="#{authenticator.check}" />
</f:metadata>
with
#Named
#RequestScoped // Scope doesn't matter actually. The listener will always be called on every request.
public class Authenticator {
public String check() {
if (authenticated) {
return null;
}
else {
return "login?faces-redirect=true";
}
}
// ...
}
This is guaranteed to be fired before the response is to be rendered. Otherwise when you do the job in e.g. #PostConstruct, then you may risk java.lang.IllegalStateException: response already committed when the bean is created for the first time when the response has already partially been rendered (and committed).
I only wouldn't consider it to be a "best" practice when it comes to handling HTTP authentication. It makes it too tight coupled into JSF. You should really keep using a servlet filter. But for other purposes, it may be fine.
See also:
When to use f:viewAction / preRenderView versus PostConstruct?
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Limitations of using a PhaseListener instead of a Servlet Filter for authorization

Resources