1

sourceforge spnego projectを使用して SSO 認証を実装しました。

あらゆる種類のサーブレット認証を実装するのはこれが初めてなので、私が知らない認証またはサーブレットに関する非常に基本的なものが欠けている可能性があります...

SpnegoHttpFilterフィルター チェーンの一番上にあるライブラリにパッケージ化されている をオーバーライドなしで使用しています。次に、QueryFilterログオン名をデータベースの user_id にマップできるように、フィルター チェーンの次に独自のフィルターを含めました。ログオン名 (Windows ドメインの NT ユーザー ID) はgetRemoteUser、HttpRequest が を通過した後に呼び出しによって返されSpnegoHttpFilterます。これはすべて正常に機能しているようです。

私自身のフィルターQueryFilterは、本来の処理を行っており、ログオン名をデータベースの user_id に正しくマッピングしています。このフィルターには、認証に合格しないリクエストを拒否するロジックもあります。これも正常に機能しています。未承認のリクエストをシミュレートすると、このフィルターはそれを停止し、サーブレットには到達しません。

問題は、my で認証を通過し、サーブレットで完全に正常に実行された場合でも、すべてのリクエストが 401 (HTTP Request Status Unauthorized) として返されることです。QueryFilter

これを使用して、独自のフィルターで応答を明示的に 200 (HTTP Request Status OK) として定義しようとしましmyHttpResponse.setStatus(HttpServletResponse.SC_OK)たが、何も変わりませんでした。

問題を切り分けるために、HttpSpnegoFilterを完全に削除し、ハードコードされたログオン名 (NT ユーザー ID) をQueryFilter. これは正常に機能し、応答は 401 (Unauthorized) ではなくなりました。

つまり、パッケージ化されたHttpSpnegoFilterものが何らかの形でリクエストを に変換していることを意味しUnauthorizedます。と、言っても変わらない形でやっていて、実はOKなんです。

この spnego sourceforge プロジェクトを使用して、応答ヘッダーを 200 (OK) として返すように設定する方法を知っている人はいますか?

Web アプリの完全なフィルター チェーンweb.xmlは以下のとおりです。前述のようにHttpSpnegoFilter、チェーンの上部でパッケージ化されたものを使用し、そのすぐ下で独自のフィルター (正しく機能しているようです) を使用します。

<filter>
    <filter-name>SpnegoHttpFilter</filter-name>
    <filter-class>net.sourceforge.spnego.SpnegoHttpFilter</filter-class>

    <init-param>
        <param-name>spnego.allow.basic</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.allow.delegation</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.allow.localhost</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.allow.unsecure.basic</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.login.client.module</param-name>
        <param-value>spnego-client</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.krb5.conf</param-name>
        <param-value>krb5.conf</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.login.conf</param-name>
        <param-value>login.conf</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.preauth.username</param-name>
        <param-value>myADServicePrincipal</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.preauth.password</param-name>
        <param-value>myADServicePrincipalPassword</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.login.server.module</param-name>
        <param-value>spnego-server</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.prompt.ntlm</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>spnego.logger.level</param-name>
        <param-value>1</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>SpnegoHttpFilter</filter-name>
    <servlet-name>QueryServlet</servlet-name>
</filter-mapping>

<filter>
    <filter-name>QueryFilter</filter-name>
    <filter-class>my.package.name.QueryFilter</filter-class>

    <init-param>
        <param-name>query.permission.list</param-name>
        <param-value>getQueryPermission</param-value>
    </init-param>

    <init-param>
        <param-name>remote.user.column</param-name>
        <param-value>nt_user_id</param-value>
    </init-param>

    <init-param>
        <param-name>user.id.column</param-name>
        <param-value>user_id</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>QueryFilter</filter-name>
    <servlet-name>QueryServlet</servlet-name>
</filter-mapping>

完全を期すために以下にも含まれています(ただし、クラスを使用せずにハードコードされたNTユーザーIDを渡すだけでQueryFilter問題なく動作するため、問題には関係ないようです)。最後から 2 番目の行は、応答が役に立たないSpnegoHttpFilterように明示的に指示する場所です。OK

import java.io.IOException;
import java.util.Map;

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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public final class QueryFilter implements Filter {

    private MapListDAO myMapListDAO;
    private String myPermissionsList;
    private String myRemoteUserColumn;
    private String myUserIdColumn;


    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
        myMapListDAO = Config.getInstance(filterConfig.getServletContext()).getMapListDAO();
        myPermissionsList = filterConfig.getInitParameter("query.permission.list");
        myRemoteUserColumn = filterConfig.getInitParameter("remote.user.column");
        myUserIdColumn = filterConfig.getInitParameter("user.id.column");
    }

    @Override
    public void destroy() {
        // TODO ...?
    }

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

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        String queryName = request.getParameter("queryName");
        // because I have SpnegoHttpFilter earlier in my filter chain
        // this returns the NT User ID (what the user logged in to the domain with)
        String remoteUser = httpRequest.getRemoteUser();
        Map<String, Object> queryPermissions = myMapListDAO.getEntry(myPermissionsList, myRemoteUserColumn, remoteUser);

        // if there is no queryName defined
        if (null == queryName) {
            httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, 
                    "Missing queryName parameter.");
            return;
        }

        // if this query is protected perform the gauntlet
        if (myMapListDAO.getList(myPermissionsList).get(0).containsKey(queryName)) {

            // if there is no remoteUser
            if (null == remoteUser || remoteUser.isEmpty()) {
                httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, 
                        "Cannot get remoteUser.");
                return;
            }

            // if the remoteUser does not have any queryPermissions
            if (null == queryPermissions) {
                httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, 
                        "Cannot find queryPermissions for " + remoteUser + ".");
                return;
            }

            // if this remoteUser does not have permission to execute the queryName
            if ((Boolean) queryPermissions.get(queryName)) {
                httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, 
                        "The remoteUser: " + remoteUser + " does not have permission to access queryName: " + queryName + ".");
                return;
            }

        }

        // attempt to add the userId to this request as an attribute we can get later
        if (null != queryPermissions) {
            httpRequest.setAttribute("userId", String.valueOf(queryPermissions.get(myUserIdColumn)));
        }

        // continue to servlet
        httpResponse.setStatus(HttpServletResponse.SC_OK);
        chain.doFilter(request, response);
    }

}

    // attempt to add the userId to this request as an attribute we can get later
    if (null != queryPermissions) {
        httpRequest.setAttribute("userId", String.valueOf(queryPermissions.get(myUserIdColumn)));
    }

    // continue to servlet
    httpResponse.setStatus(HttpServletResponse.SC_OK);
    chain.doFilter(request, response);
}

}

4

1 に答える 1

3

私のアプリは完全にイントラネット ベースであるため、セキュリティ プロトコルを完全に削除することになりました。

すべてのドメイン IP アドレスと、現在のユーザー ID、ログオン時刻、およびログオフ時刻の列を含むデータベース テーブルを作成しただけです。

ユーザーが Active Directory にログオンまたはログオフするたびにこのテーブルを更新するサーバー側のコードを書きました。

ここで、リモート アドレスを非常に簡単に取得できるので、次のようなサーブレット フィルターを作成しました。

  1. ユーザー ID の HttpSession 属性であるかどうかを確認します
  2. そうでない場合は、データベースにユーザー ID を照会し、それをセッション属性に格納します。
  3. ユーザーIDに基づいてカスタム認証を実行し、リクエストを拒否または通過します
  4. リクエストがフィルターを通過した場合、サーブレットはラップされた HttpRequest を送信し、getRemoteUser呼び出しがユーザー ID セッション属性を返すように強制します。

イントラネット上のユーザーが自分の IP アドレスを変更して他のユーザーをコピーすることは可能だと思いますが、試してみると、重複した IP アドレスが存在するというエラーが表示され、イントラネット上の何にも接続できませんでした。

更新 (3 か月後):

結局ワッフルにしました。統合は非常に簡単でした。上記の私の解決策は、いくつかの理由で実行できませんでした。

于 2012-05-01T16:14:27.370 に答える