Homegrown authentication filter does not show the welcome page - security

So I've come accross quite a numbre of questions similar to mine, and I was starting to get it until I realised I don't, in short, here's the story :
In an authentification bean, success of authentification should result in accessing some web resources, failure should "filter" access and redirect to current login page.
Now, in that authentification bean, I added this line in case of success :
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(authentificationBean1.AUTH_STATE, "true") ;
AUTH_STATE is defined in the bean as :
public static final String AUTH_STATE = "";
In case of failure, I do the following :
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(authentificationBean1.AUTH_STATE, null) ;
Now in the filter (one that is applied to every file except of authentification page), my doFilter method looks like this :
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
if (((HttpServletRequest) request).getSession().getAttribute(authentificationBean1.AUTH_STATE) == null) {
((HttpServletResponse) response).sendRedirect("authentification.xhtml");
}
if(((HttpServletRequest) request).getSession().getAttribute(authentificationBean1.AUTH_STATE) != null) {
((HttpServletResponse) response).sendRedirect("accueil.xhtml");
}
}
My idea was that if authentification went well, the authentificationBean1.AUTH_STATE session attribut will be set to something not null, thus in the filter test I'll be able to redirect to a welcom page (accueil.xhtml) ; if that attribut is null, we'll stay in the authentification page.
Tasting the whole thing : the filter seems to work but too much, by that I mean even when authentification test must succeed it doesn't allow me to pass to the welcome page. It was actually working fine without the filter, it looks like I missed something about using filters with JSF or filters as it.
P.S : didn't apply chain.doFilter because I do not have another filter to call, but suspecting something there.
Thanks for your indications.
EDIT :
<filter>
<filter-name>RestrictionFilter</filter-name>
<filter-class>beans.RestrictionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RestrictionFilter</filter-name>
<url-pattern>/faces/accueil.xhtml</url-pattern>
</filter-mapping>

Your filter is running in an infinite loop, redirecting to itself everytime. It is never continuing the request to the servlet. It seems that you're misunderstood how HTTP works. With response.sendRedirect() you're basically firing a brand new HTTP request. This brand new HTTP request will invoke the filter again. So, when your filter matches the condition in order to redirect to accueil.xhtml, it will keep redirecting to that page in an infinite loop and never continue to the servlet in order to process the request.
Further you also misunderstood the meaning of chain.doFilter(). It does not explicitly advance to the next filter. It just continues the request as if there was no filter. Whether there's another filter next in the chain or not is completely irrelevant. If there's no filter, then it will just end up in the target servlet (which is the FacesServlet in your case, who's responsible for processing the JSF page).
Basically, the flow should be as follows:
If the user is not logged in, then:
If the currently requested page is not authentification.xhtml, then redirect to it.
Or if the currently requested page is already authentification.xhtml, then continue request.
Or if the user is logged in, then continue the request regardless of the requested page.
In other words, this should do it:
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String loginURL = request.getContextPath() + "/authentification.xhtml";
boolean loggedIn = session != null && session.getAttribute(authentificationBean1.AUTH_STATE) != null;
boolean loginRequest = request.getRequestURI().startsWith(loginURL);
boolean resourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER);
if (loggedIn || loginRequest || resourceRequest)) {
chain.doFilter(request, response);
} else {
response.sendRedirect(loginURL);
}
}
Note that I also added a check on JSF resources (the CSS/JS/image files included via <h:outputStylesheet|outputScript|graphicImage>), otherwise they would also be blocked when the login page is presented. Also note that this filter can be mapped on /* and not on only a single page.

Related

Prevent page access via URL

Good morning everyone!
I have an application that has access control, it is working ok
But the user can write the URL in the browser and access pages that he does not have access to
Could someone help me solve this?
Below is the implementation of Filter
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filter) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
if (session.getAttribute("sessionUser") != null
|| req.getRequestURI().endsWith("Login.xhtml")) {
System.out.println("if");
filter.doFilter(request, response);
} else {
System.out.println("else");
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect(req.getContextPath()+"/Login.xhtml");
}
}
Before answer i suggest you to use one security framework to control access of pages. something powerfull like spring security.
But in this case you checked only that user session is validated or not but nothing related to page or page name added to session.
You should add all user accessed page (name for example), to session as attributes after successful login and then in this filter, check what page user requested to access?
If session attributes contains that page dofilter called else redirect to access denied page.

JSF SessionScoped only allow to connect from one account to a web page

I am developing a JSF application where 3 kind of users(1, 2,3) can login using an id and when they login the access to different 3 menus(Menuuser1, Menuuser2, Menuuser2) depending on what kind of users they are. Each time an user logins I store its id in an attibute in a sessionscope attribute.
I want that if an user of type 1 logins and he open another tab in the browser he is redirected to the menuuser1 and cannot login from the same computer as an user of type 2.
How could I do that?
You can perform authentication in filter , you can check the role of the user in filter class, if given role is user1, it will forward the request to the Menuuser1 else if it's the user2 then forward to Menuuser2 and so on ...
Example
public class ControlRole implements Filter{
public void init(FilterConfig arg0) throws ServletException {}
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpSession session = request.getSession(false);
User user = (session != null) ? session.getAttribute("user") : null;
if(user.getRole().getName().equals("user1")){
chain.doFilter(req, resp);//sends request to next resource
}
else if(user.getRole().getName().equals("user2")){
// ......
}
else{
oresponse.sendRedirect(request.getContextPath() + "/login");
}
And for "If an user with rol1 logins from its computer, he will not be able to login from the same computer as an user with rol2 while he is loged as user with rol1" this is automatically done by the sessionScope

JSF, session timeout handling [duplicate]

This question already has answers here:
Authorization redirect on session expiration does not work on submitting a JSF form, page stays the same
(2 answers)
Closed 4 years ago.
I have configured my session timeout in the server and have added a filter to handle session timeout. But when I am trying to redirect it back to the login page its not working. I searched the net but not getting anything solid. I am using jsf.. my code
public class SessionTimeoutFilter implements Filter {
private String timeoutPage = "login.seam";
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain filterChain) throws IOException,ServletException {
if ((request instanceof HttpServletRequest)
&& (response instanceof HttpServletResponse))
{
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
if (isSessionControlRequiredForThisResource(httpServletRequest)) {
if (isSessionInvalid(httpServletRequest))
{
String timeoutUrl = httpServletRequest.getContextPath()
+ "/" + getTimeoutPage();
System.out.println("Session is invalid! redirecting to timeoutpage : " + timeoutUrl);
httpServletResponse.sendRedirect(timeoutUrl);
return;
}
}
}
filterChain.doFilter(request, response);
}
Can anyone tell me what am i doing wrong... why is sendredirect not responding
Maybe this solution will be proper for your needs:
How to redirect to index page if session time out happened in jsf application
if you need perform some action on session timeout you can also create #Destory annotated method on session statefull bean.

Bypass login filter for 1 time when user chose continue as guest

I have a booking page with URL /booking/Create.jsf. I have a filter for URL pattern /booking/* so the user is asked to login on the page /login/signin.jsf before being taken to Create.jsf. But I have a button "Continue as guest" so that user that is not registered can create a booking on Create.jsf page without log-in. How can I make that happen. Any help will be appreciated. My filter looks like
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
UserLoginController loginController = (UserLoginController) req.getSession().getAttribute("userLoginController");
if(loginController != null && loginController.isLoggedIn()){
chain.doFilter(request, response);
}
else{
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.sendRedirect(req.getContextPath() + "/login/signin.jsf");
}
}
The page create.jsf is considered in the end as public page. So, in order to enable the filter for other pages concerning "booking", just create a new subfolder named for example "user" inside which you should put others pages of "booking", the new url pattern of the filter will be as a result: /booking/user/*. This way, the other stuff of booking remains safe and the page /booking/create.jsf will be easily available for the users and guests because it's not covered by the filter.

How to avoid ;jsessionid=XXX on the first call to a page? it works if first page is jsp

I have an application which uses the welcome-page index.jsp with an <iframe></iframe> the contents of the iframe is a jsf page. If I access index.jsp I see a cookie already on the first get in firebug:
Set-Cookie JSESSIONID=C615DA89B6EF73F801973EA3DCD3B226; Path=/
The page of the <iframe> inherits this jsessionid. BUT: when I directly access the page of the <iframe/> I get the jsessionId rewritten to all URLs without a cookie - on the first request. Afterwards the cookie is used. This is all fine - if:
The security system would allow me to perform url rewrites.
I run jboss 4.2.2
I want to achieve the same behaviour as I have with the index.jsp - e.g. always use cookies and always avoid http rewrite.
[EDIT]
thanks to balusc's answer I wrote this:
public class JsessionIdAvoiderFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
boolean allowFilterChain = redirectToAvoidJsessionId((HttpServletRequest) req, (HttpServletResponse)res);
//I'm doing this because if I execute the request completely, it will perform a pretty heavy lookup operation. No need to do it twice.
if(allowFilterChain)
chain.doFilter(req, res);
}
public static boolean redirectToAvoidJsessionId(HttpServletRequest req, HttpServletResponse res) {
HttpSession s = req.getSession();
if(s.isNew()) {
//after the redirect we don't want to redirect again.
if(!(req.isRequestedSessionIdFromCookie()&&req.isRequestedSessionIdFromURL()))
{
//yeah we have request parameters actually on that request.
String qs = req.getQueryString();
String requestURI = req.getRequestURI();
try {
res.sendRedirect(requestURI+"?"+qs);
return false;
} catch (IOException e) {
logger.error("Error sending redirect. " + e.getMessage());
}
}
}
return true;
}
}
Don't forget to add it to your web.xml
<filter>
<display-name>JsessionId Filter</display-name>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<filter-class>my.namespace.JsessionIdAvoiderFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
Since Servlet 3.0 you could use <tracking-mode>COOKIE</tracking-mode> for this. But as JBoss 4.2.2 isn't Servlet 3.0 compilant, this isn't an option.
Easiest would be to create a servlet filter which sends a redirect to HttpServletRequest#getRequestURI() when HttpSession#isNew() returns true. Don't forget to check the HttpServletRequest#isRequestedSessionIdFromCookie() to prevent an infinite redirect loop when the client doesn't support cookies at all.
Based on Christopher Schultz recommendation I tried this and it works.
package com.rama.test.jsessionfilter
public class JsessionIdAvoiderFilter implements Filter {
protected static final Logger LOGGER = LogManager.getLogger(JsessionIdAvoiderFilter.class);
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
if (!(req instanceof HttpServletRequest)) {
chain.doFilter(req, res);
return;
}
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// Redirect requests with JSESSIONID in URL to clean old links
/* If you really want clean up some old links which have Jsession id bookmarked clean it. If its new app
this below check is not required. */
if (request.isRequestedSessionIdFromURL()) {
String url = request.getRequestURL().append(request.getQueryString() != null ? "?"
+ request.getQueryString() : "").toString();
response.setHeader("Location", url);
response.sendError(HttpServletResponse.SC_MOVED_PERMANENTLY);
LOGGER.info(" Found url with jsession id in it:"+ request.getRequestURL() +": url="+url);
return;
}
// Prevent rendering of JSESSIONID in URLs for all outgoing links
HttpServletResponseWrapper wrappedResponse = new HttpServletResponseWrapper(
response) {
#Override
public String encodeRedirectUrl(String url) {
return url;
}
#Override
public String encodeRedirectURL(String url) {
return url;
}
#Override
public String encodeUrl(String url) {
return url;
}
#Override
public String encodeURL(String url) {
return url;
}
};
chain.doFilter(req, wrappedResponse);
}
public void destroy() {
}
public void init(FilterConfig arg0) throws ServletException {
}
}
and the following entry in web.xml
<filter>
<display-name>JsessionId Filter</display-name>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<filter-class>com.rama.test.jsessionfilter.JsessionIdAvoiderFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
Works great !!!.
This can be done with a simple Filter that wraps the request with an HttpServletRequest which overrides HttpServletRequest.encodeURL and HttpServletRequest.encodeRedirectURL. Simply return the String argument passed to it and you will disable URL re-writing. Note that this will only work for a single webapp unless you want to either configure it in conf/web.xml (not recommended) or configure it in all of your separate webapps.
This technique is superior to that posted later in your question because it does not require redirection which can slow-down your requests. IMO, it's also cleaner.

Resources