কীভাবে সক্রিয় ব্যবহারকারীর ইউজারডেটেলগুলি পাবেন


170

আমার নিয়ন্ত্রকগুলিতে, যখন আমার সক্রিয় (লগ ইন) ব্যবহারকারীর প্রয়োজন হয়, আমি আমার UserDetailsপ্রয়োগটি পেতে নিম্নলিখিতগুলি করছি :

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

এটি ঠিকঠাক কাজ করে তবে আমি মনে করি বসন্ত এরকম ক্ষেত্রে জীবনকে আরও সহজ করে তুলতে পারে। UserDetailsনিয়ামক বা পদ্ধতিতে স্বতঃসংশ্লিষ্ট হওয়ার কোনও উপায় আছে কি ?

উদাহরণস্বরূপ, এর মতো কিছু:

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

কিন্তু পরিবর্তে পেয়ে UsernamePasswordAuthenticationToken, আমি একটি UserDetailsপরিবর্তে পেতে পারি ?

আমি একটি মার্জিত সমাধান খুঁজছি। কোন ধারনা?

উত্তর:


226

প্রস্তাবনামূলক: স্প্রিং-সুরক্ষা ৩.২ যেহেতু @AuthenticationPrincipalএই উত্তরের শেষে বর্ণিত একটি দুর্দান্ত টীকা রয়েছে । আপনি যখন বসন্ত-সুরক্ষা> = 3.2 ব্যবহার করেন তখন যাওয়ার সর্বোত্তম উপায়।

যখন তুমি:

  • বসন্ত-সুরক্ষার একটি পুরানো সংস্করণ ব্যবহার করুন,
  • অধ্যক্ষ বা সঞ্চিত কিছু তথ্য (যেমন লগইন বা আইডি) দ্বারা ডেটাবেস থেকে আপনার কাস্টম ব্যবহারকারী অবজেক্টটি লোড করা দরকার
  • কীভাবে একটি HandlerMethodArgumentResolverবা WebArgumentResolverএটি একটি মার্জিত উপায়ে সমাধান করতে পারে তা শিখতে চাই বা পিছনের পটভূমিটি শিখতে চাই @AuthenticationPrincipalএবং AuthenticationPrincipalArgumentResolver(কারণ এটি একটি এর উপর ভিত্তি করে HandlerMethodArgumentResolver)

তারপরে পড়া চালিয়ে যান - অন্যথায় কেবল @AuthenticationPrincipalরব উইঞ্চ (লেখক @AuthenticationPrincipal) এবং লুকাশ শামেলজিয়েসেন (তার উত্তরের জন্য ) ব্যবহার করুন এবং ধন্যবাদ ।

(বিটিডাব্লু: আমার উত্তরটি কিছুটা পুরনো (জানুয়ারী ২০১২), সুতরাং স্প্রিং সিকিউরিটি ৩.২- এ এনটোটেশন সলিউশনের ভিত্তিতে লুকাশ শামেলজেইসেনই প্রথম এসেছিলেন @AuthenticationPrincipal)


তারপরে আপনি আপনার নিয়ামকটি ব্যবহার করতে পারেন

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যদি পদ্ধতি আর্গুমেন্ট (প্রথম প্যারামিটার) এর জন্য দায়বদ্ধ মনে করে তবে তা অবশ্যই ফিরিয়ে আনতে হবে ।

স্প্রিং ৩.১ থেকে একটি নতুন ধারণা বলা হয় HandlerMethodArgumentResolver। আপনি যদি স্প্রিং ৩.১+ ব্যবহার করেন তবে আপনার এটি ব্যবহার করা উচিত। (এটি এই উত্তরের পরবর্তী অংশে বর্ণিত হয়েছে))

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>

@ দেখুন: স্প্রিং এমভিসি @ নিয়ন্ত্রণকারী পদ্ধতি যুক্তিগুলি কাস্টমাইজ করতে শিখুন

এটি লক্ষণীয় হওয়া উচিত যে আপনি যদি স্প্রিং ৩.১ ব্যবহার করেন তবে তারা ওয়েবআরগমেন্ট রিসোলভারের উপর হ্যান্ডলারমেথডআর্গমেন্ট রিসোলভারের পরামর্শ দেয়। - জে মন্তব্য দেখুন


HandlerMethodArgumentResolverস্প্রিং ৩.১+ এর জন্য একই

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.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 সমাধানের সাথে এটি বর্ণনা করেছি।

@ স্প্রিং সুরক্ষা দেখুন ৩.২ তথ্যসূত্র, অধ্যায় ১১.২। @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>

@ স্প্রিং সুরক্ষা 5.0 রেফারেন্স দেখুন, অধ্যায় 39.3 @ প্রমাণীকরণের মূলসূত্র


বিকল্পভাবে কেবল একটি শিম তৈরি করুন যার একটি getUserDetails () পদ্ধতি রয়েছে এবং এটি আপনার নিয়ামকটিতে @ আউটওয়ায়ার।
সোর্সডেলিকা

এই সমাধানটি প্রয়োগ করে, আমি রেজোলিউশনআর্গুমেন্ট () এর শীর্ষে একটি ব্রেকপয়েন্ট স্থাপন করেছি তবে আমার অ্যাপ্লিকেশন কখনই ওয়েব আর্গুমেন্ট রিসলভারটিতে পদক্ষেপ নেয় না। সার্লেট প্রসঙ্গে আপনার বসন্তের কনফিগারেশনটি মূল প্রসঙ্গে নয়, তাই না?
জয়

@ জায়ে: এই কনফিগারেশনটি মূল প্রসঙ্গের অংশ, সার্লেট প্রসঙ্গে নয়। - এটি এমনটি seams করে যে আমি (id="applicationConversionService")উদাহরণটিতে আইডি নির্দিষ্ট করতে ভুলে গিয়েছি
রাল্ফ

11
এটি লক্ষণীয় হওয়া উচিত যে আপনি যদি স্প্রিং ৩.১ ব্যবহার করেন তবে তারা ওয়েবআরগমেন্ট রিসোলভারের উপর হ্যান্ডলারমেথডআর্গমেন্ট রিসোলভারের পরামর্শ দেয়। সার্ভলেট প্রসঙ্গে <নোটেশন -ড্রাইভন> এর সাথে কনফিগার করে কাজ করার জন্য আমি হ্যান্ডলারমিথডআর্গমেন্ট রিসোলভারটি পেয়েছি। তা ছাড়া, আমি এখানে পোস্ট হিসাবে উত্তরটি কার্যকর করেছি এবং সবকিছু দুর্দান্ত কাজ করে
জয়

@ সৌরসিলিকা কেন কেবল স্থির পদ্ধতি তৈরি করবেন না?
অ্যালেক্স 78191

66

যদিও Ralphs উত্তর আপনার নিজের বাস্তবায়ন স্প্রিং সিকিউরিটি 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এক্সএমএল দ্বারা বা এর দ্বারা সক্ষম করতে হবে :<mvc:annotation-driven> <mvc:argument-resolvers> <bean class="org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver" /> </mvc:argument-resolvers> </mvc:annotation-driven>
সোমডিক

কীভাবে তা পরীক্ষা করবেন customUserকী নাল কিনা ?
সাজাদ

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

27

স্প্রিং সিকিউরিটির উদ্দেশ্য হ'ল অন্যান্য স্প্রিং-এর ফ্রেমওয়ার্কগুলির সাথে কাজ করা, সুতরাং এটি স্প্রিং এমভিসির সাথে দৃ tight়ভাবে একীভূত নয়। স্প্রিং সিকিউরিটি ডিফল্টরূপে পদ্ধতি Authenticationথেকে অবজেক্টটিকে ফেরত দেয় HttpServletRequest.getUserPrincipal()যাতে আপনি মূল হিসাবে যা পান get আপনি এটি UserDetailsব্যবহার করে সরাসরি নিজের অবজেক্টটি পেতে পারেন

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

এছাড়াও খেয়াল করুন যে ব্যবহৃত প্রমাণীকরণ ব্যবস্থার উপর নির্ভর করে অবজেক্টের ধরণগুলি পৃথক হতে পারে (আপনি UsernamePasswordAuthenticationTokenউদাহরণস্বরূপ একটি পেতে পারেন না ) এবং এতে Authenticationকঠোরভাবে একটি থাকতে হবে না UserDetails। এটি স্ট্রিং বা অন্য কোনও ধরণের হতে পারে।

আপনি যদি SecurityContextHolderসরাসরি কল করতে না চান , সর্বাধিক মার্জিত পন্থা (যা আমি অনুসরণ করব) হ'ল আপনার নিজস্ব কাস্টম সুরক্ষা প্রসঙ্গ অ্যাক্সেসর ইন্টারফেসটি ইনজেক্ট করা যা আপনার চাহিদা এবং ব্যবহারকারীর অবজেক্টের সাথে মেলানোর জন্য কাস্টমাইজ করা হয়েছে। সম্পর্কিত পদ্ধতি সহ একটি ইন্টারফেস তৈরি করুন, উদাহরণস্বরূপ:

interface MySecurityAccessor {

    MyUserDetails getCurrentUser();

    // Other methods
}

তারপরে আপনি এটি SecurityContextHolderআপনার মানক প্রয়োগায় অ্যাক্সেসের মাধ্যমে বাস্তবায়ন করতে পারেন , এভাবে স্প্রিং সিকিউরিটি থেকে আপনার কোডটি পুরোপুরি ডিকপল করে। তারপরে এটি নিয়ন্ত্রণকারীগুলিতে ইনজেক্ট করুন যাদের বর্তমান ব্যবহারকারীর সুরক্ষা তথ্য বা তথ্য অ্যাক্সেসের প্রয়োজন।

অন্য প্রধান সুবিধাটি হ'ল থ্রেড-লোকাল ইত্যাদির বিষয়ে চিন্তাভাবনা না করে পরীক্ষার জন্য নির্দিষ্ট ডেটা সহ সহজ বাস্তবায়ন করা সহজ।


আমি এই পদ্ধতির বিষয়ে বিবেচনা করছিলাম, তবে আমি নিশ্চিত ছিলাম না যে) কীভাবে সঠিকভাবে এটি করা উচিত (টিএম) এবং খ) যদি কোনও থ্রেডিংয়ের সমস্যা থাকে তবে। আপনি কি নিশ্চিত যে সেখানে কোনও সমস্যা হবে না? আমি উপরে পোস্ট করা টীকা পদ্ধতিতে যাচ্ছি, তবে আমি মনে করি এটি এখনও এটি চালিয়ে যাওয়ার একটি শালীন উপায়। পোস্ট করার জন্য ধন্যবাদ। :)
অগ্নি বিয়ার

4
এটি প্রযুক্তিগতভাবে আপনার নিয়ামক থেকে সিকিউরিটি কনটেক্সটহোল্ডার অ্যাক্সেস করার সমান, তাই কোনও থ্রেডিংয়ের সমস্যা হওয়া উচিত নয়। এটি কেবলমাত্র একক স্থানে কল রাখে এবং আপনাকে পরীক্ষার জন্য বিকল্পগুলি সহজেই ইনজেক্ট করতে দেয়। আপনি অন্যান্য নন-ওয়েব ক্লাসেও একই পদ্ধতি পুনরায় ব্যবহার করতে পারেন যা সুরক্ষা তথ্যে অ্যাক্সেসের প্রয়োজন।
ভেড়া শান

গোছা ... এটাই আমি ভাবছিলাম। যদি আমার কাছে টিকাশনের পদ্ধতিতে ইউনিট পরীক্ষার সমস্যা থাকে বা আমি স্প্রিংয়ের সাথে সংযোগ কমাতে চাই তবে ফিরে আসাই ভাল।
অগ্নি বিয়ার

@ লুকিটেলর আপনি দয়া করে এই পদ্ধতির আরও ব্যাখ্যা করতে পারেন, আমি বসন্ত এবং বসন্ত সুরক্ষায় কিছুটা নতুন, সুতরাং এটি কীভাবে বাস্তবায়ন করা যায় সে সম্পর্কে আমি খুব একটা পাই না। আমি এটি কোথায় বাস্তবায়ন করব যাতে এটি অ্যাক্সেস হয়? আমার ব্যবহারকারীর পরিষেবাতে?
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
গ্রেগরি Kislin

9

স্প্রিং সিকিউরিটি সংস্করণ ৩.২ দিয়ে শুরু করে, কিছু পুরানো উত্তর দ্বারা প্রয়োগ করা কাস্টম কার্যকারিতা, এনটোটেশন আকারে বাক্সের বাইরে রয়েছে @AuthenticationPrincipalযা সমর্থন করে AuthenticationPrincipalArgumentResolver

এর ব্যবহারের একটি সাধারণ উদাহরণ হ'ল:

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

কাস্টম ব্যবহারকারীর কাছ থেকে নির্ধারিত হওয়া দরকার authentication.getPrincipal()

এখানে সংশ্লিষ্ট Javadocs হয় AuthenticationPrincipal এবং AuthenticationPrincipalArgumentResolver


1
@ এনব্রো যখন আমি সংস্করণটি নির্দিষ্ট সমাধানটি যুক্ত করেছি, তখন অন্য সমাধানগুলির
কোনওটিই


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

0

এবং আপনার যদি টেমপ্লেটগুলিতে অনুমোদিত ব্যবহারকারী প্রয়োজন (যেমন জেএসপি) ব্যবহার

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

আপনি এটি চেষ্টা করতে পারেন: বসন্ত থেকে প্রমাণীকরণ অবজেক্ট ব্যবহার করে আমরা এটির কাছ থেকে নিয়ন্ত্রক পদ্ধতিতে বিশদ জানতে পারি। নীচে উদাহরণস্বরূপ, যুক্তি সহ কন্ট্রোলার পদ্ধতিতে প্রমাণীকরণ বস্তুটি পাস করার মাধ্যমে Oএকবার ব্যবহারকারীকে সত্যায়িত করা হলে বিশদটি প্রমাণীকরণ বস্তুতে পপুলেশন হয়।

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

আপনার উত্তরটি বিস্তারিতভাবে বর্ণনা করুন।
নিকোলাই শেভচেনকো

আমি আমার উত্তরটি সম্পাদনা করেছি, আমরা প্রমাণীকরণ বস্তুটি ব্যবহার করতে পারি যা ইতিমধ্যে প্রমাণীকরণকারী ব্যবহারকারীর ব্যবহারকারীর বিবরণ রয়েছে।
মির্জা শুজাথউল্লাহ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.