सक्रिय उपयोगकर्ता का उपयोगकर्ता नाम कैसे प्राप्त करें


170

अपने नियंत्रकों में, जब मुझे सक्रिय (लॉग इन) उपयोगकर्ता की आवश्यकता होती है, तो मैं अपना UserDetailsकार्यान्वयन प्राप्त करने के लिए निम्नलिखित कर रहा हूं :

User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());

यह ठीक काम करता है, लेकिन मुझे लगता है कि स्प्रिंग इस तरह के मामले में जीवन को आसान बना सकता है। वहाँ UserDetailsनियंत्रक या विधि में autowired है करने के लिए एक रास्ता है ?

उदाहरण के लिए, कुछ इस तरह:

public ModelAndView someRequestHandler(Principal principal) { ... }

लेकिन UsernamePasswordAuthenticationTokenमैं पाने के UserDetailsबजाय , मैं एक जगह मिलता है ?

मैं एक सुंदर समाधान की तलाश में हूं। कोई विचार?

जवाबों:


226

प्रस्तावना: वसंत-सुरक्षा 3.2 के बाद @AuthenticationPrincipalसे इस उत्तर के अंत में वर्णित एक अच्छा एनोटेशन है । जब आप स्प्रिंग-सिक्योरिटी> = 3.2 का उपयोग करते हैं तो यह सबसे अच्छा तरीका है।

जब आप:

  • वसंत-सुरक्षा के पुराने संस्करण का उपयोग करें,
  • प्रिंसिपल या में संग्रहीत कुछ जानकारी (जैसे लॉगिन या आईडी) द्वारा अपने कस्टम उपयोगकर्ता ऑब्जेक्ट को डेटाबेस से लोड करने की आवश्यकता है
  • कैसे एक सीखना चाहते हैं HandlerMethodArgumentResolverया WebArgumentResolverएक सुरुचिपूर्ण तरीके से इस हल कर सकते हैं, या सिर्फ एक के पीछे की पृष्ठभूमि सीखना चाहते हैं @AuthenticationPrincipalऔर AuthenticationPrincipalArgumentResolver(क्योंकि यह एक पर आधारित है HandlerMethodArgumentResolver)

फिर पढ़ते रहें - अन्यथा बस उपयोग करें @AuthenticationPrincipalऔर रोब विंच (लेखक @AuthenticationPrincipal) और लुकास श्मेलज़ेसेन (उनके उत्तर के लिए ) को धन्यवाद दें ।

(बीटीडब्ल्यू: मेरा उत्तर थोड़ा पुराना है (जनवरी 2012), इसलिए यह लुकास श्मेलज़ेसेन था जो @AuthenticationPrincipalस्प्रिंग सिक्योरिटी 3.2 पर एनोटेशन सॉल्यूशन बेस के साथ पहले के रूप में आया ।)


फिर आप अपने कंट्रोलर में उपयोग कर सकते हैं

public ModelAndView someRequestHandler(Principal principal) {
   User activeUser = (User) ((Authentication) principal).getPrincipal();
   ...
}

अगर एक बार जरूरत हो तो ठीक है। लेकिन अगर आपको कई बार इसकी बदसूरत ज़रूरत होती है, क्योंकि यह आपके नियंत्रक को बुनियादी ढांचे के विवरण के साथ प्रदूषित करता है, जो सामान्य रूप से रूपरेखा द्वारा छिपा होना चाहिए।

तो क्या आप वास्तव में चाहते हैं कि इस तरह एक नियंत्रक हो सकता है:

public ModelAndView someRequestHandler(@ActiveUser User activeUser) {
   ...
}

इसलिए आपको केवल एक को लागू करने की आवश्यकता है WebArgumentResolver। इसकी एक विधि है

Object resolveArgument(MethodParameter methodParameter,
                   NativeWebRequest webRequest)
                   throws Exception

वेब अनुरोध (दूसरा पैरामीटर) प्राप्त करता है और Userयदि विधि तर्क (पहला पैरामीटर) के लिए जिम्मेदार महसूस करता है तो उसे वापस करना होगा ।

स्प्रिंग 3.1 के बाद से एक नई अवधारणा है HandlerMethodArgumentResolver। यदि आप स्प्रिंग 3.1+ का उपयोग करते हैं तो आपको इसका उपयोग करना चाहिए। (यह इस उत्तर के अगले भाग में वर्णित है)

public class CurrentUserWebArgumentResolver implements WebArgumentResolver{

   Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {
        if(methodParameter is for type User && methodParameter is annotated with @ActiveUser) {
           Principal principal = webRequest.getUserPrincipal();
           return (User) ((Authentication) principal).getPrincipal();
        } else {
           return WebArgumentResolver.UNRESOLVED;
        }
   }
}

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

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ActiveUser {}

कॉन्फ़िगरेशन में आपको केवल इसे जोड़ना होगा:

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"
    id="applicationConversionService">
    <property name="customArgumentResolver">
        <bean class="CurrentUserWebArgumentResolver"/>
    </property>
</bean>

@See: स्प्रिंग MVC @Controller विधि तर्कों को अनुकूलित करना सीखें

यह ध्यान दिया जाना चाहिए कि यदि आप स्प्रिंग 3.1 का उपयोग कर रहे हैं, तो वे WebArgumentResolver पर HandlerMethodArgumentResolver की सलाह देते हैं। - जय द्वारा टिप्पणी देखें


HandlerMethodArgumentResolverस्प्रिंग 3.1+ के साथ भी ऐसा ही है

public class CurrentUserHandlerMethodArgumentResolver
                               implements HandlerMethodArgumentResolver {

     @Override
     public boolean supportsParameter(MethodParameter methodParameter) {
          return
              methodParameter.getParameterAnnotation(ActiveUser.class) != null
              && methodParameter.getParameterType().equals(User.class);
     }

     @Override
     public Object resolveArgument(MethodParameter methodParameter,
                         ModelAndViewContainer mavContainer,
                         NativeWebRequest webRequest,
                         WebDataBinderFactory binderFactory) throws Exception {

          if (this.supportsParameter(methodParameter)) {
              Principal principal = webRequest.getUserPrincipal();
              return (User) ((Authentication) principal).getPrincipal();
          } else {
              return WebArgumentResolver.UNRESOLVED;
          }
     }
}

कॉन्फ़िगरेशन में, आपको इसे जोड़ने की आवश्यकता है

<mvc:annotation-driven>
      <mvc:argument-resolvers>
           <bean class="CurrentUserHandlerMethodArgumentResolver"/>         
      </mvc:argument-resolvers>
 </mvc:annotation-driven>

@ एसई लीवरिंग स्प्रिंग एमवीसी 3.1 हैंडलरमैथोडऑर्गगुएशनओलवर इंटरफ़ेस


वसंत-सुरक्षा 3.2 समाधान

स्प्रिंग सिक्योरिटी 3.2 (स्प्रिंग 3.2 के साथ भ्रमित न करें) समाधान में स्वयं का निर्माण है: @AuthenticationPrincipal( org.springframework.security.web.bind.annotation.AuthenticationPrincipal)। यह लुकास शमेलज़ेसेन के उत्तर में अच्छी तरह से वर्णित है

यह सिर्फ लिख रहा है

ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) {
    ...
 }

इस कार्य को प्राप्त करने के लिए आपको AuthenticationPrincipalArgumentResolver( org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver): "एक्टीवेटिंग" @EnableWebMvcSecurityकरके या इस बीन को भीतर रजिस्टर करके रजिस्टर करने की आवश्यकता है mvc:argument-resolvers- उसी तरह जैसा मैंने ऊपर स्प्रिंग 3.1 समाधान के साथ वर्णित किया है।

@See स्प्रिंग सुरक्षा 3.2 संदर्भ, अध्याय 11.2। @AuthenticationPrincipal


वसंत-सुरक्षा 4.0 समाधान

यह स्प्रिंग 3.2 समाधान की तरह काम करता है, लेकिन स्प्रिंग 4.0 में @AuthenticationPrincipalऔर AuthenticationPrincipalArgumentResolverदूसरे पैकेज में "ले जाया गया":

(लेकिन इसके पुराने पैक्स में पुरानी कक्षाएं अभी भी मौजूद हैं, इसलिए उन्हें मिलाएं नहीं!)

यह सिर्फ लिख रहा है

import org.springframework.security.core.annotation.AuthenticationPrincipal;
ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) {
    ...
}

इस कार्य को करने के लिए आपको रजिस्टर ( org.springframework.security.web.method.annotation.) AuthenticationPrincipalArgumentResolver: "एक्टीवेटिंग" @EnableWebMvcSecurityकरके या इस बीन को रजिस्टर करके भीतर लाने की आवश्यकता है mvc:argument-resolvers- उसी तरह जैसा मैंने ऊपर स्प्रिंग 3.1 समाधान के साथ वर्णित किया है।

<mvc:annotation-driven>
    <mvc:argument-resolvers>
        <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
    </mvc:argument-resolvers>
</mvc:annotation-driven>

@See स्प्रिंग सिक्योरिटी 5.0 संदर्भ, अध्याय 39.3 @AuthenticationPrincipal


वैकल्पिक रूप से बस एक बीन बनाएं जिसमें getUserDetails () विधि और @Autowire हो जो आपके कंट्रोलर में हो।
१५:२० पर sourcedelica

इस समाधान को कार्यान्वित करते हुए, मैंने रिज़ॉल्यूशन के शीर्ष पर एक ब्रेकपॉइंट सेट किया है () लेकिन मेरा ऐप वेब तर्क रिज़ॉल्वर में कभी नहीं चलता है। सर्वलेट संदर्भ में आपका स्प्रिंग कॉन्फ़िगरेशन रूट संदर्भ नहीं है, है ना?
Jay

@ जय: यह विन्यास मूल संदर्भ का हिस्सा है, सर्वलेट संदर्भ नहीं। - यह (id="applicationConversionService")उदाहरण है कि मैं उदाहरण में आईडी निर्दिष्ट करना भूल गया हूं
राल्फ

11
यह ध्यान दिया जाना चाहिए कि यदि आप स्प्रिंग 3.1 का उपयोग कर रहे हैं, तो वे WebArgumentResolver पर HandlerMethodArgumentResolver की सलाह देते हैं। मुझे सर्विस संदर्भ में <एनोटेशन-चालित> के साथ कॉन्फ़िगर करके काम करने के लिए हैंडलरमैथोडाअर्गमेंटResolver मिला। इसके अलावा, मैंने यहां पोस्ट के रूप में उत्तर को लागू किया और सब कुछ बढ़िया काम करता है
जे 12

@sourcedelica स्थैतिक पद्धति क्यों नहीं बनाई गई?
एलेक्स

66

जबकि राल्फ्स उत्तर एक सुरुचिपूर्ण समाधान प्रदान करता है, वसंत सुरक्षा 3.2 के साथ आपको अब अपने स्वयं के कार्यान्वयन की आवश्यकता नहीं है ArgumentResolver

यदि आपके पास एक UserDetailsकार्यान्वयन है CustomUser, तो आप ऐसा कर सकते हैं:

@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {

    // .. find messages for this User and return them...
}

वसंत सुरक्षा दस्तावेज देखें : @AuthenticationPrincipal


2
उन लोगों के लिए जो प्रदान किए गए लिंक को पढ़ना पसंद नहीं करते हैं, इसे @EnableWebMvcSecurityया तो XML में सक्षम करना होगा :<mvc:annotation-driven> <mvc:argument-resolvers> <bean class="org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver" /> </mvc:argument-resolvers> </mvc:annotation-driven>
sodik

कैसे जांचा जाए कि customUserक्या अशक्त है या नहीं?
साजाद

if (customUser != null) { ... }
T3rm1

27

स्प्रिंग सिक्योरिटी को अन्य गैर-स्प्रिंग फ्रेमवर्क के साथ काम करने का इरादा है, इसलिए यह स्प्रिंग एमवीसी के साथ कसकर एकीकृत नहीं है। स्प्रिंग सिक्योरिटी Authenticationऑब्जेक्ट को HttpServletRequest.getUserPrincipal()डिफ़ॉल्ट रूप से विधि से लौटाती है ताकि आपको प्रिंसिपल के रूप में प्राप्त हो। आप इसका UserDetailsउपयोग करके सीधे अपनी वस्तु प्राप्त कर सकते हैं

UserDetails ud = ((Authentication)principal).getPrincipal()

यह भी ध्यान दें कि उपयोग किए गए प्रमाणीकरण तंत्र के आधार पर ऑब्जेक्ट प्रकार भिन्न हो सकते हैं ( UsernamePasswordAuthenticationTokenउदाहरण के लिए, आपको नहीं मिल सकता है ) और Authenticationकड़ाई से एक को शामिल करने के लिए नहीं है UserDetails। यह एक स्ट्रिंग या किसी अन्य प्रकार का हो सकता है।

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

interface MySecurityAccessor {

    MyUserDetails getCurrentUser();

    // Other methods
}

इसके बाद आप SecurityContextHolderअपने मानक कार्यान्वयन में पहुँच कर इसे लागू कर सकते हैं , इस प्रकार अपने कोड को स्प्रिंग सिक्योरिटी से पूरी तरह से अलग कर सकते हैं। फिर इसे उन नियंत्रकों में इंजेक्ट करें, जिन्हें वर्तमान उपयोगकर्ता पर सुरक्षा जानकारी या जानकारी तक पहुंचने की आवश्यकता है।

अन्य मुख्य लाभ यह है कि थ्रेड-लोकल और इतने पर आबादी के बारे में चिंता किए बिना, परीक्षण के लिए निश्चित डेटा के साथ सरल कार्यान्वयन करना आसान है।


मैं इस दृष्टिकोण पर विचार कर रहा था, लेकिन मुझे यकीन नहीं था कि अगर कोई थ्रेडिंग मुद्दा होता तो वास्तव में इसे राइट वे (टीएम) और बी) कैसे किया जाता। क्या आप निश्चित हैं कि वहाँ कोई समस्या नहीं होगी? मैं ऊपर पोस्ट किए गए एनोटेशन विधि के साथ जा रहा हूं, लेकिन मुझे लगता है कि यह अभी भी इसके बारे में जाने का एक सभ्य तरीका है। पोस्ट करने का शुक्रिया। :)
अवनी बियर

4
यह तकनीकी रूप से आपके नियंत्रक से सीधे SecurityContextHolder तक पहुँचने के समान है, इसलिए कोई थ्रेडिंग समस्या नहीं होनी चाहिए। यह केवल एक ही स्थान पर कॉल रखता है और आपको परीक्षण के लिए आसानी से विकल्प इंजेक्ट करने की अनुमति देता है। आप अन्य गैर-वेब कक्षाओं में उसी दृष्टिकोण का फिर से उपयोग कर सकते हैं, जिसे सुरक्षा जानकारी तक पहुंच की आवश्यकता होती है।
भेड़

समझे ... वही जो मैं सोच रहा था। यदि मैं एनोटेशन विधि के साथ यूनिट परीक्षण जारी करता हूं, या यदि मैं स्प्रिंग के साथ युग्मन को कम करना चाहता हूं, तो यह वापस आने के लिए एक अच्छा होगा।
शाम

@LukeTaylor आप इस दृष्टिकोण को और स्पष्ट कर सकते हैं, मैं वसंत और वसंत सुरक्षा के लिए थोड़ा नया हूं, इसलिए मुझे इस पर अमल नहीं करना है। मैं इसे कहां लागू करूंगा ताकि इसे एक्सेस किया जा सके? मेरे UserServiceImpl को?
Cu7l4ss

9

HandlerInterceptorइंटरफ़ेस को लागू करें, और फिर UserDetailsप्रत्येक अनुरोध में एक मॉडल है, जो निम्नानुसार इंजेक्ट करें :

@Component 
public class UserInterceptor implements HandlerInterceptor {
    ....other methods not shown....
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        if(modelAndView != null){
            modelAndView.addObject("user", (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal());
        }
}

1
धन्यवाद @atrain, यह उपयोगी और सुरुचिपूर्ण है। इसके अलावा, मुझे <mvc:interceptors>अपने एप्लिकेशन कॉन्फ़िगरेशन फ़ाइल में जोड़ना पड़ा ।
एरिक

यह बेहतर है कि टेम्पलेट के माध्यम से अधिकृत उपयोगकर्ता प्राप्त करें spring-security-taglibs: stackoverflow.com/a/44373331/548473
ग्रिगोरी किसलिन

9

स्प्रिंग सुरक्षा संस्करण 3.2 के साथ शुरू, कस्टम कार्यक्षमता जो कुछ पुराने उत्तरों द्वारा लागू की गई है, @AuthenticationPrincipalएनोटेशन के रूप में बॉक्स से बाहर मौजूद है जो कि समर्थित है AuthenticationPrincipalArgumentResolver

इसका उपयोग करने का एक सरल उदाहरण है:

@Controller
public class MyController {
   @RequestMapping("/user/current/show")
   public String show(@AuthenticationPrincipal CustomUser customUser) {
        // do something with CustomUser
       return "view";
   }
}

CustomUser से असाइन करने की आवश्यकता है authentication.getPrincipal()

यहाँ की इसी Javadocs हैं AuthenticationPrincipal और AuthenticationPrincipalArgumentResolver


1
@nbro जब मैंने संस्करण विशिष्ट समाधान जोड़ा, तो इस समाधान को ध्यान में रखने के लिए अन्य समाधानों में से कोई भी अद्यतन नहीं किया गया था
जियोन्ड

ऑथेंटिकेशनप्रीनिरीपालअर्गुमेंटअनोलोवर अब पदावनत हो गया है
इगोर डोनिन

5
@Controller
public abstract class AbstractController {
    @ModelAttribute("loggedUser")
    public User getLoggedUser() {
        return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }
}

0

और अगर आपको टेम्पलेट (जैसे JSP) उपयोग में अधिकृत उपयोगकर्ता की आवश्यकता है

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:authentication property="principal.yourCustomField"/>

के साथ साथ

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-taglibs</artifactId>
        <version>${spring-security.version}</version>
    </dependency>

0

आप इसे आज़मा सकते हैं: स्प्रिंग से ऑथेंटिकेशन ऑब्जेक्ट का उपयोग करके हम इसे कंट्रोलर मेथड से उपयोगकर्ता विवरण प्राप्त कर सकते हैं। नीचे तर्क के साथ नियंत्रक विधि में ऑथेंटिकेशन ऑब्जेक्ट को पास करके उदाहरण दिया गया है। किसी उपयोगकर्ता को प्रमाणित किया जाता है कि विवरण ऑथेंटिकेशन ऑब्जेक्ट में पॉप्युलेट किए जाते हैं।

@GetMapping(value = "/mappingEndPoint") <ReturnType> methodName(Authentication auth) {
   String userName = auth.getName(); 
   return <ReturnType>;
}

कृपया अपना उत्तर विस्तृत करें।
निकोलाई शेवचेंको

मैंने अपना उत्तर संपादित कर लिया है, हम ऑथेंटिकेशन ऑब्जेक्ट का उपयोग कर सकते हैं जिसमें उपयोगकर्ता का उपयोगकर्ता विवरण है जो पहले से ही प्रमाणित है।
मिर्जा शुजतुल्ला
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.