j_security_checkを使用したJava EE / JSFでのユーザー認証の実行


156

JPAにユーザー情報を保持するJSF 2.0(およびコンポーネントが存在する場合)とJava EE 6コアメカニズム(ログイン/権限のチェック/ログアウト)を使用するWebアプリケーションのユーザー認証に関する現在のアプローチは何ですか?エンティティ。Oracle Java EEチュートリアルは、これについては少し疎です(サーブレットのみを処理します)。

これは、Spring-Security(acegi)やSeamのような他のフレームワーク全体を利用せずに、できれば新しいJava EE 6プラットフォーム(webプロファイル)にこだわることを試みます。

回答:


85

Webを検索してさまざまな方法を試した後、Java EE 6認証について以下のことを提案します。

セキュリティレルムを設定します。

私の場合、データベースにユーザーがいました。そこで、このブログ投稿に従って、データベーステーブルのユーザー名とMD5ハッシュされたパスワードに基づいてユーザーを認証できるJDBCレルムを作成しました。

http://blog.gamatam.com/2009/11/jdbc-realm-setup-with-glassfish-v3.html

注:この投稿では、ユーザーとデータベース内のグループテーブルについて説明しています。データベースにjavax.persistenceアノテーションを介してマッピングされたUserType enum属性を持つUserクラスがありました。userType列をグループ列として使用して、ユーザーとグループに同じテーブルを使用してレルムを構成しましたが、正常に機能しました。

フォーム認証を使用する:

上記のブログ投稿に従って、web.xmlとsun-web.xmlを構成しますが、BASIC認証を使用する代わりに、FORMを使用します(実際には、どちらを使用してもかまいませんが、結局はFORMを使用しました)。JSFではなく、標準のHTMLを使用してください。

次に、上記のBalusCのヒントを使用して、データベースからユーザー情報を遅延初期化します。彼は、facesコンテキストからプリンシパルを取得するマネージドBeanでそれを行うことを提案しました。代わりに、各ユーザーのセッション情報を格納するためにステートフルセッションBeanを使用したので、セッションコンテキストを挿入しました。

 @Resource
 private SessionContext sessionContext;

プリンシパルを使用して、ユーザー名を確認し、EJB Entity Managerを使用してデータベースからユーザー情報を取得し、SessionInformationEJBに保存します。

ログアウト:

ログアウトするための最良の方法も探しました。私が見つけた最高のものはサーブレットを使用することです:

 @WebServlet(name = "LogoutServlet", urlPatterns = {"/logout"})
 public class LogoutServlet extends HttpServlet {
  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   HttpSession session = request.getSession(false);

   // Destroys the session for this user.
   if (session != null)
        session.invalidate();

   // Redirects back to the initial page.
   response.sendRedirect(request.getContextPath());
  }
 }

質問の日付を考えると、私の答えは本当に遅いですが、私と同じように、これがGoogleからここに来る他の人々を助けることを願っています。

チャオ、

ビトールソウザ


15
ちょっとしたアドバイス:request.getSession(false)を使用して、それに対してinvalidate()を呼び出しています。セッションがない場合、request.getSession(false)はnullを返すことがあります。最初にnullかどうかを確認することをお
勧めし

@Vitor:こんにちは。コンテナベースのセキュリティからshiroや他のセキュリティなどの代替手段に移行するのが良い時期について何か言いたいですか?ここでは、より焦点を当てた質問を参照してください:stackoverflow.com/questions/7782720/...
ラジャット・グプタ

Glassfish JDBCレルムは、ソルトパスワードハッシュの保存をサポートしていないようです。その場合にそれを使用するのは本当にベストプラクティスですか?
Lii

申し訳ありません、あなたを助けることはできません。私はGlassfishの専門家ではありません。たぶん、新しいスレッドでその質問をして、人々が言うことを見てみませんか?
ビトー・E.・シルバ・ソウザ

1
Lii、グラスフィッシュコンテナーを使用して、塩漬けで作業できます。ハッシュを使用しないようにヘルスを構成します。パスワードに挿入するプレーンな値を比較します。これによりHttpServletResponse#login(user, password)、DBからユーザーのソルト、イテレーション、ソルティングに使用するすべてのものを取得し、そのソルトを使用してユーザーが入力したパスワードをハッシュし、コンテナーに認証を求めることができます。HttpServletResponse#login(user, password)
emportella 2014年

152

デプロイメント記述子と を使用したフォームベースの認証が必要だと思います。j_security_check

JSFでは、同じ事前定義済みフィールド名j_usernamej_password使用して、チュートリアルで示されているように、これを行うこともできます。

例えば

<form action="j_security_check" method="post">
    <h:outputLabel for="j_username" value="Username" />
    <h:inputText id="j_username" />
    <br />
    <h:outputLabel for="j_password" value="Password" />
    <h:inputSecret id="j_password" />
    <br />
    <h:commandButton value="Login" />
</form>

Usergetterで遅延読み込みを実行して、Userがすでにログインしているかどうかを確認し、ログインしていない場合は、Principalリクエストにが存在するかどうかを確認し、存在する場合はにUser関連付けられてj_usernameいるを取得します。

package com.stackoverflow.q2206911;

import java.io.IOException;
import java.security.Principal;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;

@ManagedBean
@SessionScoped
public class Auth {

    private User user; // The JPA entity.

    @EJB
    private UserService userService;

    public User getUser() {
        if (user == null) {
            Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
            if (principal != null) {
                user = userService.find(principal.getName()); // Find User by j_username.
            }
        }
        return user;
    }

}

User明らかによるJSF ELでアクセス可能です#{auth.user}

ログアウトするには、aを実行しますHttpServletRequest#logout()(そしてUsernullに設定します!)。のハンドルをHttpServletRequestJSF で取得できますExternalContext#getRequest()。セッションを完全に無効にすることもできます。

public String logout() {
    FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
    return "login?faces-redirect=true";
}

残りの部分(デプロイメント記述子とレルムでのユーザー、ロール、制約の定義)については、Java EE 6チュートリアルとservletcontainerのドキュメントに従って通常の方法に従ってください。


更新:一部のサーブレットコンテナでは、ディスパッチャから実際に到達できない場合があるHttpServletRequest#login()代わりに、新しいサーブレット3.0 を使用してプログラムによるログインを行うこともできますj_security_check。この場合、あなたはfullworthy JSFフォームとと豆使用することができますusernameし、passwordプロパティとlogin、このような方法を見て:

<h:form>
    <h:outputLabel for="username" value="Username" />
    <h:inputText id="username" value="#{auth.username}" required="true" />
    <h:message for="username" />
    <br />
    <h:outputLabel for="password" value="Password" />
    <h:inputSecret id="password" value="#{auth.password}" required="true" />
    <h:message for="password" />
    <br />
    <h:commandButton value="Login" action="#{auth.login}" />
    <h:messages globalOnly="true" />
</h:form>

そして、このビューは、最初にリクエストされたページを記憶するマネージドBeanをスコープとしました

@ManagedBean
@ViewScoped
public class Auth {

    private String username;
    private String password;
    private String originalURL;

    @PostConstruct
    public void init() {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        originalURL = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);

        if (originalURL == null) {
            originalURL = externalContext.getRequestContextPath() + "/home.xhtml";
        } else {
            String originalQuery = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_QUERY_STRING);

            if (originalQuery != null) {
                originalURL += "?" + originalQuery;
            }
        }
    }

    @EJB
    private UserService userService;

    public void login() throws IOException {
        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext externalContext = context.getExternalContext();
        HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();

        try {
            request.login(username, password);
            User user = userService.find(username, password);
            externalContext.getSessionMap().put("user", user);
            externalContext.redirect(originalURL);
        } catch (ServletException e) {
            // Handle unknown username/password in request.login().
            context.addMessage(null, new FacesMessage("Unknown login"));
        }
    }

    public void logout() throws IOException {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        externalContext.invalidateSession();
        externalContext.redirect(externalContext.getRequestContextPath() + "/login.xhtml");
    }

    // Getters/setters for username and password.
}

このようにして、UserはJSF ELでにアクセスできます#{user}


1
へのディスパッチj_security_checkがすべてのサーブレットコンテナで機能しない可能性があるという免責事項を含めるように質問を更新しました。
BalusC 2010

1
Webアプリケーションでのプログラムによるセキュリティの使用に関するJavaチュートリアルからのリンク:java.sun.com/javaee/6/docs/tutorial/doc/gjiie.html(サーブレットを使用):使用できるサーブレットクラス: @WebServlet(name="testServlet", urlPatterns={"/ testServlet "}) @ServletSecurity(@HttpConstraint(rolesAllowed = {"testUser", "admin”})) およびメソッドごとレベル: @ServletSecurity(httpMethodConstraints={ @HttpMethodConstraint("GET"), @HttpMethodConstraint(value="POST", rolesAllowed={"testUser"})})
ngeek

3
そしてあなたのポイントは..?これがJSFに適用できるかどうか。JSFにはサーブレットが1つしかFacesServletありません。サーブレットを変更することはできません(変更したくありません)。
BalusC 2010

1
@BalusC-上記が最良の方法だと言うとき、j_security_checkまたはプログラムによるログインを使用することを意味しますか?
simgineer

3
@simgineer:リクエストされたURLは、で定義された名前のリクエスト属性として使用できますRequestDispatcher.FORWARD_REQUEST_URI。リクエスト属性はJSFにあり、で利用できますExternalContext#getRequestMap()
BalusC 2012年

7

これは、認証の問題を完全にフロントコントローラー(Apache Webサーバーなど)に任せて、代わりにREMOTE_USER環境変数のJAVA表現であるHttpServletRequest.getRemoteUser()を評価するオプションであることを述べておく必要があります。これにより、Shibboleth認証などの高度なログイン設計も可能になります。Webサーバーを介したサーブレットコンテナへのリクエストのフィルタリングは、本番環境に適した設計であり、多くの場合、mod_jkを使用して行われます。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.