10

私はJavaとSpring 3にかなり慣れていません(過去8年間、主にPHPを使用していました)。Spring Security 3 をすべてのデフォルトの userDetails および userDetailsS​​ervice で動作するようにしました。次を使用して、コントローラーでログインしているユーザーのユーザー名にアクセスできることを知っています。

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName(); //get logged in username

しかし、私が理解できない2つの問題があります:

  1. ユーザーがログインしたときに保存したい他の多くのユーザーの詳細 (生年月日、性別など) があり、後でコントローラーを介してアクセスできるようにします。作成された userDetails オブジェクトにカスタム フィールドが含まれるようにするには、どうすればよいですか?

  2. 私はすでに「HttpSession session = request.getSession(true);」を呼び出しています。コントローラーの各メソッドの上部にあります。「Authentication auth = SecurityContextHolder.getContext().getAuthentication();」も呼び出す必要がないように、ログイン時にログインしたユーザーの userDetails をセッションに保存することは可能ですか? すべてのメソッドの最初に?

Security-applicationContext.xml:

<global-method-security secured-annotations="enabled"></global-method-security>     
<http auto-config='true' access-denied-page="/access-denied.html">
    <!-- NO RESTRICTIONS -->        
    <intercept-url pattern="/login.html" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    <intercept-url pattern="/*.html" access="IS_AUTHENTICATED_ANONYMOUSLY"  /> 
    <!-- RESTRICTED PAGES -->
    <intercept-url pattern="/admin/*.html" access="ROLE_ADMIN" />
    <intercept-url pattern="/member/*.html" access="ROLE_ADMIN, ROLE_STAFF" />

    <form-login login-page="/login.html"
                login-processing-url="/loginProcess"
                authentication-failure-url="/login.html?login_error=1"
                default-target-url="/member/home.html" />
    <logout logout-success-url="/login.html"/>
</http>

<authentication-manager>
    <authentication-provider>
        <jdbc-user-service data-source-ref="dataSource" authorities-by-username-query="SELECT U.username, UR.authority, U.userid FROM users U, userroles UR WHERE U.username=? AND U.roleid=UR.roleid LIMIT 1" />
        <password-encoder hash="md5"/>
    </authentication-provider>
</authentication-manager>

login.jsp:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>

<tiles:insertDefinition name="header" />
<tiles:insertDefinition name="menu" />
<tiles:insertDefinition name="prebody" />

<h1>Login</h1>

<c:if test="${not empty param.login_error}">
    <font color="red"><c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>.<br /><br /></font>
</c:if>
<form name="f" action="<c:url value='/loginProcess'/>" method="POST">
    <table>
        <tr><td>User:</td><td><input type='text' name='j_username' value='<c:if test="${not empty param.login_error}"><c:out value="${SPRING_SECURITY_LAST_USERNAME}"/></c:if>' /></td></tr>
            <tr><td>Password:</td><td><input type='password' name='j_password' /></td></tr>
            <tr><td>&nbsp;</td><td><input type="checkbox" name="_spring_security_remember_me" /> Remember Me</td></tr>
            <tr><td>&nbsp;</td><td><input name="submit" type="submit" value="Login" /></td></tr>
        </table>
    </form>

<tiles:insertDefinition name="postbody" />
<tiles:insertDefinition name="footer" />
4

3 に答える 3

20

この質問には非常に多くのことが起こっています。バラバラに対処しようと思います...

Q#1: ここにはいくつかの可能なアプローチがあります。

アプローチ #1: UserDetails オブジェクトに追加したい他の属性がある場合は、それらの属性と対応する getter および setter を含む UserDetails インターフェイスの独自の代替実装を提供する必要があります。これには、UserDetailsS​​ervice インターフェースの独自の代替実装も提供する必要があります。このコンポーネントは、これらの追加の属性を基礎となるデータストアに保持する方法を理解する必要があります。または、そのデータストアから読み取るときに、これらの追加の属性を設定する方法を理解する必要があります。このすべてを次のように配線します。

<beans:bean id="userDetailsService" class="com.example.MyCustomeUserDetailsService">
<!-- ... -->
</beans:bean>

<authentication-manager alias="authenticationManager">
    <authentication-provider ref="authenticationProvider"/>
</authentication-manager>

<beans:bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
    <beans:property name="userDetailsService" ref="userDetailsService"/>
</beans:bean>

アプローチ #2: 私のように、ドメイン固有のユーザー/アカウントの詳細を Spring Security 固有のユーザー/アカウントの詳細から分離しておく方が良いことに気付くかもしれません (特に数回の反復のスパンで) 。これは、あなたに当てはまる場合とそうでない場合があります。しかし、このアプローチで知恵を見つけることができれば、現在の設定をそのままにして、追加のユーザー/アカウント ドメイン オブジェクト、対応するリポジトリ/DAO などを追加します。ドメイン固有のユーザー/アカウントでは、次のように行うことができます。

User user = userDao.getByUsername(SecurityContextHolder.getContext().getAuthentication().getName());

Q#2: Spring Security は UserDetails をセッションに自動的に保存します (その動作をオーバーライドするための手順を明示的に行っていない場合)。したがって、各コントローラー メソッドでこれを自分で行う必要はありません。扱ってきた SecurityContextHolder オブジェクトには、実際には (SS によって) SecurityContext が読み込まれます。これには、すべての要求の開始時に、Authentication オブジェクト、UserDetails などが含まれます。このコンテキストは各リクエストの最後にクリアされますが、データは常にセッションに残ります。

ただし、回避できる場合、Spring MVC コントローラーで HttpServletRequest や HttpSession オブジェクトなどを処理することは、あまり良い方法ではないことに注意してください。Spring は、ほとんどの場合、そうする必要なく物事を達成するための、よりクリーンで慣用的な手段を提供します。その利点は、コントローラー メソッドのシグネチャとロジックが、単体テストでモックするのが難しいもの (HttpSession など) に依存しなくなり、独自のドメイン オブジェクト (またはそれらのドメイン オブジェクトのスタブ/モック) に依存する代わりになることです。 )。これにより、コントローラーのテスト容易性が大幅に向上し、実際にコントローラーをテストする可能性が高くなります。:)

これが役立つことを願っています。

于 2012-05-15T20:26:02.763 に答える
2

私の意見では、カスタム UserDetails の実装は優れていますが、ユーザーの不変の特性にのみ使用する必要があります。

カスタム User オブジェクトが UserDetails をオーバーライドすると、簡単には変更できません。変更された詳細を使用してまったく新しい認証オブジェクトを作成する必要があり、変更された UserDetails オブジェクトをセキュリティ コンテキストに戻すことはできません。

私が構築しているアプリケーションでは、これに気づき、再構築する必要があったため、認証が成功すると、リクエストごとに変化するユーザーに関する詳細が表示されます(ただし、ページの読み込みごとにデータベースからリロードしたくない)個別にセッションに保持する必要がありますが、認証チェック後にのみアクセス/変更可能です。

https://stackoverflow.com/a/8769670/1411545に記載されているこの WebArgumentResolverが私の状況にとってより良い解決策であるかどうかを調べようとしています。

于 2012-05-23T02:28:25.393 に答える
1

セッションに直接アクセスするのは少し面倒で、エラーが発生しやすい可能性があります。たとえば、remember-meまたはリダイレクトを伴わないその他のメカニズムを使用してユーザーが認証された場合、その要求が完了するまでセッションにデータが入力されません。

カスタムアクセサーインターフェイスを使用して、への呼び出しをラップしますSecurityContextHolderこの関連する質問に対する私の答えを参照してください。

于 2012-05-15T20:10:32.747 に答える