I have JSF web app project, I need to catch only incoming specific url pattern and do something in application scope bean. What is the best approach? I don't want to change existing functionality of webapp.
You can register a filter that applies to an specific url pattern. And from the doFilter method call your application scoped bean.
Something like this:
#WebFilter("/yourpattern")
public class MyFilter extends HttpFilter {
#Inject
MyApplicationScopedBean myApplicationScopedBean;
#Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
myApplicationScopedBean.doSomeThing();
super.doFilter(request, response, chain);
}
}
Related
How can i retrieve the bookmarkable URL, respecting rewritten rules using ViewHandler.getBookmarkableURL(...) inside a #WebFilter to redirect the user to the rewritten login page URL.
Is there an alternate function to get a bookmarkable URL without FacesContext?
Simplified example
The page /sites/user/login.xhtml is rewritten to just /Login using OCPSoft rewrite library but in following filter i dont know how to do this since i dont have access to FacesContext.
The real example has additional querystring params that also contribute to the rewritten URL
#WebFilter(filterName = "UserFilter", urlPatterns =
{
"/sites/user/account.xhtml"
}, dispatcherTypes =
{
DispatcherType.FORWARD, DispatcherType.REQUEST, DispatcherType.ERROR
})
public class UserFilter extends HttpFilter
{
#Override
public void doFilter(final HttpServletRequest request, final HttpServletResponse response,
final HttpSession session, final FilterChain chain) throws ServletException, IOException
{
if (isLoggedIn())
chain.doFilter(request, response);
else
response.sendRedirect(request.getContextPath() + "/sites/user/login.xhtml");
}
}
This is a follow up to a previous question: Error using JSF protected views when opening a new tab
I am using faces-config protected views to protect against CSRF. I ran into problems earlier with links opened in a new tab that I resolved by adding
rel="noopener noreferrer"
to all new tab links.
But now I' running into the same issue with commandButtons.
I have
<p:commandButton value="Submit"
action="#{bean.submit}" />
And submit() returns a string with the new view. But I still get the following error:
javax.faces.application.ProtectedViewException: JSF1099: Referer [sic] header value http://.../updatestatus.xhtml?javax.faces.Token=1534516398157&cr=45309 does not appear to be a protected view. Preventing display of viewId /finance/commitmentregister/view.xhtml
at com.sun.faces.lifecycle.RestoreViewPhase.maybeTakeProtectedViewAction(Unknown Source)
Is there a way to set no referer or opener on the Primefaces commandbutton?
Edit:
Maybe not the answer I was looking for, but I got around the problem by adding a servlet request wrapper in a filter to return null when asked for the referer.
Further edit:
Adding the rough outline of how my code looks with the wrapper fix:
#WebFilter(filterName = "UserLoginFilter", urlPatterns = { "*.xhtml" })
public class UserLoginFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(httpServletRequest) {
#Override
public String getHeader(String name) {
if (name.equalsIgnoreCase("referer")) {
return null;
} else {
return super.getHeader(name);
}
}
};
chain.doFilter(wrapper, response);
}
}
A week ago, I have studied about ViewExpiredException, and I've read several things about it.
viewExpiredException JSF
How to control web page caching, across all browsers?
Session timeout and ViewExpiredException handling on JSF/PrimeFaces ajax request
My problem, which is some cases, I would like to ignore the ViewExpiredException. These are situations that do not need a "session", and my Beans are #RequestScoped. As an example, the pages login.xhtml, register.xhtml and passwordRecovery.xhtml.
In these cases, it is very strange display an error to the user saying that your session has expired. So if you open the login page and stand still for a while, when he inform your data and click Login, it would be forwarded to an error page. I would just ignore it and let transparent to the user.
So, my solution so far is create a ExceptionHandler to ignore these exceptions:
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
// just remove the exception from queue
if (t instanceof ViewExpiredException) {
i.remove();
}
}
getWrapped().handle();
}
Then, I created a filter to check whether the user is logged in, if not then redirected to login page (This filter applies only pages that require authentication):
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!loginManagedBean.isLogged()) {
String pathLogin = request.getContextPath() + "/" + LOGIN_VIEW;
if (isAJAXRequest(request)) {
response.setContentType("text/xml");
response.getWriter()
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.printf("<partial-response><redirect url=\"%s\"></redirect></partial-response>", pathLogin);
return;
}
pathLogin += "?source=" + request.getServletPath();
response.sendRedirect(pathLogin);
return;
}
chain.doFilter(request, response);
}
So when the session expires, does not affect the user experience in the login and registration pages. And on pages I wish session, are handled by the filter.
That would be a good solution? Are there any security risk to ignore ViewExpiredException in a ExceptionHandler?
Ignoring them is not technically bad in this specific case, but it indicates a bad design. It's as if you're using the wrong tool for the job. I.e. those views should actually never expire.
Just make specifically those views stateless.
<f:view transient="true">
...
</f:view>
This can be placed anywhere in the page, even duplicated, but most self-documenting is making it the top level tag of the page, composition or definition.
See also:
What is the usefulness of statelessness in JSF?
As I was told and found on websites, my bean will only be executed if I have a call for it on my .xhtml.
Is it possible to call my bean without any EL expression?
I need this because my HomeController is calling a method that checks for the session status and on my home.xhtml and don't have any need for fall this bean, for now.
You need to look for a solution in a different direction.
If you're homegrowing user authentication instead of using container managed authentication, then you normally use a servlet filter for the particular purpose of checking if an user is logged in or not.
The servlet filter can like as servlets (like FacesServlet) be mapped on a particular URL pattern. It will then be invoked on every request matching that URL pattern. You can explore request/session data in the filter and then handle the request/response accordingly by either continuning the filter chain, or by sending a redirect.
You need to implement javax.servlet.Filter interface accordingly. Here's a kickoff example of how the doFilter() method should be implemented assuming that you've a #SessionScoped managed bean LoginController. JSF stores session scoped managed beans as attributes of HttpSession.
#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);
LoginController loginController = (LoginController) (session != null) ? session.getAttribute("loginController") : null;
if (loginController == null || !loginController.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.
}
}
Map this filter on an URL pattern covering the restricted pages, e.g. /app/*.
#WebFilter("/app/*")
public class LoginFilter implements Filter {
// ...
}
Update if the login.xhtml is also covered by this URL pattern and you really can't change it, then change the if block as follows:
if (!request.getRequestURI().endsWith("/login.xhtml") && (loginController == null || !loginController.isLoggedIn())) {
// ...
}
I'm trying to implement a remember-me function in my java ee 6 application, but I have issues combining it with the build-in security feature. I have the following configuration in my web.xml:
<login-config>
<auth-method>FORM</auth-method>
<realm-name>my-realm</realm-name>
<form-login-config>
<form-login-page>/login.jsf</form-login-page>
<form-error-page>/login.jsf</form-error-page>
</form-login-config>
</login-config>
What I'm trying to create is a filter that automatically logs a person in of their session is expired, if they have a cookie containing some data. This works, but when the filter is called, the redirect to login.jsf has already come into effect, before I have a change to do anything about it. I assumed that filters are called before java ee's own security system since they actually are called on secured pages, but this seems to not be the case. Is there some way to let the user come to the same page that they requested instead of being redirected to login.jsf?
Filter:
#WebFilter(
filterName="authFilter",
servletNames={
"Faces Servlet"
}
)
public class AuthFilter implements Filter {
public AuthFilter() {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
User user = (User)req.getSession().getAttribute("user");
if(user == null){
String uuid = CookieUtil.getCookieValue(req, "rememberme");
if(uuid != null){
UserBean userBean = EJBUtil.lookup(UserBean.class);
RememberMe rememberme = userBean.findRememberMe(uuid);
if(rememberme != null){
user = rememberme.getUser();
try{
req.login(user.getEmail(), user.getPasswordDigest());
req.getSession().setAttribute("user", user);
CookieUtil.addCookie(res, "rememberme", uuid, CookieUtil.AGE_ONE_YEAR);
}catch(ServletException e){}
}
else{
CookieUtil.removeCookie(res, "rememberme");
}
}
}
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
Container managed authentication is indeed invoked before all filters. This is a security restriction.
You've basically 3 options:
Use programmatic filtering and login instead so that you have more finer grained control.
Do the job in preRenderView event method of the bean associated with login.jsf instead.
Grab a framework which supports "Remember me" facility on top of container managed security transparently, such as Apache Shiro or Spring Security.