问题
I'm developing a login mechanism that authenticates the user within a webfilter. When the login-button is clicked and the credentials are valid it's supposed to redirect.
The problem I have is, that the Webfilter doesn't seem to have the credentials after the login button has been pushed the first time. Logging suggests that the parameters are not present in the @SessionScoped bean that is supposed to store the credentials.
For it to actually log in, I have to push the login-button again (this time it does not matter wheter I put in the credentials at all) and then I get redirected.
The same problem also occurs when I try to invalidate the session. I'm on the page, click the button to invalidate, the page gets shown again and only after a refresh I get redirected to the login page.
The whole process of validating the user and redirecting in case the session is not valid takes place within a webfilter.
I assume the problem is, that parameters and other actions only take effect after the webfilter has been processed, but then, how are these mechanisms supposed to work? I've read everywhere that self-implemented login-mechanisms should be implemented in a webfilter, and it makes perfect sense to do so.
EDIT: I've added the essential code. (not everything, it's quite a lot)
login.xhtml
<h:body>
<h1>Login</h1>
<h:form id="loginForm">
<h:message for="loginForm" />
<h:outputLabel value="Username" />
<h:inputText value="#{CRMSession.username}"></h:inputText>
<br />
<h:outputLabel value="Passwort" />
<h:inputSecret value="#{CRMSession.passHash}"></h:inputSecret>
<h:commandButton action="submit" value="submit"></h:commandButton>
</h:form>
Webfilter (shortened): (I took out the whole cookie part, since that's all commented out at the moment the error can't be there. I just took it out to make the whole thing readable)
@WebFilter(filterName = "SP", urlPatterns = "/*")
public class SessionProvider implements Filter {
private final static String AUTH_COOKIENAME = "kimcrmstoken";
private final static String LOGINURL = "./login.jsf";
private final static String INDEXURL = "./index.jsf";
@Inject
CRMSession crmsession;
@Inject
LoggingProvider loggingProvider;
@Inject
LoginHandler loginHandler;
@Inject
ConfigUpdates configUpdates;
@Inject
CRMContext context;
@Inject
UserHandler userHandler;
Logger logger;
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
Cookie[] cookies = req.getCookies();
logger = loggingProvider.getLogger(this.getClass().getName());
logger.log(LogLevel.INFO, "SessionProvider working");
if(credentialsPresent())
logger.log(LogLevel.INFO, "credentials present");
else
logger.log(LogLevel.INFO, "NO credentials present");
if(crmsession.isAuthenticated())
logger.log(LogLevel.INFO, "Session authenticated");
if (!crmsession.isAuthenticated() && credentialsPresent()) {
/*
* Session ist nicht authentifiziert, aber credentials sind
* vorhanden
*/
logger.log(LogLevel.INFO, "session not authenticated but credentials present");
try {
if (!loginHandler.validateCredentials()) {
logger.log(LogLevel.INFO, "Credentials invalit");
crmsession.setPassHashToNull();
crmsession.setAuthenticated(false);
resp.sendRedirect(LOGINURL);
} else {
logger.log(LogLevel.INFO, "Credentials valid");
crmsession.setAuthenticated(true);
crmsession.setLoggedIn(true);
crmsession.setJustloggedin(true);
}
} catch (SQLException e) {
// wird erreicht wenn bei der Userauthentifizierung eine
// SQLexception geworfen wird
logger.log(LogLevel.ALERT, "Fehler in User Authentifizierung");
throw new ServletException(e.getMessage());
}
}
/*
* prüfen ob in der Session ein username und ein Passwort vorhanden
* sind, wenn nicht->loginpage
*/
if (!crmsession.isAuthenticated()) {
if (req.getRequestURI().contains("/login.jsf")) {
logger.log(LogLevel.INFO, "PATH is login.jsf");
crmsession.setAuthenticated(false);
chain.doFilter(request, response);
return;
}
if (!credentialsPresent()) {
logger.log(LogLevel.INFO, "NO Pass or username, redirecting");
crmsession.setAuthenticated(false);
resp.sendRedirect(LOGINURL);
return;
}
}
}
/*
* Weiterleitung zum index nach der anmeldung
*/
if(crmsession.isAuthenticated() && crmsession.isJustloggedin()){
crmsession.setJustloggedin(false);
resp.sendRedirect(INDEXURL);
}
// Fortfahren
logger.log(LogLevel.FINEST, "SessionProvider done");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
public boolean credentialsPresent() {
if (crmsession.getUsername() == null)
return false;
if (crmsession.getPassHash() == null)
return false;
if (crmsession.getUsername().equals(""))
return false;
if (crmsession.getPassHash().equals(""))
return false;
return true;
}
}
Same problem with session invalidation:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
index
<h:form>
<h:commandButton action="#{index.clearSession}" value="clear">
</h:commandButton>
</h:form>
</html>
Backing Bean:
import java.io.Serializable;
import javax.enterprise.context.RequestScoped;
import javax.faces.context.FacesContext;
import javax.inject.Named;
import javax.servlet.http.HttpSession;
@RequestScoped
@Named
public class Index implements Serializable {
public String clearSession(){
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
session.invalidate();
return "./index.jsf";
}
}
回答1:
It appears that you got the responsibility of the filter wrong. Its responsibility is not to perform the actual login (or logout). Its responsibility is to restrict access to the requested resource based on the logged-in user.
You should perform the actual login (and logout) in the action method of the request/view scoped backing bean associated with the login form. After a successful login/logout you should redirect to the target page. The filter should just check if the currently logged-in user is allowed to access the requested resource. If it is, then continue filter chain. If it is not, then redirect to login page or send 401/403.
Note that your filter doesn't cover JSF resource or ajax requests. In this answer you can find a complete example of a JSF aware authentication filter: Authorization redirect on session expiration does not work on submitting a JSF form, page stays the same.
来源:https://stackoverflow.com/questions/31646573/webfilter-login-mechanism-takes-always-two-attempts