rest - कस्टम टोकन का उपयोग कर आरईएसटी एपीआई सुरक्षित करना(स्टेटलेस, यूआई, कोई कुकीज़ नहीं, कोई बुनियादी प्रमाणीकरण नहीं, कोई ओएथ नहीं, कोई लॉगिन पेज नहीं)




spring-security spring-boot (4)

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

  • आरईएसटी एपीआई एचटीटीपीएस पर स्टेटलेस बैकएंड द्वारा परोसा जाता है
  • ग्राहक वेब ऐप, मोबाइल ऐप, कोई एसपीए स्टाइल ऐप, थर्ड-पार्टी एपीआई हो सकता है
  • कोई मूल औथ, कोई कुकीज़ नहीं, कोई यूआई नहीं (कोई जेएसपी / एचटीएमएल / स्थिर-संसाधन नहीं), कोई पुनर्निर्देशन नहीं, ओएथ प्रदाता नहीं।
  • एचटीटीपीएस हेडर पर कस्टम टोकन सेट
  • बाहरी स्टोर के खिलाफ टोकन सत्यापन (जैसे मेमकैच / रेडिस / या यहां तक ​​कि किसी भी आरडीबीएमएस)
  • सभी एपीआई को चयनित पथों को छोड़कर प्रमाणित करने की आवश्यकता है (जैसे / लॉगिन, / साइनअप, / सार्वजनिक, आदि ..)

मैं स्प्रिंगबूट, वसंत सुरक्षा, आदि का उपयोग करता हूं .. जावा कॉन्फ़िगरेशन के साथ समाधान पसंद करते हैं (कोई एक्सएमएल नहीं)


आप सही हैं, यह आसान नहीं है और वहां कई अच्छे उदाहरण नहीं हैं। उदाहरण मैंने देखा कि आप इसे अन्य वसंत सुरक्षा सामान का उपयोग नहीं कर सके। मैंने हाल ही में कुछ ऐसा किया है, मैंने यह किया है।

अपने हेडर वैल्यू को पकड़ने के लिए आपको एक कस्टम टोकन चाहिए

public class CustomToken extends AbstractAuthenticationToken {
  private final String value;

  //Getters and Constructor.  Make sure getAutheticated returns false at first.
  //I made mine "immutable" via:

      @Override
public void setAuthenticated(boolean isAuthenticated) {
    //It doesn't make sense to let just anyone set this token to authenticated, so we block it
    //Similar precautions are taken in other spring framework tokens, EG: UsernamePasswordAuthenticationToken
    if (isAuthenticated) {

        throw new IllegalArgumentException(MESSAGE_CANNOT_SET_AUTHENTICATED);
    }

    super.setAuthenticated(false);
}
}

हेडर निकालने के लिए आपको एक वसंत सुरक्षा फ़िल्टर की आवश्यकता है और प्रबंधक को प्रमाणित करने के लिए कहें, इस जोरदार पाठ की तरह कुछ

public class CustomFilter extends AbstractAuthenticationProcessingFilter {


    public CustomFilter(RequestMatcher requestMatcher) {
        super(requestMatcher);

        this.setAuthenticationSuccessHandler((request, response, authentication) -> {
        /*
         * On success the desired action is to chain through the remaining filters.
         * Chaining is not possible through the success handlers, because the chain is not accessible in this method.
         * As such, this success handler implementation does nothing, and chaining is accomplished by overriding the successfulAuthentication method as per:
         * http://docs.spring.io/autorepo/docs/spring-security/3.2.4.RELEASE/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html#successfulAuthentication(javax.servlet.http.HttpServletRequest,%20javax.servlet.http.HttpServletResponse,%20javax.servlet.FilterChain,%20org.springframework.security.core.Authentication)
         * "Subclasses can override this method to continue the FilterChain after successful authentication."
         */
        });

    }



    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {


        String tokenValue = request.getHeader("SOMEHEADER");

        if(StringUtils.isEmpty(tokenValue)) {
            //Doing this check is kinda dumb because we check for it up above in doFilter
            //..but this is a public method and we can't do much if we don't have the header
            //also we can't do the check only here because we don't have the chain available
           return null;
        }


        CustomToken token = new CustomToken(tokenValue);
        token.setDetails(authenticationDetailsSource.buildDetails(request));

        return this.getAuthenticationManager().authenticate(token);
    }



    /*
     * Overriding this method to maintain the chaining on authentication success.
     * http://docs.spring.io/autorepo/docs/spring-security/3.2.4.RELEASE/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html#successfulAuthentication(javax.servlet.http.HttpServletRequest,%20javax.servlet.http.HttpServletResponse,%20javax.servlet.FilterChain,%20org.springframework.security.core.Authentication)
     * "Subclasses can override this method to continue the FilterChain after successful authentication."
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {


        //if this isn't called, then no auth is set in the security context holder
        //and subsequent security filters can still execute.  
        //so in SOME cases you might want to conditionally call this
        super.successfulAuthentication(request, response, chain, authResult);

        //Continue the chain
        chain.doFilter(request, response);

    }


}

वसंत सुरक्षा श्रृंखला में अपने कस्टम फ़िल्टर रजिस्टर करें

 @Configuration
 public static class ResourceEndpointsSecurityConfig extends WebSecurityConfigurerAdapter {        

      //Note, we don't register this as a bean as we don't want it to be added to the main Filter chain, just the spring security filter chain
      protected AbstractAuthenticationProcessingFilter createCustomFilter() throws Exception {
        CustomFilter filter = new CustomFilter( new RegexRequestMatcher("^/.*", null));
        filter.setAuthenticationManager(this.authenticationManagerBean());
        return filter;
      }

       @Override
       protected void configure(HttpSecurity http) throws Exception {                  

            http
            //fyi: This adds it to the spring security proxy filter chain
            .addFilterBefore(createCustomFilter(), AnonymousAuthenticationFilter.class)
       }
}

फ़िल्टर के साथ निकाले गए टोकन को सत्यापित करने के लिए एक कस्टम ऑथ प्रदाता।

public class CustomAuthenticationProvider implements AuthenticationProvider {


    @Override
    public Authentication authenticate(Authentication auth)
            throws AuthenticationException {

        CustomToken token = (CustomToken)auth;

        try{
           //Authenticate token against redis or whatever you want

            //This i found weird, you need a Principal in your Token...I use User
            //I found this to be very redundant in spring security, but Controller param resolving will break if you don't do this...anoying
            org.springframework.security.core.userdetails.User principal = new User(...); 

            //Our token resolved to a username so i went with this token...you could make your CustomToken take the principal.  getCredentials returns "NO_PASSWORD"..it gets cleared out anyways.  also the getAuthenticated for the thing you return should return true now
            return new UsernamePasswordAuthenticationToken(principal, auth.getCredentials(), principal.getAuthorities());
        } catch(Expection e){
            //TODO throw appropriate AuthenticationException types
            throw new BadCredentialsException(MESSAGE_AUTHENTICATION_FAILURE, e);
        }


    }

    @Override
    public boolean supports(Class<?> authentication) {
        return CustomToken.class.isAssignableFrom(authentication);
    }


}

अंत में, अपने प्रदाता को एक बीन के रूप में पंजीकृत करें ताकि प्रमाणीकरण प्रबंधक इसे कुछ @ कॉन्फ़िगरेशन क्लास में पा सके। आप शायद इसे @ कॉम्पोनेंट भी कर सकते हैं, मैं इस विधि को पसंद करता हूं

@Bean
public AuthenticationProvider createCustomAuthenticationProvider(injectedDependencies)  {
    return new CustomAuthenticationProvider(injectedDependencies);
}

एक अन्य उदाहरण परियोजना जो जेडब्ल्यूटी - जिप्स्टर का उपयोग करती है

JHipster का उपयोग कर एक माइक्रोसाइवेयर एप्लिकेशन जेनरेट करने का प्रयास करें। यह वसंत सुरक्षा और जेडब्ल्यूटी के बीच बॉक्स एकीकरण के साथ एक टेम्पलेट उत्पन्न करता है।

https://jhipster.github.io/security/


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

बुनियादी विचार निम्नलिखित चार वसंत सुरक्षा घटकों को लागू करना है:

  1. org.springframework.security.web.AuthenticationEntryPoint कॉल को जाल करने के लिए प्रमाणीकरण की आवश्यकता है लेकिन आवश्यक प्रमाणीकरण टोकन गुम है और इस प्रकार अनुरोधों को अस्वीकार कर देता है।
  2. org.springframework.security.core.Authentication . REST API के लिए आवश्यक प्रमाणीकरण जानकारी रखने के लिए प्रमाणीकरण।
  3. org.springframework.security.authentication.AuthenticationProvider प्रमाणीकरण वास्तविक प्रमाणीकरण करने के लिए org.springframework.security.authentication.AuthenticationProvider (डेटाबेस के विरुद्ध, एक एलडीएपी सर्वर, एक वेब सेवा, आदि)।
  4. HTTP अनुरोधों के बीच प्रमाणीकरण टोकन रखने के लिए org.springframework.security.web.context.SecurityContextRepository । नमूना में, कार्यान्वयन एक ईएचसीएसीईई उदाहरण में टोकन बचाता है।

नमूना एक्सएमएल कॉन्फ़िगरेशन का उपयोग करता है लेकिन आप आसानी से समकक्ष जावा कॉन्फ़िगरेशन के साथ आ सकते हैं।


मैं जेएसओएन वेब टोकन http://jwt.io/ की सिफारिश करता हूं, यह स्टेटलेस और स्केलेबल है।

यहां एक उदाहरण प्रोजेक्ट है, https://github.com/brahalla/Cerberus





stateless