I'm developing JSF application with Apache Shiro. I autenticate the user with Shiro and redirect her to home page there is no problem with that. After the authentication when I try to access login page, it doesn't redirect me the homepage. I can login again even when there is already loggedin user. I'm doing Programmatic Login as BalusC mentioned in his blog post.
[main]
credentialsMatcher = org.apache.shiro.authc.credential.PasswordMatcher
myRealm = com.example.security.myRealm
myRealm.credentialsMatcher = $credentialsMatcher
securityManager.realms = $myRealm
user = com.example.web.filter.FacesAjaxAwareUserFilter
user.loginUrl = /login.xhtml
[urls]
/login.xhtml = user
This filter is written from the blog post.
public class FacesAjaxAwareUserFilter extends UserFilter {
private static final String FACES_REDIRECT_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<partial-response><redirect url=\"%s\"></redirect></partial-response>";
@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
HttpServletRequest req = (HttpServletRequest) request;
if ("partial/ajax".equals(req.getHeader("Faces-Request"))) {
response.setContentType("text/xml");
response.setCharacterEncoding("UTF-8");
response.getWriter().printf(FACES_REDIRECT_XML, req.getContextPath() + getLoginUrl());
}
else {
super.redirectToLogin(request, response);
}
}
}
What is the problem and how can I redirect the user if she is already authenticated?
EDIT: For now I'm using PostConstruct annotation to redirect if the user is already authenticated. I'm open to any good solution.
After the authentication when I try to access login page, it doesn't redirect me the homepage. I can login again even when there is already loggedin user
Neither Shiro nor the custom Shiro user filter are intented to prevent that. Shiro doesn't have builtin facilities for this. The custom Shiro user filter runs only when an unauthenticated user is found, not when an already authenticated user is found.
Preventing an authenticated user from accessing the login page directly is your own responsibility. Depending on business requirements you can do the following:
Just allow it. Perhaps the user just want to switch logins. You could if necessary conditionally show a message like:
<ui:fragment rendered="#{not empty request.remoteUser}">
You are already logged-in as #{request.remoteUser}.
By logging in as another user, you will be logged out.
</ui:fragment>
<h:form id="login">
...
</h:form>
Don't allow it, but stay in the same page. Conditionally hide the login form and show a message like:
<ui:fragment rendered="#{not empty request.remoteUser}">
Hey, how did you end up here?
You are already logged-in as #{request.remoteUser}!
</ui:fragment>
<h:form id="login" rendered="#{empty request.remoteUser}">
...
</h:form>
And, of course, make sure that your web application doesn't have anywhere a login link when the user is already logged in.
Don't allow it and redirect to the desired target page. This can in turn be done in several ways. Most clean approach is using a servlet filter.
@WebFilter(urlPatterns = "/login.xhtml")
public class LoginPageFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (request.getRemoteUser() != null) {
response.sendRedirect(request.getContextPath() + "/home.xhtml"); // Redirect to home page.
} else {
chain.doFilter(req, res); // User is not logged-in, so just continue request.
}
}
// Add/generate init() and destroy() with NOOP.
}
You can also do this in a preRenderView
event listener. A @PostConstruct
may be too late as the response may already be committed at that point. Note that redirecting without any form of feedback may be confusing for the enduser. In the filter, consider passing an additional parameter which should trigger a conditional message. Or in the preRenderView
event listener, set a flash scoped message.