【问题标题】:Setting Authentication Header in Servlet via Filter通过过滤器在 Servlet 中设置身份验证标头
【发布时间】:2011-06-21 10:33:54
【问题描述】:

前言

这是我第一次尝试过滤器,请温柔。

项目描述

我正在尝试为我们的几个应用程序完成 SSO 的构建,但我似乎遇到了困难。我尝试连接的 webapp 使用“身份验证”标头来确定应用程序中的用户凭据。我已经构建了一个过滤器,希望在将标题传递给 webapp 之前设置它。

问题

代码通过 eclipse 验证、编译、加载到 Tomcat 并传递到 webapp。唯一缺少的是 Authentication 标头。

我错过了什么/做错了什么?

AuthenticationFilter 来源

package xxx.xxx.xxx.xxx.filters;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import xxx.xxx.xxx.ConfigFile;
import xxx.xxx.xxx.Console;
import xxx.xxx.xxx.FalseException;

import xxx.xxx.activity.EncryptUtil;

public class AuthenticationFilter implements Filter {
  public ConfigFile config;

  public void init(FilterConfig arg0) throws ServletException {
    config = new ConfigFile("C:/config.properties");
  }

  public void doFilter(ServletRequest sRequest, ServletResponse sResponse, FilterChain filterChain) throws IOException, ServletException {
    Console.debug("AuthenticationFilter.doFilter() triggered.");
    ServletRequestWrapper request = new ServletRequestWrapper((HttpServletRequest) sRequest);
    HttpServletResponse response = (HttpServletResponse) sResponse;
    HttpSession session = request.getSession();
    Cookie cookie = null;
    try {
      if (request.getParameter("logout") != null) {
        session.invalidate();
        throw new FalseException("Logout recieved");
      }
      String auth = request.getHeader("Authorization");
      if (auth == null) {
        Console.debug("Authorization Header not found.");
        // get cookie --COOKIE NAME--
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
          throw new FalseException("Cookies not set.");
        }
        for (int i = 0; i < cookies.length; i++) {
          if (cookies[i].getName().equals(config.getProperty("authentication.cookie.name"))) {
            cookie = cookies[i];
          }
        }
        if (cookie == null) {
          throw new FalseException("Cannot find Cookie (" + config.getProperty("authentication.cookie.name") + ") on Client");
        }
        Console.debug("Cookie (" + config.getProperty("authentication.cookie.name") + ") found on Client. value="+cookie.getValue());
        String decToken = decryptToken(cookie.getValue());
        Console.debug("Decrypted Token: "+decToken);

        Console.debug("Setting Authorization Header...");
        request.setAttribute("Authorization", decToken);
        request.addHeader("Authorization", decryptToken(cookie.getValue()));
        Console.debug("Authorization Header set.");
        Console.debug("Validating Authorization Header value: "+request.getHeader("Authorization"));
      }
    }catch (FalseException e) {
      Console.msg(e.getMessage() + ", giving the boot.");
      response.sendRedirect(config.getProperty("application.login.url"));
    } catch (Exception e) {
      Console.error(e);
    }
    Console.debug("AuthenticationFilter.doFilter() finished.");
    filterChain.doFilter(request, response);
  }

  public void destroy() {

  }

  private String decryptToken(String encToken) {
    String token = null;
    token = EncryptUtil.decryptFromString(encToken);
    return token;
  }
}

web.xml 来源

<web-app>
  <filter>
    <filter-name>AuthenticationFilter</filter-name>
    <display-name>AuthenticationFilter</display-name>
    <description></description>
    <filter-class>com.xxx.xxx.xxx.filters.AuthenticationFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>AuthenticationFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  ...
</web-app>

ServletRequestWrapper 源码

package com.xxx.xxx.xxx.filters;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

public class ServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {

  public ServletRequestWrapper(HttpServletRequest request) {
    super(request);
    headerMap = new HashMap();
  }

  private Map headerMap;

  public void addHeader(String name, String value) {
    headerMap.put(name, new String(value));
  }

  public Enumeration getHeaderNames() {
    HttpServletRequest request = (HttpServletRequest) getRequest();
    List list = new ArrayList();
    for (Enumeration e = request.getHeaderNames(); e.hasMoreElements();) {
      list.add(e.nextElement().toString());
    }

    for (Iterator i = headerMap.keySet().iterator(); i.hasNext();) {
      list.add(i.next());
    }
    return Collections.enumeration(list);
  }

  public String getHeader(String name) {
    Object value;
    if ((value = headerMap.get("" + name)) != null)
      return value.toString();
    else
      return ((HttpServletRequest) getRequest()).getHeader(name);
  }

}

调试日志

LoginServlet.doGet() triggered.
[DEBUG] : Authenticate.isClientLoggedIn() triggered.
xxx url : https://xxx.xxx.xxx/xxx/home.action
[DEBUG] : Authenticate.isClientLoggedIn() status code: 401
Unauthorized User.
Client IS NOT logged in.

-- Fill out Login Form, submit --

LoginServlet.doPost() triggered.
[DEBUG] : Authenticate.isClientLoggedIn() triggered.
xxx url : https://xxx.xxx.xxx./xxx/home.action
[DEBUG] : Authenticate.isClientLoggedIn() status code: 401
Unauthorized User.
Client IS NOT logged in.
Client (--USERID--) attempting basic authentication with password(--PASSWORD--).
[DEBUG] : BasicAuthentication.touch(http://localhost:PORT/vu/loginCheck.html, --USERID--, --PASSWORD--) triggered.
[DEBUG] : BasicAuthentication.touch() response code: 200
Client (--USERID--) has been logged IN.
Client (--USERID--) basic authentication finished, Client is logged in.
Client (--USERID--) logged in successfully.
[DEBUG] : Cookie (xxx_token) Set: 1e426f19ebdfef05dec6544307addc75401ecdc908a3c7e6df5336c744--SECRET--
[DEBUG] : Redirecting client to https://xxx.xxx.xxx/xxx/home.action

-- Redirected to webapp, filter recieves --

[DEBUG] : AuthenticationFilter.doFilter() triggered.
[DEBUG] : Authorization Header not found. << Initical check to see if user is already logged in to site
[DEBUG] : Cookie (xxx_token) found on Client. value=1e426f19ebdfef05dec6544307addc75401ecdc908a3c7e6df5336c744--SECRET--
[DEBUG] : Decrypted Token: Basic --SECRET--
[DEBUG] : Setting Authorization Header...
[DEBUG] : Authorization Header set.
[DEBUG] : Validating Authorization Header value: Basic --SECRET-- << Value matches Decrypted Token
[DEBUG] : AuthenticationFilter.doFilter() finished.

-- Web Application errors out, unable to find Authorization header 

感谢您的帮助。

【问题讨论】:

  • 只是一些建议;请不要抛出 Throwable,这是为非常广泛的错误保留的,通常只有 JVM 作为最后手段才捕获,并且永远不会在不重新抛出它们的情况下在过滤器中捕获 throwable 或异常。
  • @Andrew White,我会记住这一点。我试图在没有一堆难看的 if 语句的情况下将我的过程和错误分开。它不能原谅我的行为,只是解释它。
  • 你说这个可以编译,但是我在 ServletRequestWrapper 上找不到 addHeader() 方法。
  • @highlycaffeinated:抱歉,我没有在帖子中包含自定义包装类。我现在添加它。
  • 好的,你到底是怎么断定标题丢失的?您是否在感兴趣的 webapp 中调试了request.getHeader()? webapp 收到的请求是ServletRequestWrapper 的实例吗?

标签: java servlets servlet-filters


【解决方案1】:

我正在添加一个新答案,因为它完全不同。

我对我的系统进行了测试。我复制了您的代码,转储了 cookie 测试,并编写了一个简单的 Servlet 来为我转储。

它工作得很好,除了一个警告。

我不知道您的应用如何使用它。但是您的ServletRequestWrapper 实现了getHeaderNamesgetHeader,但它没有实现getHeaders。我在使用getHeaders 尝试转储请求时遇到了这个问题,当然,授权丢失了。

因此,您可能需要仔细查看您的代码,看看它是否确实没有使用getHeaders。如果是,它会“正常工作”,但会完全跳过您所做的工作,从而错过您的 Authorization 标头。

这是我的实现,它对我有用。

    @Override
    public Enumeration getHeaders(String name) {
        Enumeration e = super.getHeaders(name);
        if (e != null && e.hasMoreElements()) {
            return e;
        } else {
            List l = new ArrayList();
            if (headerMap.get(name) != null) {
                l.add(headerMap.get(name));
            }
            return Collections.enumeration(l);
        }
    }

【讨论】:

    【解决方案2】:

    首先,最基本的问题(类似于“这是插入”的问题),我假设您的 cookie 都植根于同一个域,并且您不想在这里获得跨域行为。因为 cookie 不会那样做。

    除了 cookie 测试之外,这看起来还不错。但这一切都取决于 cookie 测试。

    如果您想测试 Authorization 标头,那么您可以简单地短路 cookie 测试(即它总是通过)并使用一些有效值填充 Authorization 标头。这将在短期内测试您的整个授权方案。

    一旦完成/修复,您就可以专注于 cookie 设置和交付。

    我还假设您没有使用基于 Java EE 容器的身份验证,而 Tomcat 会为您执行此检查。在这种情况下,过滤器只是“为时已晚”。在您的过滤器被调用之前,容器就已经做出了决定。

    如果您使用基于容器的身份验证,并且您的应用程序位于同一个容器上,我想 Tomcat(或某人)在容器级别具有 SSO 选项。我知道 Glassfish 会开箱即用地为您做到这一点。如果是这种情况,应该直接修改 Tomcat 工件(即不可移植的 Java EE/Servlet 机制)来实现这一点。

    【讨论】:

    • 好答案威尔。我还想指出,出于安全考虑,建议使用应用程序服务器提供的身份验证和 SSO 设施。它们可能经过了更好的测试和更广泛的审查。我特别担心提到的 EncryptionUtils。
    • 通常我会同意你们的观点,但项目要求规定登录必须支持现有的用户管理,这恰好是基本身份验证。应用程序本身使用标题中的信息来填充网站上的信息。
    • 我并不是建议您更改为基于容器的身份验证。但是,需要明确的是,容器确实支持基本身份验证。但是您必须更改您的应用程序以使用 Servlet 安全接口(isUserInRole、getPrincipal 等)。
    • 这就是过滤器的原因,因为我不能(轻松)更改应用程序。最终该项目将转移到哈希表,但同时我们在这里。
    猜你喜欢
    • 2015-04-01
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-28
    • 2022-01-20
    • 2012-10-27
    相关资源
    最近更新 更多