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.
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:
- klient wysyła ich poświadczenia (nazwa użytkownika i hasło) do serwera. Serwer uwierzytelnia poświadczenia i, jeśli są one ważne, generuje token dla użytkownika.
- serwer przechowuje wcześniej wygenerowany token w jakimś magazynie wraz z identyfikatorem użytkownika i datą wygaśnięcia. Serwer wysyła wygenerowany token do klienta.
- klient wysyła token do serwera w każdym żądaniu.
- 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.
- po uwierzytelnieniu serwer wykonuje autoryzację.
- 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.
@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:
- CDI:
javax.enterprise.inject.Produces
- JAX-RS:
javax.ws.rs.Produces
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.
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: {]}
-
@DenyAll
na sposób ma pierwszeństwo przed@RolesAllowed
oraz@PermitAll
na zajęciach. -
@RolesAllowed
na sposób ma pierwszeństwo przed@PermitAll
na zajęciach. -
@PermitAll
na sposób ma pierwszeństwo przed@RolesAllowed
na zajęciach. -
@DenyAll
nie można przywiązywać się do zajęć. -
@RolesAllowed
na klasa ma pierwszeństwo przed@PermitAll
na zajęciach.
@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.
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