使用请求头的Tomcat身份验证

使用请求头的Tomcat身份验证,tomcat,servlets,login,passwords,jdbcrealm,Tomcat,Servlets,Login,Passwords,Jdbcrealm,我有一个在Tomcat7上运行的web应用程序。当前用户通过JDBCRealm使用密码登录 在我的组织中有一个身份验证服务器。用户向该服务器发送HTTP请求,服务器以某种方式对其进行身份验证,然后使用指定已验证用户名(无密码)的自定义HTTP头将请求转发到我的应用程序 我想使用这种机制对我的应用程序的用户进行身份验证,同时保留JDBC领域。当用户向/login发送请求时,他们的请求将使用标题进行身份验证,他们将被重定向到主页,就像他们使用标准j_security_check表单登录一样,但不必提

我有一个在Tomcat7上运行的web应用程序。当前用户通过JDBCRealm使用密码登录

在我的组织中有一个身份验证服务器。用户向该服务器发送HTTP请求,服务器以某种方式对其进行身份验证,然后使用指定已验证用户名(无密码)的自定义HTTP头将请求转发到我的应用程序

我想使用这种机制对我的应用程序的用户进行身份验证,同时保留JDBC领域。当用户向
/login
发送请求时,他们的请求将使用标题进行身份验证,他们将被重定向到主页,就像他们使用标准
j_security_check
表单登录一样,但不必提供密码

以下是我到目前为止的想法:

@WebFilter("/login/*")
public class LoginFilter implements Filter {

  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
    throws IOException, ServletException {

    HttpServletRequest httpRequest = (HttpServletRequest) req;
    HttpServletResponse httpResponse = (HttpServletResponse) res;

    String username = ... // Extract username from httpRequest, not an issue

    httpRequest.login(username, "???"); // Need to provide a password!

    httpResponse.sendRedirect(httpRequest.getContextPath() + "/pages/home.xhtml");

  }

}
当我为JDBCRealm提供了正确的密码,但该密码在过滤器中不可用时,这种方法就可以工作

更新:我用一个定制阀门解决了这个问题:

public class LoginValve extends ValveBase {

    @Override
    public void invoke(Request req, Response res) throws IOException, ServletException {

    if (!req.getRequestURI().equals(req.getContextPath() + "/login")) {
        getNext().invoke(req, res);
        return;
    }

    Session session = req.getSessionInternal();

    if (session.getPrincipal() == null) {
        String username = ... // From req
        List<String> roles = ... // From req
        session.setPrincipal(new GenericPrincipal(username, null, roles));
    }

    req.setUserPrincipal(session.getPrincipal());

    getNext().invoke(req, res);

}
公共类LoginValve扩展ValveBase{
@凌驾
public void invoke(请求请求、响应请求)抛出IOException、ServletException{
如果(!req.getRequestURI().equals(req.getContextPath()+“/login”)){
getNext().invoke(req,res);
返回;
}
Session Session=req.getSessionInternal();
if(session.getPrincipal()==null){
字符串用户名=…//来自req
列表角色=…//来自req
setPrincipal(新的GenericPrincipal(用户名、null、角色));
}
req.setUserPrincipal(session.getPrincipal());
getNext().invoke(req,res);
}

我从一个稍后调用的过滤器进行重定向。

我离评论还有三个信誉点,所以我必须根据给定的信息回答,并用猜测填补一些空白

您可以通过子类化来实现“身份验证”

重写getRemoteUser()和getUserPrincipal(),以便它们返回经过身份验证的用户的用户名,以及基本上可以是任何您想要创建的主体对象。a应该便于使用。您可能还应该重写getAuthType()返回HttpServletRequest.FORM_AUTH,使所有内容都很好地结合起来。并根据Javadoc实现一个快速注销()方法,该方法表示“在请求中调用getUserPrincipal、getRemoteUser和getAuthType时,将null设置为返回的值。”

然后,在过滤器中,将传入的HttpServletRequest包装到包装器对象中,并将包装器对象传递到chain.doFilter()中


如果您想变得更有趣,还可以覆盖包装器上的login()方法,它接受任何(或空)密码并创建内部实例变量,这些变量将由getRemoteUser()、getUserPrincipal()和getAuthType()返回.

只需从realm使用的同一用户数据库获取它?密码在数据库中散列,而且似乎是浪费资源,因为我并不真正需要密码,我只想将用户设置为已登录。然后将realm配置为不再对其进行散列。我无法将密码保存在明文中,因此用户注册是通过同一个realm进行的?这非常简单可能是因为您显然有一个单独的身份验证服务器。那么,只需添加另一个不散列密码的域,它将用作只读域,这样您就可以使用已经散列的密码登录。我已经对HttpServletRequestWrapper进行了子类化,并且当筛选器设置为@WebFilter(“/*”)时然后,您的解决方案似乎可以正常工作。但是,当我将其设置为@WebFilter(“/login/*”)时,会出现一个“未找到资源”错误,因为chain.doFilter之后没有任何内容。我需要以某种方式将新请求重定向到“/pages/home.xhtml”,但sendRedirect没有像doFilter那样接受请求参数。我还尝试使用getRequestDispatcher(…).前进(……),初始重定向正常,用户已登录,但所有后续AJAX请求似乎都被破坏。我不知道这是否是AJAX失败的原因,但我注意到了一件奇怪的事情:在每个XHR上,服务器都会响应一个新的JSESSIONID cookie,尽管过滤器只对投票给我答案的人调用onceThanks我超过了评论的阈值。@liad,JSSessionID cookie问题让我问,是所有请求,包括XHR、图像等,都以反向代理方式通过身份验证服务器,还是只是部分请求?还是身份验证服务器使用302响应将请求重定向到应用服务器?记住,sesson cookie是与主机名关联。由于身份验证服务器正在插入请求头,我假设这意味着会话cookie(至少来自身份验证请求)与身份验证服务器的主机名关联。未通过身份验证服务器的任何后续请求都不会具有正确的会话cookie。当您说每个XHR响应都设置了新的JSESSIONID cookie时,这表明请求没有发送任何JSESSIONID cookie?如果没有,原因为何?