স্প্রিংয়ের মাধ্যমে RESTful প্রমাণীকরণ


262

সমস্যা:
আমাদের একটি স্প্রিং এমভিসি-ভিত্তিক RESTful API রয়েছে যাতে সংবেদনশীল তথ্য রয়েছে। এপিআই সুরক্ষিত করা উচিত, তবে প্রতিটি অনুরোধের সাথে ব্যবহারকারীর শংসাপত্রগুলি (ব্যবহারকারী / পাস কম্বো) প্রেরণ করা কাম্য নয়। REST নির্দেশিকা (এবং অভ্যন্তরীণ ব্যবসায়ের প্রয়োজনীয়তা) অনুযায়ী, সার্ভারকে অবশ্যই রাষ্ট্রহীন থাকতে হবে। মেশাপ-শৈলীর পদ্ধতির মধ্যে অন্য সার্ভার দ্বারা এপিআই গ্রাস করা হবে।

প্রয়োজনীয়তা:

  • ক্লায়েন্ট .../authenticateশংসাপত্র সহ (অরক্ষিত ইউআরএল) একটি অনুরোধ করে ; সার্ভার একটি সুরক্ষিত টোকেন প্রদান করে যা ভবিষ্যতে অনুরোধগুলি বৈধ করতে এবং রাষ্ট্রবিহীন থাকার জন্য সার্ভারের জন্য পর্যাপ্ত তথ্য রয়েছে। এটি সম্ভবত স্প্রিং সিকিউরিটির রিমাই-মি টোকেনের মতো একই তথ্য ধারণ করে ।

  • ক্লায়েন্ট বিভিন্ন (সুরক্ষিত) ইউআরএলগুলিতে পরবর্তী অনুরোধ করে, পূর্বে প্রাপ্ত টোকেনকে ক্যোয়ারী প্যারামিটার হিসাবে যুক্ত করে (বা, খুব কম, কোনও এইচটিটিপি অনুরোধ শিরোনাম)।

  • ক্লায়েন্ট কুকি সংরক্ষণ করতে পারে আশা করা যায় না।

  • যেহেতু আমরা ইতিমধ্যে বসন্ত ব্যবহার করি, সমাধানটি স্প্রিং সুরক্ষা ব্যবহার করা উচিত।

আমরা এই কাজটি করার চেষ্টা করে প্রাচীরের বিরুদ্ধে মাথা ঠাটিয়ে যাচ্ছি, সুতরাং আশা করি এর বাইরে কেউ ইতিমধ্যে এই সমস্যার সমাধান করেছেন।

উপরের দৃশ্যের পরিপ্রেক্ষিতে আপনি কীভাবে এই বিশেষ প্রয়োজনটিকে সমাধান করতে পারেন?


49
হাই ক্রিস, আমি নিশ্চিত নই যে ক্যোয়ারী প্যারামিটারে সেই টোকেনটি পাস করা সেরা ধারণা। এটি এইচটিটিপিএস বা এইচটিটিপি নির্বিশেষে লগগুলিতে প্রদর্শিত হবে। শিরোনামগুলি সম্ভবত নিরাপদ। শুধু এফওয়াইআই। দুর্দান্ত প্রশ্ন যদিও। +1
jmort253

1
রাজ্যহীন সম্পর্কে আপনার কী বোঝার? আপনার টোকেন প্রয়োজনীয়তা আমার রাজ্যহীন সম্পর্কে বোঝার সাথে সংঘবদ্ধ। এইচটিটিপি প্রমাণীকরণের উত্তরটি আমার কাছে একমাত্র রাষ্ট্রবিহীন বাস্তবায়ন বলে মনে হচ্ছে।
মার্কাস মালকুশ

9
@ মারকাস মালকুশ রাষ্ট্রহীন প্রদত্ত ক্লায়েন্টের সাথে পূর্বের যোগাযোগের সার্ভারের জ্ঞানকে বোঝায়। এইচটিটিপি সংজ্ঞা অনুসারে রাষ্ট্রহীন, এবং সেশন কুকিজ এটিকে রাষ্ট্রীয় করে তোলে। টোকেনের আজীবন (এবং উত্স, সেই বিষয়ে) অপ্রাসঙ্গিক; সার্ভারটি কেবল যত্নশীল যে এটি বৈধ এবং কোনও ব্যবহারকারীর সাথে আবদ্ধ হতে পারে (কোনও সেশন নয়)। একটি সনাক্তকারী টোকেন পাস করা, অতএব, রাষ্ট্রীয়তার সাথে হস্তক্ষেপ করে না।
ক্রিস ক্যাশওয়েল 3'14

1
@ ক্রিসক্যাশওয়েল আপনি কীভাবে নিশ্চিত করবেন যে ক্লায়েন্ট দ্বারা টোকেনটি ছদ্মবেশ / উত্পন্ন হচ্ছে না? টোকনটি এনক্রিপ্ট করতে, ক্লায়েন্টকে সরবরাহ করার জন্য, এবং তারপরে ভবিষ্যতের অনুরোধগুলির সময় ডিক্রিপ্ট করার জন্য একই কীটি ব্যবহার করবেন? স্পষ্টতই বেস 64 বা অন্য কিছু বিভ্রান্তি যথেষ্ট নয়। আপনি এই টোকেনগুলির "বৈধতা" দেওয়ার কৌশলগুলি বিস্তারিতভাবে বর্ণনা করতে পারেন?
ক্রেগ ওটিস

6
যদিও এটি তারিখযুক্ত এবং আমি 2 বছরেরও বেশি সময় ধরে কোডটি স্পর্শ বা আপডেট করেছি না, তবে আমি এই ধারণাগুলি আরও প্রসারিত করার জন্য একটি গিস্ট তৈরি করেছি। gist.github.com/ccashwell/dfc05dd8bd1a75d189d1
ক্রিস ক্যাশওয়েল

উত্তর:


190

ওপিতে বর্ণিত ঠিক মতো আমরা এই কাজটি পরিচালনা করতে পেরেছি এবং আশা করি অন্য কেউ সমাধানটি ব্যবহার করতে পারে। আমরা যা করেছি তা এখানে:

সুরক্ষা প্রসঙ্গটি এর মতো সেট আপ করুন:

<security:http realm="Protected API" use-expressions="true" auto-config="false" create-session="stateless" entry-point-ref="CustomAuthenticationEntryPoint">
    <security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
    <security:intercept-url pattern="/authenticate" access="permitAll"/>
    <security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>

<bean id="CustomAuthenticationEntryPoint"
    class="com.demo.api.support.spring.CustomAuthenticationEntryPoint" />

<bean id="authenticationTokenProcessingFilter"
    class="com.demo.api.support.spring.AuthenticationTokenProcessingFilter" >
    <constructor-arg ref="authenticationManager" />
</bean>

আপনি দেখতে পাচ্ছেন, আমরা একটি কাস্টম তৈরি করেছি AuthenticationEntryPoint, যা 401 Unauthorizedআমাদের দ্বারা ফিল্টার চেইনে অনুরোধটি প্রমাণিত না হলে মূলত একটি ফিরিয়ে দেয় AuthenticationTokenProcessingFilter

কাস্টমআথেন্টিফিকেশন এন্ট্রিপয়েন্ট :

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Authentication token was either missing or invalid." );
    }
}

প্রমাণীকরণ টোকেনপ্রসেসিং ফিল্টার :

public class AuthenticationTokenProcessingFilter extends GenericFilterBean {

    @Autowired UserService userService;
    @Autowired TokenUtils tokenUtils;
    AuthenticationManager authManager;

    public AuthenticationTokenProcessingFilter(AuthenticationManager authManager) {
        this.authManager = authManager;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        @SuppressWarnings("unchecked")
        Map<String, String[]> parms = request.getParameterMap();

        if(parms.containsKey("token")) {
            String token = parms.get("token")[0]; // grab the first "token" parameter

            // validate the token
            if (tokenUtils.validate(token)) {
                // determine the user based on the (already validated) token
                UserDetails userDetails = tokenUtils.getUserFromToken(token);
                // build an Authentication object with the user's info
                UsernamePasswordAuthenticationToken authentication = 
                        new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails((HttpServletRequest) request));
                // set the authentication into the SecurityContext
                SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(authentication));         
            }
        }
        // continue thru the filter chain
        chain.doFilter(request, response);
    }
}

স্পষ্টতই, TokenUtilsকিছু প্রাইভেট (এবং খুব কেস-নির্দিষ্ট) কোড রয়েছে এবং সহজেই ভাগ করা যায় না। এখানে এর ইন্টারফেস:

public interface TokenUtils {
    String getToken(UserDetails userDetails);
    String getToken(UserDetails userDetails, Long expiration);
    boolean validate(String token);
    UserDetails getUserFromToken(String token);
}

এটি আপনাকে একটি ভাল শুরু করতে হবে। শুভ কোডিং। :)


টোকেনটি যখন অনুরোধের সাথে প্রেরণ করা হচ্ছে তখন কী টোকেনকে প্রমাণীকরণ করা দরকার? কীভাবে সরাসরি ব্যবহারকারীর নাম তথ্য পাবেন এবং বর্তমান প্রসঙ্গে / অনুরোধে সেট করবেন?
ফিশার

1
@ স্প্রিং আমি এগুলিকে কোথাও সংরক্ষণ করি না ... টোকেনের পুরো ধারণাটি হ'ল প্রতিটি অনুরোধের সাথে এটি পাস করা দরকার, এবং এর বৈধতা নির্ধারণ করার জন্য এটি আংশিকভাবে (আংশিকভাবে) বিভক্ত করা যেতে পারে (সুতরাং validate(...)পদ্ধতিটি)। এটি গুরুত্বপূর্ণ কারণ আমি চাই সার্ভারটি রাজ্যহীন থাকুক। আমি কল্পনা করব আপনি বসন্তটি ব্যবহার না করেই এই পদ্ধতিটি ব্যবহার করতে পারেন।
ক্রিস ক্যাশওয়েল

1
ক্লায়েন্ট যদি ব্রাউজার হয় তবে টোকেন কীভাবে সংরক্ষণ করা যায়? বা আপনার প্রতিটি অনুরোধের জন্য প্রমাণীকরণ আবার করতে হবে?
শিক্ষানবিশ_

2
দুর্দান্ত টিপস @ ক্রিসক্যাশওয়েল - যে অংশটি আমি খুঁজে পাচ্ছি না তা হ'ল আপনি কোথায় ব্যবহারকারীর শংসাপত্রগুলি প্রমাণীকরণ করবেন এবং একটি টোকেন ফেরত প্রেরণ করবেন? আমি অনুমান করব এটি / প্রমাণীকরণের শেষ পয়েন্টটির প্ররোচনায় কোথাও হওয়া উচিত। আমি কি সঠিক ? না হলে / প্রমাণীকরণের লক্ষ্য কী?
যোনাতন মামান

3
প্রমাণীকরণ ব্যবস্থাপনার ভিতরে কী আছে?
MoienGK

25

আপনি ডাইজেস্ট অ্যাক্সেস প্রমাণীকরণ বিবেচনা করতে পারেন । মূলত প্রোটোকলটি নিম্নরূপ:

  1. অনুরোধ ক্লায়েন্ট থেকে করা হয়
  2. সার্ভার একটি অনন্য নন স্ট্রিংয়ের সাথে সাড়া দেয়
  3. ক্লায়েন্ট ননসের সাথে এমডি 5 হ্যাশ করে একটি ব্যবহারকারীর নাম এবং পাসওয়ার্ড (এবং কিছু অন্যান্য মান) সরবরাহ করে; এই হ্যাশটি এইচএ 1 হিসাবে পরিচিত
  4. সার্ভার তখন ক্লায়েন্টের পরিচয় যাচাই করতে এবং অনুরোধ করা সামগ্রীগুলি পরিবেশন করতে সক্ষম
  5. সার্ভারটি নতুন গিরি সরবরাহ না করা পর্যন্ত ননসের সাথে যোগাযোগ অব্যাহত রাখতে পারে (রিপ্লে আক্রমণগুলি দূর করতে একটি কাউন্টার ব্যবহার করা হয়)

এই সমস্ত যোগাযোগের শিরোনাম দিয়ে তৈরি করা হয়েছে, যা jmort253 পয়েন্ট হিসাবে উল্লেখ করা হয়েছে, সাধারণত ইউআরএল প্যারামিটারে সংবেদনশীল উপাদানের যোগাযোগের চেয়ে বেশি সুরক্ষিত।

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


1
যদিও এটি একটি সম্ভাব্য পন্থা, একটি টোকেন পুনরুদ্ধার করতে যে কয়েকটি রাউন্ড ট্রিপ করতে হবে তা এটিকে কিছুটা অনাকাঙ্ক্ষিত করে তোলে।
ক্রিস ক্যাশওয়েল

যদি আপনার ক্লায়েন্ট এইচটিটিপি প্রমাণীকরণের বিবরণ অনুসরণ করে তবে এই রাউন্ড ট্রিপগুলি কেবলমাত্র প্রথম কল এবং যখন 5 হয় upon
মার্কাস মালকুশ

5

তথ্য বহনকারী টোকেন সম্পর্কিত, জেএসএন ওয়েব টোকেনস ( http://jwt.io ) একটি উজ্জ্বল প্রযুক্তি। মূল ধারণাটি হ'ল তথ্য উপাদানগুলিকে (দাবিগুলি) টোকনে এম্বেড করা এবং তারপরে পুরো টোকেনে স্বাক্ষর করা যাতে বৈধকরণের শেষটি যাচাই করতে পারে যে দাবিগুলি সত্যই বিশ্বাসযোগ্য।

আমি এই জাভা বাস্তবায়নটি ব্যবহার করি: https://bitbucket.org/b_c/jose4j/wiki/Home

একটি স্প্রিং মডিউলও রয়েছে (বসন্ত-সুরক্ষা-জেউডব্লিউটি), তবে এটি কী সমর্থন করে আমি তা লক্ষ্য করি নি।


2

আপনি JSON ওয়েব টোকেনগুলির সাথে কেন OAuth ব্যবহার শুরু করবেন না

http://projects.spring.io/spring-security-oauth/

OAuth2 একটি মানকৃত অনুমোদন প্রোটোকল / কাঠামো। অফিসিয়াল OAuth2 নির্দিষ্টকরণ অনুসারে :

আপনি এখানে আরও তথ্য পেতে পারেন

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