জ্যাকস-আরএস এবং জার্সির সাথে টোকেন-ভিত্তিক অনুমোদনের জন্য বেস্ট অনুশীলন


459

আমি জার্সিতে টোকেন-ভিত্তিক প্রমাণীকরণ সক্ষম করার জন্য একটি উপায় খুঁজছি। আমি চেষ্টা করছি কোনও বিশেষ কাঠামো ব্যবহার না করার জন্য। এটা কি সম্ভব?

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

আমি প্রতিটি অনুরোধের জন্য একটি কাস্টম ফিল্টার ব্যবহার করার কথা ভাবছিলাম এবং @PreAuthorize("hasRole('ROLE')") আমি কেবল ভেবেছিলাম যে এটি টোকেনটি বৈধ কিনা তা পরীক্ষা করার জন্য ডাটাবেসটিতে প্রচুর অনুরোধ ঘটায়।

অথবা ফিল্টার তৈরি করে না এবং প্রতিটি অনুরোধে একটি পরম টোকেন রেখেছেন? যাতে প্রতিটি এপিআই প্রথমে টোকেনটি পরীক্ষা করে এবং উত্স পুনরুদ্ধারের জন্য কিছু কার্যকর করে।

উত্তর:


1387

টোকেন-ভিত্তিক প্রমাণীকরণ কীভাবে কাজ করে

টোকেন-ভিত্তিক প্রমাণীকরণে ক্লায়েন্ট টোকেন নামক এক টুকরো ডেটার জন্য হার্ড শংসাপত্রগুলি (যেমন ব্যবহারকারীর নাম এবং পাসওয়ার্ড) বিনিময় করে । প্রতিটি অনুরোধের জন্য, হার্ড শংসাপত্রগুলি প্রেরণের পরিবর্তে ক্লায়েন্ট প্রমাণীকরণ এবং তারপরে অনুমোদনের জন্য টোকেনটি সার্ভারে প্রেরণ করবে।

কয়েকটি কথায়, টোকেনের উপর ভিত্তি করে একটি প্রমাণীকরণ স্কিম এই পদক্ষেপগুলি অনুসরণ করে:

  1. ক্লায়েন্ট তাদের শংসাপত্রগুলি (ব্যবহারকারীর নাম এবং পাসওয়ার্ড) সার্ভারে প্রেরণ করে।
  2. সার্ভার শংসাপত্রগুলি প্রমাণীকরণ করে এবং যদি সেগুলি বৈধ হয় তবে ব্যবহারকারীর জন্য একটি টোকেন তৈরি করে।
  3. সার্ভার ব্যবহারকারী শনাক্তকারী এবং একটি মেয়াদোত্তীকরণের তারিখের সাথে পূর্ববর্তী উত্পন্ন টোকেনটিকে কিছু স্টোরেজে সঞ্চয় করে।
  4. সার্ভারটি উত্পন্ন টোকেনটি ক্লায়েন্টকে প্রেরণ করে।
  5. ক্লায়েন্ট প্রতিটি অনুরোধে সার্ভারে টোকেন প্রেরণ করে।
  6. সার্ভার, প্রতিটি অনুরোধে আগত অনুরোধ থেকে টোকেনটি বের করে। টোকেন সহ, সার্ভার প্রমাণীকরণ সম্পাদন করতে ব্যবহারকারীর বিবরণ সন্ধান করে।
    • টোকেনটি বৈধ হলে সার্ভারটি অনুরোধটি গ্রহণ করে।
    • টোকেনটি অবৈধ থাকলে সার্ভারটি অনুরোধটি প্রত্যাখ্যান করে।
  7. প্রমাণীকরণটি সম্পাদন করা হয়ে গেলে সার্ভার অনুমোদন দেয়।
  8. সার্ভারটি টোকেনগুলি রিফ্রেশ করার জন্য একটি শেষ পয়েন্ট সরবরাহ করতে পারে।

দ্রষ্টব্য: যদি সার্ভার একটি স্বাক্ষরিত টোকেন জারি করে থাকে (যেমন জেডাব্লুটি, যা আপনাকে রাজ্যবিহীন প্রমাণীকরণ সম্পাদন করতে দেয় ) তৃতীয় পদক্ষেপের প্রয়োজন নেই ।

আপনি জ্যাক্স-আরএস ২.০ (জার্সি, আরএসটিইসি এবং অ্যাপাচি সিএক্সএফ) দিয়ে কী করতে পারেন

এই দ্রবণটি কোনও বিক্রেতার নির্দিষ্ট সমাধান এড়িয়ে কেবল জ্যাকস-আরএস ২.০ এপিআই ব্যবহার করে । সুতরাং, এটি জার্সি , আরএসটিসি এবং অ্যাপাচি সিএক্সএফ-এর মতো জ্যাকস-আরএস 2.0 বাস্তবায়নের সাথে কাজ করা উচিত ।

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

কোনও ব্যবহারকারীকে তাদের ব্যবহারকারীর নাম এবং পাসওয়ার্ড দিয়ে প্রমাণীকরণ এবং একটি টোকেন জারি করা

একটি JAX-RS সংস্থান পদ্ধতি তৈরি করুন যা শংসাপত্রগুলি (ব্যবহারকারীর নাম এবং পাসওয়ার্ড) গ্রহণ করে এবং বৈধ করে এবং ব্যবহারকারীর জন্য একটি টোকেন জারি করে:

@Path("/authentication")
public class AuthenticationEndpoint {

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Response authenticateUser(@FormParam("username") String username, 
                                     @FormParam("password") String password) {

        try {

            // Authenticate the user using the credentials provided
            authenticate(username, password);

            // Issue a token for the user
            String token = issueToken(username);

            // Return the token on the response
            return Response.ok(token).build();

        } catch (Exception e) {
            return Response.status(Response.Status.FORBIDDEN).build();
        }      
    }

    private void authenticate(String username, String password) throws Exception {
        // Authenticate against a database, LDAP, file or whatever
        // Throw an Exception if the credentials are invalid
    }

    private String issueToken(String username) {
        // Issue a token (can be a random String persisted to a database or a JWT token)
        // The issued token must be associated to a user
        // Return the issued token
    }
}

শংসাপত্রগুলি বৈধকরণের সময় যদি কোনও ব্যতিক্রম ছুঁড়ে ফেলা হয় তবে স্থিতি 403(নিষিদ্ধ) সহ একটি প্রতিক্রিয়া ফিরে আসবে।

শংসাপত্রগুলি সফলভাবে যাচাই করা হলে, স্থিতি 200(ঠিক আছে) সহ একটি প্রতিক্রিয়া ফিরিয়ে দেওয়া হবে এবং জারি টোকেনটি ক্লায়েন্টকে প্রতিক্রিয়া পেডে প্রেরণ করা হবে। ক্লায়েন্ট অবশ্যই প্রতিটি অনুরোধে সার্ভারে টোকেন প্রেরণ করতে হবে।

গ্রাস করার সময় application/x-www-form-urlencoded, ক্লায়েন্টকে অনুরোধ পেইডে নিম্নলিখিত ফর্ম্যাটে শংসাপত্রগুলি প্রেরণ করতে হবে:

username=admin&password=123456

ফর্ম প্যারামগুলির পরিবর্তে, ব্যবহারকারীর নাম এবং পাসওয়ার্ডটি একটি শ্রেণিতে আবৃত করা সম্ভব:

public class Credentials implements Serializable {

    private String username;
    private String password;

    // Getters and setters omitted
}

এবং তারপরে এটি JSON হিসাবে গ্রাস করুন:

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response authenticateUser(Credentials credentials) {

    String username = credentials.getUsername();
    String password = credentials.getPassword();

    // Authenticate the user, issue a token and return a response
}

এই পদ্ধতির ব্যবহার করে, ক্লায়েন্টকে অনুরোধের পে-লোডে নিম্নলিখিত ফর্ম্যাটে শংসাপত্রগুলি প্রেরণ করতে হবে:

{
  "username": "admin",
  "password": "123456"
}

অনুরোধ থেকে টোকেনটি বের করে এটিকে বৈধ করে তোলা হচ্ছে

ক্লায়েন্টটির Authorizationঅনুরোধের মানক HTTP শিরোনামে টোকেন প্রেরণ করা উচিত । উদাহরণ স্বরূপ:

Authorization: Bearer <token-goes-here>

মান HTTP শিরোলেখ নাম দুর্ভাগা কারণ এটি বহন করা হয় প্রমাণীকরণ তথ্য, না অনুমোদন । তবে এটি সার্ভারে শংসাপত্র প্রেরণের জন্য আদর্শ এইচটিটিপি শিরোনাম।

জ্যাকস-আরএস সরবরাহ করে @NameBinding, একটি মেটা-টীকা ফিল্টার এবং ইন্টারসেপ্টরগুলিকে রিসোর্স ক্লাস এবং পদ্ধতিতে আবদ্ধ করতে অন্যান্য টীকা তৈরি করতে ব্যবহৃত হয়। @Securedনিম্নলিখিত হিসাবে একটি টীকা সংজ্ঞায়িত করুন :

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured { }

উপরোক্ত সংজ্ঞায়িত নাম-বাঁধাই করা টীকাগুলি কোনও ফিল্টার শ্রেণি সাজানোর জন্য ব্যবহৃত হবে, যা প্রয়োগ করে ContainerRequestFilter, কোনও উত্স পদ্ধতিতে পরিচালনা করার আগে আপনাকে অনুরোধটি আটকাতে দেয়। ContainerRequestContextHTTP অনুরোধ হেডার অ্যাক্সেস করতে এবং তারপর টোকেন নিষ্কর্ষ ব্যবহার করা যেতে পারে:

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    private static final String REALM = "example";
    private static final String AUTHENTICATION_SCHEME = "Bearer";

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Get the Authorization header from the request
        String authorizationHeader =
                requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);

        // Validate the Authorization header
        if (!isTokenBasedAuthentication(authorizationHeader)) {
            abortWithUnauthorized(requestContext);
            return;
        }

        // Extract the token from the Authorization header
        String token = authorizationHeader
                            .substring(AUTHENTICATION_SCHEME.length()).trim();

        try {

            // Validate the token
            validateToken(token);

        } catch (Exception e) {
            abortWithUnauthorized(requestContext);
        }
    }

    private boolean isTokenBasedAuthentication(String authorizationHeader) {

        // Check if the Authorization header is valid
        // It must not be null and must be prefixed with "Bearer" plus a whitespace
        // The authentication scheme comparison must be case-insensitive
        return authorizationHeader != null && authorizationHeader.toLowerCase()
                    .startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
    }

    private void abortWithUnauthorized(ContainerRequestContext requestContext) {

        // Abort the filter chain with a 401 status code response
        // The WWW-Authenticate header is sent along with the response
        requestContext.abortWith(
                Response.status(Response.Status.UNAUTHORIZED)
                        .header(HttpHeaders.WWW_AUTHENTICATE, 
                                AUTHENTICATION_SCHEME + " realm=\"" + REALM + "\"")
                        .build());
    }

    private void validateToken(String token) throws Exception {
        // Check if the token was issued by the server and if it's not expired
        // Throw an Exception if the token is invalid
    }
}

টোকেন বৈধকরণের সময় যদি কোনও সমস্যা হয় তবে স্থিতির সাথে একটি প্রতিক্রিয়া 401(অননুমোদিত) ফিরে আসবে। অন্যথায় অনুরোধটি একটি উত্স পদ্ধতিতে এগিয়ে যাবে।

আপনার REST শেষের দিকগুলি সুরক্ষিত করা

রিসোর্স পদ্ধতি বা রিসোর্স ক্লাসগুলিতে প্রমাণীকরণ ফিল্টারকে বেঁধে রাখতে, তাদের @Securedউপরের তৈরি টীকায় টিকা দিয়ে দিন । বর্ণিত পদ্ধতি এবং / অথবা ক্লাসগুলির জন্য, ফিল্টারটি কার্যকর করা হবে। এর মানে যে এই ধরনের এন্ড পয়েন্ট হবে শুধুমাত্র যদি অনুরোধ করে একটি বৈধ টোকেন সাথে সঞ্চালিত হয় পৌঁছে যেতে।

কিছু পদ্ধতি বা শ্রেণীর যদি প্রমাণীকরণের প্রয়োজন না হয় তবে কেবল এগুলি বর্ননা করবেন না:

@Path("/example")
public class ExampleResource {

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response myUnsecuredMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Secured
        // The authentication filter won't be executed before invoking this method
        ...
    }

    @DELETE
    @Secured
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response mySecuredMethod(@PathParam("id") Long id) {
        // This method is annotated with @Secured
        // The authentication filter will be executed before invoking this method
        // The HTTP request must be performed with a valid token
        ...
    }
}

উপরে প্রদর্শিত উদাহরণে, ফিল্টারটি কেবলমাত্র সেই mySecuredMethod(Long)পদ্ধতির জন্যই কার্যকর করা হবে কারণ এটির সাথে এটি টিকানো আছে @Secured

বর্তমান ব্যবহারকারীকে চিহ্নিত করা

এটি খুব সম্ভবত আপনার অনুরোধটি সম্পাদনকারী ব্যবহারকারীকে আপনার REST এপিআই পুনরায় জানা উচিত। নিম্নলিখিতটি অর্জনের জন্য ব্যবহার করা যেতে পারে:

বর্তমান অনুরোধের সুরক্ষা প্রসঙ্গে ওভাররাইড করা

আপনার ContainerRequestFilter.filter(ContainerRequestContext)পদ্ধতির মধ্যে, SecurityContextবর্তমান অনুরোধের জন্য একটি নতুন উদাহরণ সেট করা যেতে পারে। তারপরে ওভাররাইড করুন SecurityContext.getUserPrincipal(), একটি Principalউদাহরণ ফিরে আসুন:

final SecurityContext currentSecurityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(new SecurityContext() {

        @Override
        public Principal getUserPrincipal() {
            return () -> username;
        }

    @Override
    public boolean isUserInRole(String role) {
        return true;
    }

    @Override
    public boolean isSecure() {
        return currentSecurityContext.isSecure();
    }

    @Override
    public String getAuthenticationScheme() {
        return AUTHENTICATION_SCHEME;
    }
});

ব্যবহারকারী পরিচয়কারী (ব্যবহারকারীর নাম) সন্ধান করতে টোকেনটি ব্যবহার করুন, এটির Principalনাম হবে।

উদ্বুদ্ধ SecurityContextকোনো Jax-আরএস রিসোর্স ক্লাসে:

@Context
SecurityContext securityContext;

জ্যাকস-আরএস রিসোর্স পদ্ধতিতে একই কাজ করা যেতে পারে:

@GET
@Secured
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response myMethod(@PathParam("id") Long id, 
                         @Context SecurityContext securityContext) {
    ...
}

এবং তারপরে Principal:

Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();

সিডিআই ব্যবহার করে (প্রসঙ্গ এবং নির্ভরতা ইনজেকশন)

যদি কোনও কারণে আপনি ওভাররাইড করতে না চান SecurityContext, আপনি সিডিআই (প্রসঙ্গ এবং নির্ভরতা ইনজেকশন) ব্যবহার করতে পারেন, যা ইভেন্ট এবং প্রযোজকগুলির মতো দরকারী বৈশিষ্ট্য সরবরাহ করে।

সিডিআই কোয়ালিফায়ার তৈরি করুন:

@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER })
public @interface AuthenticatedUser { }

আপনার AuthenticationFilterউপরের তৈরিতে, এটির সাথে কোনও Eventটীকাগুলি ইনজেকশন করুন @AuthenticatedUser:

@Inject
@AuthenticatedUser
Event<String> userAuthenticatedEvent;

যদি প্রমাণীকরণটি সফল হয়, ইভেন্টটি ব্যবহারকারীর নামটি প্যারামিটার হিসাবে পাস করার মাধ্যমে আগুন জ্বালিয়ে দিন (মনে রাখবেন, কোনও ব্যবহারকারীর জন্য টোকন জারি করা হয় এবং ব্যবহারকারী সনাক্তকারীকে সন্ধান করতে টোকন ব্যবহৃত হবে):

userAuthenticatedEvent.fire(username);

এটি খুব সম্ভবত এমন একটি শ্রেণি রয়েছে যা আপনার অ্যাপ্লিকেশনটিতে একজন ব্যবহারকারীকে উপস্থাপন করে। এই ক্লাস কল করুন User

প্রমাণীকরণ ইভেন্টটি পরিচালনা করতে একটি সিডিআই বিন তৈরি করুন, Userসংশ্লিষ্ট ব্যবহারকারী নামটির সাথে একটি উদাহরণ সন্ধান করুন এবং এটি authenticatedUserনির্মাতা ক্ষেত্রে নির্ধারণ করুন :

@RequestScoped
public class AuthenticatedUserProducer {

    @Produces
    @RequestScoped
    @AuthenticatedUser
    private User authenticatedUser;

    public void handleAuthenticationEvent(@Observes @AuthenticatedUser String username) {
        this.authenticatedUser = findUser(username);
    }

    private User findUser(String username) {
        // Hit the the database or a service to find a user by its username and return it
        // Return the User instance
    }
}

authenticatedUserক্ষেত্রটিতে কোনও উত্পাদন করে Userদৃষ্টান্ত যে এই ধরনের Jax-আরএস সেবা, CDI মটরশুটি, সার্ভলেট এবং EJBs যেমন ধারক পরিচালিত মটরশুটি, মধ্যে ইনজেকশনের করা যেতে পারে। কোনও Userউদাহরণ ইনজেক্ট করতে নিম্নলিখিত কোডের টুকরোটি ব্যবহার করুন (আসলে এটি একটি সিডিআই প্রক্সি):

@Inject
@AuthenticatedUser
User authenticatedUser;

নোট করুন যে সিডিআই @Producesটীকা জ্যাক্স-আরএস টীকা থেকে পৃথক@Produces :

@Producesআপনার AuthenticatedUserProducerসিমের মধ্যে সিডিআই টীকাটি ব্যবহার করেছেন তা নিশ্চিত হন ।

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

ওভাররাইডকারী পদ্ধতির তুলনায় SecurityContextসিডিআই পদ্ধতির সাহায্যে জ্যাক্স-আরএস সংস্থান এবং সরবরাহকারী ছাড়া অন্য মটরশুটি থেকে প্রমাণীকৃত ব্যবহারকারী পেতে পারবেন।

ভূমিকা-ভিত্তিক অনুমোদনের সমর্থন

ভূমিকা-ভিত্তিক অনুমোদনের ক্ষেত্রে কীভাবে সহায়তা করবেন সে সম্পর্কে বিস্তারিত জানার জন্য দয়া করে আমার অন্য উত্তরটি দেখুন

টোকেন ইস্যু করা হচ্ছে

একটি টোকেন হতে পারে:

  • ওপাক: মানটি ছাড়া অন্য কোনও বিবরণ প্রকাশ করে না (এলোমেলো স্ট্রিংয়ের মতো)
  • স্ব-অন্তর্নিহিত: টোকেন নিজেই (JWT এর মতো) সম্পর্কে বিশদ রয়েছে details

বিস্তারিত নীচে দেখুন:

টোকেন হিসাবে এলোমেলো স্ট্রিং

একটি টোকেন একটি এলোমেলো স্ট্রিং উত্পন্ন করে এবং এটি ব্যবহারকারীর শনাক্তকারী এবং একটি মেয়াদোত্তীকরণের তারিখের সাথে সাথে একটি ডেটাবেজে জারি রেখে জারি করা যেতে পারে। জাভাতে এলোমেলো স্ট্রিং কীভাবে উত্পন্ন করা যায় তার একটি ভাল উদাহরণ এখানে দেখা যায় । আপনি ব্যবহার করতে পারেন:

Random random = new SecureRandom();
String token = new BigInteger(130, random).toString(32);

জেডব্লিউটি (জেএসন ওয়েব টোকন)

JWT (জেএসএন ওয়েব টোকন) দুটি পক্ষের মধ্যে সুরক্ষিতভাবে দাবি উপস্থাপনের জন্য একটি মান পদ্ধতি এবং আরএফসি 7519 দ্বারা সংজ্ঞায়িত ।

এটি একটি স্ব-অন্তর্ভুক্ত টোকেন এবং এটি আপনাকে দাবিতে বিশদ সংরক্ষণ করতে সক্ষম করে । এই দাবির টোকেন পে লোড যা একটি JSON- যেমন এনকোডেড হয়েছে সঞ্চিত আছে করুন Base64- । এখানে আরএফসি 7519 তে নিবন্ধিত কিছু দাবি এবং সেগুলি কী বোঝাতে চাইছে (আরও তথ্যের জন্য সম্পূর্ণ আরএফসি পড়ুন):

  • iss: অধ্যক্ষ যে টোকেন জারি করেছেন।
  • sub: অধ্যক্ষ যে JWT এর বিষয়।
  • exp: টোকেনের মেয়াদ শেষ হওয়ার তারিখ।
  • nbf: প্রক্রিয়া করার জন্য টোকেনটি গ্রহণযোগ্য হবে এমন সময়।
  • iat: যে সময়টিতে টোকেন জারি করা হয়েছিল।
  • jti: টোকেনের জন্য স্বতন্ত্র সনাক্তকারী।

সচেতন হন যে আপনার অবশ্যই সংবেদনশীল ডেটা যেমন পাসওয়ার্ডকে টোকেনে সংরক্ষণ করবেন না।

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

আপনার যদি জেডাব্লুটি টোকেনগুলি ট্র্যাক করার প্রয়োজন না হয় তবে আপনার প্রয়োজন হবে না। চিন্তাভাবনা, টোকেনগুলি চালিয়ে যাওয়ার দ্বারা, আপনার অ্যাক্সেসটিকে অবৈধ ও বাতিল করার সম্ভাবনা থাকবে। জেডাব্লুটি টোকেনগুলির ট্র্যাক রাখতে, সার্ভারে পুরো টোকেনটি চালিয়ে যাওয়ার পরিবর্তে আপনি টোকেন শনাক্তকারী ( jtiদাবি) সহ কিছু অন্যান্য বিবরণ যেমন আপনার ব্যবহারকারীর জন্য টোকেন জারি করেছেন, মেয়াদ শেষ হওয়ার তারিখ ইত্যাদি চালিয়ে যেতে পারেন

টোকেন চালিয়ে যাওয়ার সময়ে, আপনার ডেটাবেস অনির্দিষ্টকালের জন্য বাড়তে রোধ করার জন্য সর্বদা পুরানোগুলি সরিয়ে ফেলার কথা বিবেচনা করুন।

JWT ব্যবহার করে

জেডাব্লুটি টোকেন ইস্যু এবং বৈধ করার জন্য কয়েকটি জাভা গ্রন্থাগার রয়েছে:

জেডাব্লুটিটির সাথে কাজ করার জন্য আরও কিছু দুর্দান্ত সংস্থানগুলি খুঁজতে, http://jwt.io এ দেখুন ।

জেডাব্লুটিটির সাথে টোকেন প্রত্যাহার পরিচালনা করা

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

jtiদাবি টোকেনটি টোকেনটি আইডেন্টিফায়ার সঞ্চয় করতে ব্যবহার করা উচিত। টোকেনটি বৈধকরণের সময়, নিশ্চিত করুন যে jtiসার্ভারের পাশে আপনার টোকেন শনাক্তকারীদের বিরুদ্ধে দাবির মান পরীক্ষা করে তা প্রত্যাহার করা হয়নি ।

সুরক্ষার উদ্দেশ্যে, কোনও ব্যবহারকারী যখন তাদের পাসওয়ার্ড পরিবর্তন করেন তখন সমস্ত টোকেন প্রত্যাহার করুন।

অতিরিক্ত তথ্য

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

The server stores the previously generated token in some storage along with the user identifier and an expiration date. The server sends the generated token to the client. এটি কীভাবে বিশ্রামে?
স্কটিসিস

3
@ স্কটিটিউস টোকেন ভিত্তিক প্রমাণীকরণ কীভাবে সার্ভার জারি করেছে সেই টোকেনটিকে স্মরণ করে works আপনি স্টেটলেস প্রমাণীকরণের জন্য জেডাব্লুটি টোকেন ব্যবহার করতে পারেন।
ক্যাসিওমোলিন

প্লেইনের পরিবর্তে হ্যাশ পাসওয়ার্ড প্রেরণের বিষয়ে (সার্ভার-উত্পন্ন উত্সাহীন হ্যাশ) কী হবে? এটি কী সুরক্ষা স্তর বাড়ায় (উদাহরণস্বরূপ যখন https ব্যবহার করবেন না)? মাঝখানে মানুষের ক্ষেত্রে - তিনি একটি অধিবেশন হাইজ্যাক করতে সক্ষম হবেন, তবে কমপক্ষে তিনি পাসওয়ার্ডটি পাবেন না
ডেনিস ইস্তকোভিচ

15
আমি বিশ্বাস করতে পারি না এটি সরকারী নথিতে নেই।
ড্যানিয়েল এম।

2
@grep in REST এ সার্ভার সাইডে সেশন বলে কিছুই নেই। ফলস্বরূপ, সেশন রাষ্ট্রটি ক্লায়েন্টের পক্ষে পরিচালিত হয়।
ক্যাসিওমোলিন

98

এই উত্তরটি সমস্ত অনুমোদনের বিষয়ে এবং এটি প্রমাণীকরণ সম্পর্কে আমার পূর্ববর্তী উত্তরের পরিপূরক

অন্য উত্তর কেন ? আমি জেএসআর -250 টীকাতে কীভাবে সমর্থন করব তার বিশদ যুক্ত করে আমার পূর্ববর্তী উত্তরটি প্রসারিত করার চেষ্টা করেছি। তবে আসল উত্তরটি খুব দীর্ঘ হয়ে গেছে এবং 30,000 অক্ষরের সর্বাধিক দৈর্ঘ্য অতিক্রম করেছে । সুতরাং আমি অন্য উত্তরটি প্রমাণীকরণ সম্পাদন এবং টোকেন প্রদানের উপর ফোকাস রেখে পুরো উত্তর অনুমোদনের বিশদটি এই উত্তরে স্থানান্তরিত করেছি।


@Securedটিকা রচনা সহ ভূমিকা-ভিত্তিক অনুমোদনের পক্ষে সহায়তা করা

অন্যান্য উত্তরে প্রমাণীকরণের প্রবাহটি দেখানো ছাড়াও , ভূমিকা-ভিত্তিক অনুমোদনের জন্য REST টিপয়েন্টগুলিতে সমর্থন করা যেতে পারে।

একটি গণনা তৈরি করুন এবং আপনার প্রয়োজন অনুসারে ভূমিকা সংজ্ঞা দিন:

public enum Role {
    ROLE_1,
    ROLE_2,
    ROLE_3
}

@Securedভূমিকাগুলিকে সমর্থন করার আগে তৈরি নাম বাইন্ডিং টীকা পরিবর্তন করুন :

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured {
    Role[] value() default {};
}

এবং তারপরে @Securedঅনুমোদনের জন্য সংস্থান শ্রেণি এবং পদ্ধতিগুলি বর্নিত করুন । পদ্ধতির টীকাগুলি শ্রেণীর টিকাগুলি ওভাররাইড করবে:

@Path("/example")
@Secured({Role.ROLE_1})
public class ExampleResource {

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response myMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Secured
        // But it's declared within a class annotated with @Secured({Role.ROLE_1})
        // So it only can be executed by the users who have the ROLE_1 role
        ...
    }

    @DELETE
    @Path("{id}")    
    @Produces(MediaType.APPLICATION_JSON)
    @Secured({Role.ROLE_1, Role.ROLE_2})
    public Response myOtherMethod(@PathParam("id") Long id) {
        // This method is annotated with @Secured({Role.ROLE_1, Role.ROLE_2})
        // The method annotation overrides the class annotation
        // So it only can be executed by the users who have the ROLE_1 or ROLE_2 roles
        ...
    }
}

AUTHORIZATIONঅগ্রাধিকার সহ একটি ফিল্টার তৈরি করুন , যা AUTHENTICATIONপূর্বনির্ধারিত অগ্রাধিকার ফিল্টারের পরে কার্যকর করা হয় ।

ResourceInfoরিসোর্স পেতে ব্যবহার করা যেতে পারে Methodও রিসোর্স Classযে অনুরোধ হ্যান্ডেল হবে এবং তারপর নিষ্কর্ষ @Securedতাদের কাছ থেকে টীকা:

@Secured
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Get the resource class which matches with the requested URL
        // Extract the roles declared by it
        Class<?> resourceClass = resourceInfo.getResourceClass();
        List<Role> classRoles = extractRoles(resourceClass);

        // Get the resource method which matches with the requested URL
        // Extract the roles declared by it
        Method resourceMethod = resourceInfo.getResourceMethod();
        List<Role> methodRoles = extractRoles(resourceMethod);

        try {

            // Check if the user is allowed to execute the method
            // The method annotations override the class annotations
            if (methodRoles.isEmpty()) {
                checkPermissions(classRoles);
            } else {
                checkPermissions(methodRoles);
            }

        } catch (Exception e) {
            requestContext.abortWith(
                Response.status(Response.Status.FORBIDDEN).build());
        }
    }

    // Extract the roles from the annotated element
    private List<Role> extractRoles(AnnotatedElement annotatedElement) {
        if (annotatedElement == null) {
            return new ArrayList<Role>();
        } else {
            Secured secured = annotatedElement.getAnnotation(Secured.class);
            if (secured == null) {
                return new ArrayList<Role>();
            } else {
                Role[] allowedRoles = secured.value();
                return Arrays.asList(allowedRoles);
            }
        }
    }

    private void checkPermissions(List<Role> allowedRoles) throws Exception {
        // Check if the user contains one of the allowed roles
        // Throw an Exception if the user has not permission to execute the method
    }
}

ব্যবহারকারীর অপারেশন চালানোর অনুমতি না থাকলে অনুরোধটি একটি 403(নিষিদ্ধ) দিয়ে বাতিল করা হয় ।

যে ব্যবহারকারীটি অনুরোধটি করছেন তা জানতে, আমার পূর্ববর্তী উত্তরটি দেখুন । আপনি যে পদ্ধতির দিকে যান তার উপর নির্ভর করে আপনি এটি SecurityContext(যা ইতিমধ্যে সেট করা উচিত ContainerRequestContext) থেকে সিডিআই ব্যবহার করে এটি পেতে পারেন।

যদি কোনও @Securedটীকাতে কোনও ভূমিকা ঘোষিত না হয় তবে আপনি ধরে নিতে পারেন যে সমস্ত অনুমোদনপ্রাপ্ত ব্যবহারকারীরা সেই শেষ পয়েন্টটি অ্যাক্সেস করতে পারবেন, ব্যবহারকারীদের ভূমিকা উপেক্ষা করে।

জেএসআর-250 টীকা সহ ভূমিকা-ভিত্তিক অনুমোদনের পক্ষে সহায়তা করা

বিকল্প @Securedহিসাবে উপরে বর্ণিত টীকায় ভূমিকা সংজ্ঞায়নের জন্য , আপনি জেএসআর-250 টীকা যেমন @RolesAllowed, @PermitAllএবং বিবেচনা করতে পারেন @DenyAll

জ্যাকস-আরএস বাক্সের বাইরে এই জাতীয় মন্তব্য সমর্থন করে না, তবে এটি একটি ফিল্টার দিয়ে অর্জন করা যেতে পারে। আপনি যদি সেগুলির সকলকে সমর্থন করতে চান তবে এখানে কয়েকটি বিবেচনার বিষয় মনে রাখবেন:

  • @DenyAllপদ্ধতিতে ক্লাসের উপর @RolesAllowedএবং অগ্রাধিকার লাগে @PermitAll
  • @RolesAllowedপদ্ধতিতে @PermitAllক্লাসের উপর অগ্রাধিকার লাগে ।
  • @PermitAllপদ্ধতিতে @RolesAllowedক্লাসের উপর অগ্রাধিকার লাগে ।
  • @DenyAll ক্লাসের সাথে সংযুক্ত করা যায় না।
  • @RolesAllowedক্লাস উপর ক্লাস উপর প্রাধান্য লাগে @PermitAll

সুতরাং একটি অনুমোদনের ফিল্টার যা জেএসআর-250 টীকা পরীক্ষা করে তা হতে পারে:

@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        Method method = resourceInfo.getResourceMethod();

        // @DenyAll on the method takes precedence over @RolesAllowed and @PermitAll
        if (method.isAnnotationPresent(DenyAll.class)) {
            refuseRequest();
        }

        // @RolesAllowed on the method takes precedence over @PermitAll
        RolesAllowed rolesAllowed = method.getAnnotation(RolesAllowed.class);
        if (rolesAllowed != null) {
            performAuthorization(rolesAllowed.value(), requestContext);
            return;
        }

        // @PermitAll on the method takes precedence over @RolesAllowed on the class
        if (method.isAnnotationPresent(PermitAll.class)) {
            // Do nothing
            return;
        }

        // @DenyAll can't be attached to classes

        // @RolesAllowed on the class takes precedence over @PermitAll on the class
        rolesAllowed = 
            resourceInfo.getResourceClass().getAnnotation(RolesAllowed.class);
        if (rolesAllowed != null) {
            performAuthorization(rolesAllowed.value(), requestContext);
        }

        // @PermitAll on the class
        if (resourceInfo.getResourceClass().isAnnotationPresent(PermitAll.class)) {
            // Do nothing
            return;
        }

        // Authentication is required for non-annotated methods
        if (!isAuthenticated(requestContext)) {
            refuseRequest();
        }
    }

    /**
     * Perform authorization based on roles.
     *
     * @param rolesAllowed
     * @param requestContext
     */
    private void performAuthorization(String[] rolesAllowed, 
                                      ContainerRequestContext requestContext) {

        if (rolesAllowed.length > 0 && !isAuthenticated(requestContext)) {
            refuseRequest();
        }

        for (final String role : rolesAllowed) {
            if (requestContext.getSecurityContext().isUserInRole(role)) {
                return;
            }
        }

        refuseRequest();
    }

    /**
     * Check if the user is authenticated.
     *
     * @param requestContext
     * @return
     */
    private boolean isAuthenticated(final ContainerRequestContext requestContext) {
        // Return true if the user is authenticated or false otherwise
        // An implementation could be like:
        // return requestContext.getSecurityContext().getUserPrincipal() != null;
    }

    /**
     * Refuse the request.
     */
    private void refuseRequest() {
        throw new AccessDeniedException(
            "You don't have permissions to perform this action.");
    }
}

দ্রষ্টব্য: উপরের বাস্তবায়নটি জার্সির উপর ভিত্তি করে RolesAllowedDynamicFeature। আপনি যদি জার্সি ব্যবহার করেন তবে আপনার নিজের ফিল্টার লেখার দরকার নেই, কেবল বিদ্যমান বাস্তবায়নটি ব্যবহার করুন।


এই মার্জিত সমাধানের সাথে কি কোনও গিথুব সংগ্রহস্থল উপলব্ধ?
ড্যানিয়েল ফেরেরিরা কাস্ত্রো

6
পছন্দ করুন এখানে দেখুন ।
ক্যাসিওমোলিন

অনুমোদনের কোনও অনুমোদিত ব্যবহারকারীর পক্ষ থেকে অনুরোধের অনুরোধের কোনও কার্যকর উপায় আছে কি না এবং সেই ব্যবহারকারী ডেটা পরিবর্তন করতে পারে কারণ তিনি ডেটার "মালিকানাধীন" (যেমন কোনও হ্যাকার অন্য ব্যবহারকারীর নাম পরিবর্তন করতে তার টোকেন ব্যবহার করতে পারবেন না)? আমি জানি আমি প্রতিটি শেষ বিন্দুতে user_id== token.userId, বা এর মতো কিছু পরীক্ষা করে দেখতে পারি তবে এটি খুব পুনরাবৃত্তিযোগ্য।
এমফিনস্টাইন

@ এমফেইনস্টেইন এর একটি উত্তরের জন্য অবশ্যই মন্তব্যগুলিতে আমি এখানে টাইপ করতে পারি তার চেয়ে বেশি অক্ষরের প্রয়োজন। আপনাকে কিছু দিকনির্দেশনা দেওয়ার জন্য, আপনি সারি স্তরের সুরক্ষা সন্ধান করতে পারেন ।
cassiomolin

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