2012-05-15 26 views
8

Java ve Spring 3'te oldukça yeni (çoğunlukla 8 yıldır kullanılan PHP). Ben tüm varsayılan userDetails ve userDetailsService çalışmak için bahar güvenliğini 3 halletmiş ve ben kullanarak bir denetleyici içinde kullanıcıya ait kullanıcı oturum erişebilirsiniz biliyorum:Spring Security: özel kullanıcı hesaplamaları

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

Ama iki sorun ben rakam yapamam vardır dışarı:

  1. diğer kullanıcı bilgileri bir sürü ben saklı edilmesini istediğiniz vardır zaman (vb DOB, cinsiyet, gibi) ve daha sonra kontrolörleri aracılığıyla erişilebilir şekilde bir kullanıcı günlükleri. Oluşturulan userDetails nesnesinin özel alanlarmı içerecek şekilde ne yapmam gerekiyor?

  2. Zaten "HttpSession session = request.getSession (true); benim denetleyicideki her yöntemimin üstünde. Oturum açmış kullanıcının userDetails'ını oturum açtıktan sonra bir oturumda saklamak mümkün mü? "Kimlik doğrulama auth = SecurityContextHolder.getContext(). GetAuthentication();" her yöntemin başında?

Güvenlik-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'ye:

<%@ 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" /> 

cevap

19

Bu soruda bir çok şey oluyor. Bunu parçalara ayırmaya çalışacağım ...

S # 1: Burada birkaç olası yaklaşım var.

Yaklaşım # 1: UserDetails nesnesine eklemek istediğiniz başka öznitelikleriniz varsa, ilgili öznitelikleri ve ayarlayıcılarla birlikte bu öznitelikleri içeren UserDetails arabiriminin kendi alternatif uygulamanızı sağlamalısınız. Bu, UserDetailsService arabiriminin kendi alternatif uygulamanızı da sağlamanızı gerektirir. Bu bileşen, bu ek özniteliklerin temel veri deposuna nasıl devam edeceğini anlamak zorundadır veya bu veri deposundan okurken, bu ek özniteliklerin nasıl doldurulacağını anlamak zorundadır. Öyle gibi tüm bu kadar Tel ediyorum:

<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> 

yaklaşımda # 2: ben, sen daha iyi alana özgü kullanıcı tutmak için hizmet ediyoruz (özellikle birkaç tekrarlamalar span üzerinden) bulabilirsiniz Beğen/hesap bilgilerini ayrı Spring Security belirli kullanıcı/hesap detaylarından. Bu sizin için geçerli olabilir veya olmayabilir. Ancak bu yaklaşımda herhangi bir bilgelik bulabilirseniz, o anda sahip olduğunuz kurulum ile devam edersiniz ve ek bir Kullanıcı/Hesap alan adı nesnesi, ilgili depo/DAO vb. Eklersiniz.Eğer etki alanına özgü kullanıcı/hesap almak istiyorsanız aşağıdaki gibi yapabilirsiniz:

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

S # 2: açık geçersiz kılmak için bazı önlemler aldık sürece Bahar Güvenlik otomatik (oturumdaki UserDetails saklar Bu davranış). Bu nedenle, kendi kontrol sisteminizin her birinde bunu yapmanıza gerek yoktur. Çalıştığınız SecurityContextHolder nesnesi, her isteğin başında Authentication nesnesi, UserDetails, vb. Dahil olmak üzere SecurityContext ile (SS tarafından) doldurulur. Bu içerik her isteğin sonunda silinir, ancak veriler her zaman oturumda kalır.

Bununla birlikte, bunu önlemek için bir Spring MVC denetleyicisinde HttpServletRequest, HttpSession nesneleri, vb. Ile uğraşmak için gerçekten harika bir uygulama değil. İlkbahar neredeyse her zaman gerek kalmadan bir şey elde etmek için daha temiz, daha deyimsel araçlar sunuyor. Bunun avantajı, denetleyici metot imzaları ve mantığın, bir birim testinde alay edilmesi zor olan şeylere (örn. HttpSession) bağımlı olması ve kendi etki alanı nesnelerine (veya bu etki alanı nesnelerine ait taslaklar/alaylara) bağımlı olmamasıdır.). Bu, kontrol cihazlarınızın test edilebilirliğini büyük ölçüde artırır ve böylece denetleyicilerinizi gerçekten test etmeniz olasılığını artırır. Umarım bu biraz yardımcı olur.

+0

Teşekkür Kent, bu oldukça yardımcı oldu! Dağınıklık için özür dilerim, ilk kez Baharı kodluyorum. 2 numaralı yaklaşımı severim, her bir yöntemin başlangıcında bu kod satırını nereye koyarım? Ve bu soruya dayanarak tahmin ediyorum, eğer bu 2. yaklaşımla gidersem kullanıcı nesnesini saklamak mümkün olmaz. Yeni bir kullanıcı nesnesi oluşturup her seferinde aynı veri için db'ye gitmeli mi? – Felix

+1

Cevap, "bağlıdır". 2 numaralı yaklaşımla, bir kullanıcının hesabına çok nadiren erişmem gerekiyor (bir alan perspektifinden). Belki de sadece "profillerini" görüntüleme veya düzenleme amacıyla erişirim. Bu durumda, gerektiğinde bu bilgi için veri deposunu okurum. Bununla birlikte, kullanıcının özniteliklerine sık sık erişmeniz gerekiyorsa (etki alanı perspektifinden, belki de kullanıcı nesnesinin bazı özniteliğinin üstbilgide görünmesi gerekir ve bu nedenle her istekte gerekir), muhtemelen yeniden gözden geçirmeniz gerekir yaklaşım # 1. –

+1

Teşekkür Kent. Hibrit bir çözüm geliştirdim. Giriş yapmış olan kullanıcıya ihtiyacım olduğunda, verileri elde etmek için bir sarmalayıcı denetleyicisinde bir yöntem çalıştırıyorum. Yöntem, oturumun bir kullanıcı nesnesi içerip içermediğini kontrol eder ve eğer yaparsa, kullanıcının kullanıcı adı, SecurityContext kullanıcı adının değerine eşit olup olmadığını kontrol eder. Değilse (veya oturum kullanıcısı nesnesi boşsa), kullanıcı verilerini veritabanından alır ve nesneyi oturum halinde kaydeder. İki ekstra if ifadesi, ancak 1 daha az veritabanı çağrısı. – Felix

1

doğrudan oturumu erişme biraz dağınık olduğunu ve hata eğilimli olabilir. Örneğin, kullanıcı bir hatırlatmayı içermeyen hatırlama-me ya da başka bir mekanizma kullanılarak doğrulanırsa, bu istek tamamlanıncaya kadar oturum doldurulmaz.

Aramaları SecurityContextHolder'a sarmak için özel bir erişim arabirimi kullanırdım. Bakınız my answer to this related question.

+0

İyi çağrı, oturumları kullandığımdan beri biraz zaman geçti. – Felix

2

Benim düşünceme göre, Custom UserDetails uygulaması harika fakat sadece kullanıcılarınızın değişmez özellikleri için kullanılmalıdır.

Özel Kullanıcı nesnesiniz UserDetails'i geçersiz kılınca, kolayca değiştirilemez. Değiştirilmiş ayrıntılarla yepyeni bir kimlik doğrulama nesnesi oluşturmanız ve yalnızca değiştirilmiş UserDetails nesnesini güvenlik bağlamına geri yükleyemezsiniz.

Yapmakta olduğum uygulamada bunu fark ettim ve her isteğiyle değiştiren kullanıcıyla ilgili başarılı kimlik doğrulama ayrıntıları üzerine (ancak db'den yeniden yüklemek istemediğim için) yeniden deneme yapmak zorundaydım Her sayfa yükü) ayrı ayrı oturumda tutulmalıdır, ancak yine de bir kimlik doğrulama kontrolünden sonra sadece erişilebilir/değiştirilebilir.

Bu WebArgumentResolver öğesinin https://stackoverflow.com/a/8769670/1411545 numaralı belgede belirtilen durumunun daha iyi bir çözüm olup olmadığını anlamaya çalışıyorum.

İlgili konular