2012-05-15 14 views
8

मैं जावा और वसंत 3 के लिए काफी नया हूं (मुख्य रूप से पिछले 8 वर्षों में PHP का उपयोग किया जाता है)। मैं सभी डिफ़ॉल्ट userDetails और userDetailsService साथ काम करने के वसंत सुरक्षा 3 मिल गया है और मुझे लगता है मैं का उपयोग कर एक नियंत्रक में उपयोगकर्ता के उपयोगकर्ता नाम में लॉग इन का उपयोग कर सकते पता है:वसंत सुरक्षा: कस्टम उपयोगकर्ता विवरण

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

लेकिन दो समस्याओं मैं समझ नहीं सकता देखते हैं आउट:

  1. वहाँ अन्य उपयोगकर्ता जानकारी का एक बहुत मैं संग्रहीत करना चाहते हैं, जब (जैसे जन्म तिथि, लिंग, आदि के रूप में) और बाद में नियंत्रक के माध्यम से सुलभ होने के लिए किसी उपयोगकर्ता के लॉग। मुझे ऐसा करने की क्या ज़रूरत है ताकि उपयोगकर्ता द्वारा निर्मित ऑब्जेक्ट डिस्प्ले ऑब्जेक्ट में मेरे कस्टम फ़ील्ड हों?

  2. मैं पहले से ही "Http सत्र सत्र = request.getSession (सत्य)" कह रहा हूं; " मेरे नियंत्रक में मेरे प्रत्येक तरीके के शीर्ष पर। लॉग इन पर लॉग इन करने के लिए उपयोगकर्ता के उपयोगकर्ता में लॉग इन करना संभव है ताकि मुझे "प्रमाणीकरण auth = SecurityContextHolder.getContext() को कॉल करने की आवश्यकता न हो। प्रमाणीकरण();" हर विधि की शुरुआत में?

सुरक्षा-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" /> 

उत्तर

19

इस प्रश्न में बहुत कुछ चल रहा है। मैं इसे टुकड़ों में संबोधित करने की कोशिश करूंगा ...

प्रश्न # 1: यहां कुछ संभावित दृष्टिकोण हैं।

दृष्टिकोण # 1: यदि आपके पास अन्य उपयोगकर्ता विशेषताएं हैं जो आप अपने उपयोगकर्ता विवरण ऑब्जेक्ट में जोड़ना चाहते हैं, तो आपको उपयोगकर्ता विवरण इंटरफ़ेस का अपना वैकल्पिक कार्यान्वयन प्रदान करना चाहिए जिसमें संबंधित गुणक और सेटर्स के साथ उन विशेषताओं को शामिल किया गया हो। यह आवश्यक होगा कि आप UserDetailsService इंटरफ़ेस का अपना वैकल्पिक कार्यान्वयन भी प्रदान करें। इस घटक को समझना होगा कि इन अतिरिक्त विशेषताओं को अंतर्निहित डेटास्टोर में कैसे बनाए रखना है, या उस डेटास्टोर से पढ़ने पर, उन्हें समझना होगा कि उन अतिरिक्त विशेषताओं को कैसे पॉप्युलेट करना है। तुम इतनी तरह इस सब के ऊपर तार चाहते हैं:

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

Approache # 2: की तरह मुझे, आपको लगता है कि आप बेहतर डोमेन-विशिष्ट उपयोगकर्ता रखने के लिए सेवा की कर रहे हैं (विशेष रूप से कई पुनरावृत्तियों के अंतराल पर) मिल सकता है/खाता विवरण वसंत सुरक्षा विशिष्ट उपयोगकर्ता/खाता विवरण से अलग अलग। यह आपके लिए मामला हो सकता है या नहीं भी हो सकता है। लेकिन अगर आप इस दृष्टिकोण में कोई ज्ञान पा सकते हैं, तो आप वर्तमान में अपने सेटअप के साथ चिपके रहेंगे और एक अतिरिक्त उपयोगकर्ता/खाता डोमेन ऑब्जेक्ट, संबंधित भंडार/डीएओ इत्यादि जोड़ देंगे।आप डोमेन-विशिष्ट उपयोगकर्ता/खाते प्राप्त करना चाहते हैं, तो आप ऐसा इस प्रकार कर सकते हैं:

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

क्यू # 2: वसंत सुरक्षा स्वचालित रूप से सत्र में UserDetails संग्रहीत करता है (जब तक आप स्पष्ट ओवरराइड करने के लिए कदम उठाए हैं वह व्यवहार)। इसलिए आपके प्रत्येक नियंत्रक विधियों में इसे स्वयं करने की आवश्यकता नहीं है। सुरक्षा कॉन्टेक्स्टहोल्डर ऑब्जेक्ट जो आप से निपट रहे हैं वास्तव में प्रत्येक अनुरोध की शुरुआत में प्रमाणीकरण ऑब्जेक्ट, उपयोगकर्ता विवरण आदि सहित सुरक्षा कॉन्टेक्स्ट के साथ आबादी (एसएस द्वारा) है। प्रत्येक संदर्भ के अंत में यह संदर्भ साफ़ कर दिया गया है, लेकिन डेटा हमेशा सत्र में रहता है।

हालांकि, यह ध्यान देने योग्य है कि स्प्रिंग एमवीसी नियंत्रक में HttpServletRequest, HttpSession ऑब्जेक्ट्स आदि से निपटने के लिए यह वास्तव में एक अच्छा अभ्यास नहीं है यदि आप इससे बच सकते हैं। वसंत लगभग हमेशा के बिना चीजों को प्राप्त करने के लिए क्लीनर, अधिक मूर्खतापूर्ण साधन प्रदान करता है। इसका लाभ यह होगा कि नियंत्रक विधि हस्ताक्षर और तर्क उन चीजों पर निर्भर रहना बंद कर देते हैं जो यूनिट परीक्षण (उदाहरण के लिए एचटीपीएसशन) में नकल करना मुश्किल है और अपने डोमेन ऑब्जेक्ट्स पर निर्भर होने के बजाय (या उन डोमेन ऑब्जेक्ट्स के स्टब्स/मैक्स)। यह आपके नियंत्रकों की टेस्टेबिलिटी को काफी हद तक बढ़ाता है ... और इस प्रकार संभावना है कि आप वास्तव में अपने नियंत्रकों का परीक्षण करेंगे। :)

उम्मीद है कि इससे कुछ मदद मिलती है।

+0

धन्यवाद केंट, यह काफी मददगार था! गड़बड़ी के लिए खेद है, पहली बार वसंत कोडिंग। मुझे दृष्टिकोण # 2 पसंद है, मैं प्रत्येक विधि की शुरुआत में कोड की उस पंक्ति को कहां रखूंगा? और मुझे लगता है कि उस प्रश्न के आधार पर, यदि मैं उस दूसरे दृष्टिकोण के साथ जाता हूं तो उपयोगकर्ता ऑब्जेक्ट को स्टोर करना संभव है ताकि मैं ' टी को एक नया उपयोगकर्ता ऑब्जेक्ट बनाने की आवश्यकता है और प्रत्येक बार एक ही डेटा के लिए डीबी पर जाना है? – Felix

+1

उत्तर है, "यह निर्भर करता है।" मुझे लगता है कि दृष्टिकोण # 2 के साथ, मुझे शायद ही कभी किसी उपयोगकर्ता के खाते (डोमेन परिप्रेक्ष्य से) तक पहुंचने की आवश्यकता होती है। हो सकता है कि मैं इसे केवल "प्रोफ़ाइल" प्रदर्शित करने या संपादित करने के प्रयोजनों के लिए एक्सेस करूं। यदि ऐसा है, तो जब आवश्यक हो तो मैं इस जानकारी के लिए डेटास्टोर को पढ़ूंगा। यदि, हालांकि, आपको अक्सर उपयोगकर्ता के गुणों (डोमेन परिप्रेक्ष्य से) तक पहुंचने की आवश्यकता होती है; शायद उपयोगकर्ता ऑब्जेक्ट की कुछ विशेषता शीर्षलेख में दिखाई देने की आवश्यकता होती है, और इसलिए प्रत्येक अनुरोध में इसकी आवश्यकता होती है), तो आपको शायद पुनर्विचार करना चाहिए दृष्टिकोण # 1। –

+1

धन्यवाद केंट। मैं एक संकर प्रकार समाधान कर समाप्त हो गया। जब मुझे लॉग इन उपयोगकर्ता की आवश्यकता होती है तो मैं डेटा प्राप्त करने के लिए एक रैपर नियंत्रक में एक विधि चलाता हूं। विधि यह देखने के लिए जांचती है कि सत्र में उपयोगकर्ता ऑब्जेक्ट है या नहीं, और यदि यह जांचता है कि ऑब्जेक्ट का उपयोगकर्ता नाम सुरक्षा कॉन्टेक्स्ट उपयोगकर्ता नाम के बराबर है या नहीं। यदि नहीं (या यदि सत्र उपयोगकर्ता ऑब्जेक्ट शून्य है) तो यह डेटाबेस से उपयोगकर्ता डेटा प्राप्त करता है और ऑब्जेक्ट को सत्र में संग्रहीत करता है। कथन दो अतिरिक्त, लेकिन 1 कम डेटाबेस कॉल। – Felix

1

सत्र सीधे एक्सेस करना थोड़ा गंदा है, और त्रुटियों की संभावना हो सकती है। उदाहरण के लिए, यदि उपयोगकर्ता को याद रखने वाले या मुझे कुछ अन्य तंत्र का उपयोग करके प्रमाणित किया गया है जिसमें रीडायरेक्ट शामिल नहीं है, तो सत्र पूरा होने तक सत्र पॉप्युलेट नहीं किया जाएगा।

मैं SecurityContextHolder पर कॉल को लपेटने के लिए कस्टम एक्सेसर इंटरफ़ेस का उपयोग करूंगा। my answer to this related question देखें।

+0

अच्छी कॉल, यह कुछ समय हो गया है क्योंकि मैंने सत्रों का उपयोग किया है। – Felix

2

मेरी राय में, कस्टम उपयोगकर्ता विवरण कार्यान्वयन बहुत अच्छा है, लेकिन केवल आपके उपयोगकर्ता की अपरिवर्तनीय विशेषताओं के लिए उपयोग किया जाना चाहिए।

एक बार आपका कस्टम उपयोगकर्ता ऑब्जेक्ट उपयोगकर्ता विवरण को ओवरराइड करता है, तो यह आसानी से बदला नहीं जाता है। आपको संशोधित विवरणों के साथ एक पूरी नई प्रमाणीकरण वस्तु बनाना है और केवल संशोधित उपयोगकर्ता डिस्प्ले ऑब्जेक्ट को सुरक्षा संदर्भ में चिपका नहीं सकता है।

मैं जिस एप्लिकेशन को बना रहा हूं, उसमें मुझे एहसास हुआ है और इसे फिर से खोजना है ताकि प्रत्येक अनुरोध के साथ बदल रहे उपयोगकर्ता के बारे में सफल प्रमाणीकरण विवरण (लेकिन मैं डीबी से पुनः लोड नहीं करना चाहता प्रत्येक पृष्ठ लोड) को अलग-अलग सत्र में रखा जाना आवश्यक है, लेकिन प्रमाणीकरण जांच के बाद भी अभी भी सुलभ/परिवर्तनीय है।

यह पता लगाने की कोशिश कर रहा है कि https://stackoverflow.com/a/8769670/1411545 में वर्णित यह WebArgumentResolver मेरी स्थिति के लिए एक बेहतर समाधान है।

संबंधित मुद्दे