我正在开发一种登录机制,可以在webfilter中对用户进行身份验证。当登录按钮被点击并且凭证有效时,它应该被重定向。
我遇到的问题是,Webfilter在第一次按下登录按钮后似乎没有凭据。日志记录提示参数不存在于应该存储凭据的@SessionScoped bean中。
要真正登录,我必须再次按下登录按钮(这一次我是否输入凭据并不重要),然后我被重定向。
当我尝试使会话无效时也会出现同样的问题。我在页面上,单击按钮使其失效,页面再次显示,只有在刷新后我才被重定向到登录页面。
在会话无效的情况下,验证用户和重定向的整个过程在webfilter中进行。
我认为问题是,参数和其他动作只有在webfilter被处理后才生效,但是,这些机制应该如何工作?我到处读到,自实现的登录机制应该在webfilter中实现,这样做非常有意义。
编辑:我已经添加了必要的代码。(不是全部,但相当多)
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(缩短):(我拿出了整个cookie部分,因为这是所有的注释掉了,目前的错误不能在那里。我只是把它拿出来让整个东西更容易读)
@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;
}
}
会话无效同样的问题:
<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>
支持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";
}
}
看来你把过滤器的职责搞错了。它的职责不是执行实际的登录(或注销)。它的职责是根据登录的用户限制对所请求资源的访问。
您应该在与登录表单关联的请求/视图作用域支持bean的操作方法中执行实际的登录(和注销)。成功登录/注销后,您应该重定向到目标页面。过滤器应该检查当前登录的用户是否被允许访问所请求的资源。如果是,则继续过滤链。如果不是,则重定向到登录页面或发送401/403。
注意,您的过滤器不包括JSF资源或ajax请求。在这个答案中,您可以找到一个JSF感知身份验证过滤器的完整示例:会话到期时的授权重定向在提交JSF表单时不起作用,页面保持不变。