Najlepsza praktyka uwierzytelniania za pomocą tokenów REST za pomocą JAX-RS i Jersey

Szukam sposobu na włączenie uwierzytelniania tokenowego w Jersey. Staram się nie używać żadnych konkretnych ram. Czy to możliwe?

Mój plan to: użytkownik zarejestruje się w moim serwisie internetowym, mój serwis internetowy generuje token, wysyła go do klienta, a Klient zatrzyma go. Następnie klient, dla każdego żądania, wyśle token zamiast nazwy użytkownika i hasła.

Myślałem o użyciu niestandardowego filtra dla każdego żądania i @PreAuthorize("hasRole('ROLE')") ale pomyślałem to powoduje, że wiele żądań do bazy danych sprawdza, czy token jest poprawny.

Czy nie utworzyć filtra i w każdym żądaniu umieścić token param? Tak, że każdy API najpierw sprawdza token, a następnie wykonuje coś, aby odzyskać zasób.

Author: cassiomolin, 2014-11-06

2 answers

Jak działa uwierzytelnianie oparte na tokenach?]} W przypadku uwierzytelniania opartego na tokenie, klient wymienia twarde poświadczenia (takie jak nazwa użytkownika i hasło) na fragment danych o nazwie token. Dla każdego żądania, zamiast wysyłać twarde poświadczenia, klient wyśle token do serwera w celu przeprowadzenia uwierzytelnienia, a następnie autoryzacji.

W kilku słowach schemat uwierzytelniania oparty na tokenach wykonaj następujące kroki:

  1. klient wysyła ich poświadczenia (nazwa użytkownika i hasło) do serwera.
  2. Serwer uwierzytelnia poświadczenia i, jeśli są one ważne, generuje token dla użytkownika.
  3. serwer przechowuje wcześniej wygenerowany token w jakimś magazynie wraz z identyfikatorem użytkownika i datą wygaśnięcia.
  4. Serwer wysyła wygenerowany token do klienta.
  5. klient wysyła token do serwera w każdym żądaniu.
  6. serwer w każdym żądaniu pobiera token z przychodzące żądanie. Dzięki tokenowi serwer wyszukuje dane użytkownika w celu przeprowadzenia uwierzytelnienia.
    • jeśli token jest ważny, serwer akceptuje żądanie.
    • jeśli token jest nieprawidłowy, serwer odrzuca żądanie.
  7. po uwierzytelnieniu serwer wykonuje autoryzację.
  8. serwer może dostarczyć punkt końcowy do odświeżania tokenów.

Uwaga: Krok 3 nie jest wymagany, jeśli serwer JWT, który pozwala na wykonywanie uwierzytelniania bezstanowego ).

[[66]}co można zrobić z JAX-RS 2.0 (Jersey, RESTEasy i Apache CXF) Rozwiązanie to wykorzystuje tylko interfejs API JAX-RS 2.0, unikając wszelkich rozwiązań specyficznych dla dostawcy. Tak więc, powinno działać z implementacjami JAX - RS 2.0, takimi jak Jersey, RESTEasy i Apache CXF.

Warto wspomnieć, że jeśli używasz uwierzytelnianie oparte na tokenie nie polega na standardowych mechanizmach bezpieczeństwa aplikacji internetowych Java EE oferowanych przez kontener serwletów i konfigurowalnych za pomocą deskryptora web.xml aplikacji. To niestandardowe uwierzytelnianie.

W 2007 roku firma została założona przez Johna Cage 'a, a w 2008 roku została założona przez Johna Cage' a.]} W tym celu należy utworzyć metodę JAX-RS, która odbiera i weryfikuje dane uwierzytelniające (nazwę użytkownika i hasło) i wydaje token dla użytkownika.]}
@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
    }
}

Jeśli jakieś wyjątki podczas walidacji danych uwierzytelniających zwracana jest odpowiedź o statusie 403 (zakazana).

Jeśli poświadczenia zostaną pomyślnie zweryfikowane, odpowiedź o statusie 200 (OK) zostanie zwrócona, a wydany token zostanie wysłany do Klienta w ładunku odpowiedzi. Klient musi wysłać token do serwera w każdym żądaniu.

Podczas korzystania z application/x-www-form-urlencoded, klient musi wysłać poświadczenia w następującym formacie w żądaniu ładowność:

username=admin&password=123456
Zamiast form params, możliwe jest zawinięcie nazwy użytkownika i hasła do klasy:
public class Credentials implements Serializable {

    private String username;
    private String password;

    // Getters and setters omitted
}

A następnie skonsumować go jako 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
}
W związku z tym, że Klient nie jest w stanie uzyskać dostępu do danych uwierzytelniających, nie jest w stanie uzyskać dostępu do danych uwierzytelniających.]}
{
  "username": "admin",
  "password": "123456"
}
W związku z tym, że nie jest to możliwe, nie jest to możliwe.]}

Klient powinien wysłać token w standardowym nagłówku HTTP Authorization żądania. Na przykład:

Authorization: Bearer <token-goes-here>

Nazwa standardowego nagłówka HTTP jest niefortunna, ponieważ zawiera informacje uwierzytelniania , a nie autoryzacji. Jest to jednak standardowy nagłówek HTTP do wysyłania poświadczeń do serwera.

JAX-RS zapewnia @NameBinding, meta-adnotacja używana do tworzenia innych adnotacji w celu powiązania filtrów i przechwytywaczy z klasami zasobów i metodami. Zdefiniuj adnotację @Secured w następujący sposób:

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

Powyższa definicja adnotacja wiążąca nazwę zostanie użyta do dekoracji klasy filtra, która implementuje ContainerRequestFilter, pozwala przechwycić żądanie, zanim zostanie ono obsłużone metodą zasobów. Na ContainerRequestContext może być użyty do uzyskania dostępu do nagłówków żądań HTTP, a następnie wyodrębnić token:

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

Jeśli wystąpią jakiekolwiek problemy podczas walidacji tokena, zostanie zwrócona odpowiedź o statusie 401 (nieautoryzowana). W przeciwnym razie żądanie przejdzie do zasobu metoda.

Zabezpieczenie punktów końcowych odpoczynku

Aby powiązać filtr uwierzytelniania z metodami zasobów lub klasami zasobów, Dodaj do nich adnotację @Secured utworzoną powyżej. Dla metod i/lub klas, które są adnotowane, filtr zostanie wykonany. Oznacza to, że takie punkty końcowe zostaną osiągnięte tylko, jeśli żądanie zostanie wykonane z poprawnym tokenem.

Jeśli niektóre metody lub klasy nie wymagają uwierzytelniania, po prostu nie dodawaj adnotacji oni:

@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
        ...
    }
}

W powyższym przykładzie filtr zostanie wykonany tylko{[70] } dla metody mySecuredMethod(Long), ponieważ jest opatrzony adnotacją @Secured.

Identyfikacja aktualnego użytkownika

Jest bardzo prawdopodobne, że będziesz musiał znać użytkownika, który wykonuje żądanie przeciwko twojemu REST API. Do jej osiągnięcia można zastosować następujące podejścia:

Nadpisywanie kontekstu zabezpieczeń bieżącego żądania

W Twoim ContainerRequestFilter.filter(ContainerRequestContext) metoda, Nowa SecurityContext instancja może być ustawiona dla bieżącego żądania. Następnie nadpisać SecurityContext.getUserPrincipal(), powrót a Principal instancja:

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;
    }
});

Użyj tokena, aby wyszukać identyfikator Użytkownika (username), który będzie Principal'imię.

SecurityContext W dowolnej klasie zasobów JAX-RS:

@Context
SecurityContext securityContext;
To samo można zrobić w metodzie zasobów JAX-RS:]}
@GET
@Secured
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response myMethod(@PathParam("id") Long id, 
                         @Context SecurityContext securityContext) {
    ...
}

A następnie uzyskać Principal:

Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();
W tym celu należy skontaktować się z Działem obsługi klienta.]}

Jeśli z jakiegoś powodu nie chcesz nadpisać SecurityContext, możesz użyć CDI (Context and Dependency Injection), który zapewnia przydatne funkcje, takie jak zdarzenia i producenci.

Utwórz kwalifikator CDI:

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

W Twoim AuthenticationFilter utworzonym powyżej, wstrzyknij Event @AuthenticatedUser:

@Inject
@AuthenticatedUser
Event<String> userAuthenticatedEvent;

Jeśli uwierzytelnienie się powiedzie, odpal Zdarzenie przekazujące nazwę użytkownika jako parametr (pamiętaj, że token jest wystawiany dla użytkownika i token będzie używany do wyszukiwania identyfikatora użytkownika):

userAuthenticatedEvent.fire(username);

Jest bardzo prawdopodobne, że istnieje klasa, która reprezentuje użytkownika w Twojej aplikacji. Nazwijmy tę klasę User.

Utwórz CDI bean do obsługi zdarzenia uwierzytelniania, znajdź instancję User z nazwą użytkownika korespondenta i przypisz ją do pola producenta 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
    }
}

Pole authenticatedUser tworzy User instancja, która może być wtryskiwana do kontenerów zarządzanych beans, takich jak usługi JAX-RS, CDI beans, servlets i EJBs. Użyj poniższego kodu, aby wstrzyknąć instancję User (w rzeczywistości jest to proxy CDI):

@Inject
@AuthenticatedUser
User authenticatedUser;

Zauważ, że CDI @Produces adnotacja jest Inna od JAX-RS @Produces adnotacja:

Upewnij się, że używasz CDI @Produces adnotacja w Twojej AuthenticatedUserProducer fasolce.

Kluczem jest tutaj fasola @RequestScoped, pozwala na udostępnianie danych między filtrami i fasolami. Jeśli nie chcesz używać zdarzeń, możesz zmodyfikować filtr, aby przechowywał uwierzytelnionego użytkownika w obszarze żądania, a następnie odczytać go z klas zasobów JAX-RS.

W porównaniu z podejściem, które przewyższa SecurityContext, podejście CDI pozwala uzyskać uwierzytelnionego użytkownika z fasola inna niż JAX-RS zasobów i dostawców.

Wspomaganie autoryzacji opartej na rolach

Proszę odnieść się do mojej innej odpowiedź aby uzyskać szczegółowe informacje na temat obsługi autoryzacji opartej na rolach.

Wydawanie żetonów

Tokenem może być:

  • nieprzezroczyste: nie ujawnia żadnych szczegółów innych niż sama wartość (jak losowy ciąg znaków)
  • samodzielny: zawiera szczegóły dotyczące samego tokena (jak JWT).

Zobacz szczegóły poniżej:

Losowy ciąg znaków jako token

Token może zostać wydany poprzez wygenerowanie losowego ciągu znaków i utrzymanie go w bazie danych wraz z identyfikatorem użytkownika i datą wygaśnięcia. Dobry przykład jak wygenerować losowy ciąg znaków w Javie można zobaczyć tutaj . Można też użyć:

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

JWT (JSON Web Token)

JWT (JSON Web Token) jest standardową metodą bezpiecznego reprezentowania roszczeń między dwiema stronami i jest zdefiniowany przez RFC 7519 .

Jest to samodzielny token i umożliwia przechowywanie szczegółów w oświadczeniach . Dane te są przechowywane w tokenie ładunku, który jest JSON zakodowany jako Base64. Oto kilka twierdzeń zarejestrowanych w RFC 7519 i co one oznaczają (Przeczytaj cały RFC, aby uzyskać więcej informacji):

  • iss: dyrektor, który wydał token.
  • sub: dyrektor, który jest temat JWT.
  • exp: Data wygaśnięcia tokenu.
  • nbf: Czas, w którym token zacznie być akceptowany do przetwarzania.
  • iat: czas wystawienia żetonu.
  • jti: niepowtarzalny identyfikator tokena.

Pamiętaj, że nie możesz przechowywać poufnych danych, takich jak hasła, w tokenie.

Ładunek może być odczytany przez Klienta i integralność tokena można łatwo sprawdzić, weryfikując jego podpis na serwerze. Podpis jest tym, co zapobiega manipulowaniu tokenem.

Nie musisz utrzymywać tokenów JWT, jeśli nie musisz ich śledzić. Mimo, że utrzymując żetony, będziesz miał możliwość unieważnienia i cofnięcia dostępu do nich. Aby śledzić tokeny JWT, zamiast utrzymywać cały token na serwerze, możesz zachować identyfikator tokenu(jti oświadczenie) wraz z innymi szczegółami, takimi jak użytkownik, dla którego wydano token, data wygaśnięcia itp.

Podczas utrzymywania tokenów, zawsze rozważ usunięcie starych, aby zapobiec powiększaniu się bazy danych w nieskończoność.

Używanie JWT

Istnieje kilka bibliotek Javy do wydawania i walidacji tokenów JWT, takich jak:]}

Aby znaleźć inne wspaniałe zasoby do pracy z JWT, zajrzyj do http://jwt.io .

Obsługa odwoływania tokenów za pomocą JWT

Jeśli chcesz odwołać żetony, musisz ich śledzić. Nie musisz przechowywać całego tokenu po stronie serwera, przechowuj tylko identyfikator tokena (który musi być unikalny) i niektóre metadane, jeśli potrzebujesz. Do identyfikatora tokena można użyć UUID.

The jti oświadczenie należy wykorzystać do przechowywania identyfikatora tokena na token. Podczas walidacji tokena upewnij się, że nie został on odwołany, sprawdzając wartość jti twierdzenie o identyfikatorach tokenów po stronie serwera.

Ze względów bezpieczeństwa, odwołaj wszystkie tokeny dla użytkownika, gdy zmieni on hasło.

Informacje dodatkowe

  • nie ma znaczenia, jakiego typu uwierzytelnienia zdecydujesz się użyć. zawsze rób to na górze połączenia HTTPS, aby zapobiec atak człowieka w środku .
  • spójrz na to pytanie z bezpieczeństwa informacji, aby uzyskać więcej informacji na temat tokenów.
  • w tym artykule znajdziesz kilka przydatnych informacji na temat uwierzytelniania tokenowego.
 1442
Author: cassiomolin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-15 14:42:43

ta odpowiedź dotyczy autoryzacji i jest uzupełnieniem mojej poprzedniej odpowiedzi o autoryzacji

dlaczego kolejna odpowiedź? próbowałem rozszerzyć moją poprzednią odpowiedź, dodając szczegóły dotyczące obsługi adnotacji JSR-250. Jednak oryginalna odpowiedź stała się drogą zbyt długa i przekroczyła maksymalną długość 30 000 znaków. Więc przesunąłem całą autoryzację. szczegóły tej odpowiedzi, utrzymując drugą odpowiedź skupioną na wykonywaniu uwierzytelniania i wydawaniu tokenów.


Wspomaganie autoryzacji opartej na rolach za pomocą adnotacji

Oprócz przepływu uwierzytelniania pokazanego w innej odpowiedzi , autoryzacja oparta na rolach może być obsługiwana w pozostałych punktach końcowych.

[[34]}Utwórz wyliczenie i zdefiniuj role zgodnie z Twoimi potrzebami: [43]}
public enum Role {
    ROLE_1,
    ROLE_2,
    ROLE_3
}

Zmień adnotację wiążącą nazwę @Secured utworzoną przed aby wspierać role:

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

, a następnie dodaj adnotację klas zasobów i metod za pomocą @Secured, aby wykonać autoryzację. Adnotacje metody nadpisują adnotacje klasy:

@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
        ...
    }
}

Utwórz filtr z AUTHORIZATION priorytet, który jest wykonywany po AUTHENTICATION filtr priorytetowy zdefiniowany wcześniej.

The ResourceInfo może być użyty do uzyskania zasobu Method i zasobów Class który zajmie się żądaniem, a następnie wyodrębnij z nich @Secured adnotacje:

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

Jeśli użytkownik nie ma uprawnień do wykonania operacji, żądanie jest przerywane za pomocą 403 (zabronione).

Aby poznać użytkownika, który wykonuje żądanie, zobacz moja poprzednia odpowiedź. Można go dostać z SecurityContext (który powinien być już ustawiony w ContainerRequestContext) lub wstrzyknąć go za pomocą CDI, w zależności od podejścia, na które się zdecydujesz.

Jeśli adnotacja @Secured nie ma zadeklarowanych ról, możesz przyjąć wszystkie uwierzytelnieni użytkownicy mogą uzyskać dostęp do tego punktu końcowego, bez względu na role, które mają użytkownicy.

Wspomaganie autoryzacji opartej na rolach z adnotacjami JSR-250

Alternatywnie do definiowania ról w adnotacji @Secured, Jak pokazano powyżej, możesz rozważyć adnotacje JSR-250, takie jak @RolesAllowed, @PermitAll oraz @DenyAll.

JAX-RS nie obsługuje takich adnotacji po wyjęciu z pudełka, ale można to osiągnąć za pomocą filtra. Oto kilka rozważań na temat pamiętaj, jeśli chcesz wspierać je wszystkie: {]}

Tak więc filtr autoryzacyjny sprawdzający adnotacje JSR-250 może wyglądać następująco:]}
@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.");
    }
}

Uwaga: powyższa implementacja oparta jest na koszulce RolesAllowedDynamicFeature. Jeśli używasz Jersey, nie musisz pisać własnego filtra, po prostu Użyj istniejącej implementacji.

 112
Author: cassiomolin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55