Ich suche nach einer Möglichkeit, die tokenbasierte Authentifizierung in Jersey zu aktivieren. Ich versuche, keinen bestimmten Rahmen zu verwenden. Ist das möglich?
Mein Plan ist: Ein Benutzer meldet sich für meinen Webdienst an, mein Webdienst generiert ein Token, sendet es an den Client und der Client behält es. Dann sendet der Client für jede Anforderung das Token anstelle von Benutzername und Kennwort.
Ich habe darüber nachgedacht, einen benutzerdefinierten Filter für jede Anforderung und @PreAuthorize("hasRole('ROLE')")
zu verwenden, aber ich dachte nur, dass dies viele Anforderungen an die Datenbank verursacht, um zu überprüfen, ob das Token gültig ist.
Oder nicht Filter erstellen und in jede Anfrage ein Param-Token setzen? Damit überprüft jede API zuerst das Token und führt anschließend etwas aus, um die Ressource abzurufen.
Bei der tokenbasierten Authentifizierung tauscht der Client harte Anmeldeinformationen (wie z. B. Benutzername und Passwort) gegen eine Dateneinheit namens token aus. Anstatt die festen Anmeldeinformationen zu senden, sendet der Client bei jeder Anforderung das Token an den Server, um die Authentifizierung und anschließend die Autorisierung durchzuführen.
Mit wenigen Worten, ein auf Token basierendes Authentifizierungsschema folgt diesen Schritten:
Hinweis: Schritt 3 ist nicht erforderlich, wenn der Server ein signiertes Token (z. B. JWT) ausgegeben hat, mit dem Sie statuslos Authentifizierung).
Diese Lösung verwendet nur die JAX-RS 2.0-API. Keine herstellerspezifische Lösung. Daher sollte es mit JAX-RS 2.0-Implementierungen wie Jersey , RESTEasy und Apache CXF funktionieren.
Erwähnenswert ist, dass Sie sich bei der tokenbasierten Authentifizierung nicht auf die standardmäßigen Sicherheitsmechanismen für Java EE-Webanwendungen verlassen, die vom Servlet-Container angeboten werden und über den web.xml
- Deskriptor der Anwendung konfiguriert werden können . Es ist eine benutzerdefinierte Authentifizierung.
Erstellen Sie eine JAX-RS-Ressourcenmethode, die die Anmeldeinformationen (Benutzername und Kennwort) empfängt und überprüft, und stellen Sie ein Token für den Benutzer aus:
@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
}
}
Wenn beim Überprüfen der Anmeldeinformationen Ausnahmen ausgelöst werden, wird eine Antwort mit dem Status 403
(Verboten) zurückgegeben.
Wenn die Anmeldeinformationen erfolgreich validiert wurden, wird eine Antwort mit dem Status 200
(OK) zurückgegeben und das ausgestellte Token in der Antwortnutzlast an den Client gesendet. Der Client muss das Token bei jeder Anforderung an den Server senden.
Bei Verwendung von application/x-www-form-urlencoded
Muss der Client die Anmeldeinformationen in der Anforderungsnutzlast im folgenden Format senden:
username=admin&password=123456
Anstelle von Formularparametern können Sie den Benutzernamen und das Kennwort in eine Klasse einschließen:
public class Credentials implements Serializable {
private String username;
private String password;
// Getters and setters omitted
}
Und verbrauchen Sie es dann als 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
}
Bei diesem Ansatz muss der Client die Anmeldeinformationen in der Nutzlast der Anforderung in folgendem Format senden:
{
"username": "admin",
"password": "123456"
}
Der Client sollte das Token im Standard-HTTP-Header Authorization
der Anforderung senden. Zum Beispiel:
Authorization: Bearer <token-goes-here>
Der Name des Standard-HTTP-Headers ist unglücklich, da er Authentifizierung Informationen enthält, nicht Autorisierung. Es ist jedoch der Standard-HTTP-Header zum Senden von Anmeldeinformationen an den Server.
JAX-RS bietet @NameBinding
, eine Meta-Annotation, mit der andere Annotationen erstellt werden, um Filter und Interceptors an Ressourcenklassen und -methoden zu binden. Definieren Sie eine Annotation @Secured
Wie folgt:
@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured { }
Die oben definierte Annotation für die Namensbindung wird zum Dekorieren einer Filterklasse verwendet, die ContainerRequestFilter
implementiert, sodass Sie die Anforderung abfangen können, bevor sie von einer Ressourcenmethode verarbeitet wird. Mit ContainerRequestContext
können Sie auf die HTTP-Anforderungsheader zugreifen und dann das Token extrahieren:
@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
}
}
Wenn während der Token-Validierung Probleme auftreten, wird eine Antwort mit dem Status 401
(Nicht autorisiert) zurückgegeben. Andernfalls wird die Anforderung an eine Ressourcenmethode weitergeleitet.
Um den Authentifizierungsfilter an Ressourcenmethoden oder Ressourcenklassen zu binden, kommentieren Sie diese mit der oben erstellten Annotation @Secured
. Für die mit Annotationen versehenen Methoden und/oder Klassen wird der Filter ausgeführt. Dies bedeutet, dass solche Endpunkte nur erreicht werden, wenn die Anforderung mit einem gültigen Token ausgeführt wird.
Wenn einige Methoden oder Klassen keine Authentifizierung benötigen, kommentieren Sie sie einfach nicht:
@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
...
}
}
In dem oben gezeigten Beispiel wird der Filter only für die mySecuredMethod(Long)
-Methode ausgeführt, da er mit @Secured
Kommentiert ist.
Es ist sehr wahrscheinlich, dass Sie den Benutzer kennen müssen, der die Anforderung für Ihre REST -API ausführt. Die folgenden Ansätze können verwendet werden, um dies zu erreichen:
Innerhalb Ihrer ContainerRequestFilter.filter(ContainerRequestContext)
-Methode kann eine neue SecurityContext
-Instanz für die aktuelle Anforderung festgelegt werden. Überschreiben Sie dann SecurityContext.getUserPrincipal()
und geben Sie eine Principal
-Instanz zurück:
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;
}
});
Verwenden Sie das Token, um die Benutzerkennung (username) zu suchen, die der Name von Principal
ist.
Injizieren Sie SecurityContext
in eine beliebige JAX-RS-Ressourcenklasse:
@Context
SecurityContext securityContext;
Dasselbe kann in einer JAX-RS-Ressourcenmethode durchgeführt werden:
@GET
@Secured
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response myMethod(@PathParam("id") Long id,
@Context SecurityContext securityContext) {
...
}
Und dann erhalten Sie die Principal
:
Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();
Wenn Sie aus irgendeinem Grund das SecurityContext
nicht überschreiben möchten, können Sie CDI (Context and Dependency Injection) verwenden, das nützliche Funktionen wie Ereignisse und Produzenten bietet.
Erstellen Sie ein CDI-Qualifikationsmerkmal:
@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER })
public @interface AuthenticatedUser { }
Fügen Sie in Ihr AuthenticationFilter
, das oben erstellt wurde, ein Event
ein, das mit @AuthenticatedUser
Versehen ist:
@Inject
@AuthenticatedUser
Event<String> userAuthenticatedEvent;
Wenn die Authentifizierung erfolgreich ist, lösen Sie das Ereignis aus, das den Benutzernamen als Parameter übergibt (denken Sie daran, dass das Token für einen Benutzer ausgegeben wird und das Token zum Nachschlagen der Benutzer-ID verwendet wird):
userAuthenticatedEvent.fire(username);
Es ist sehr wahrscheinlich, dass es eine Klasse gibt, die einen Benutzer in Ihrer Anwendung darstellt. Nennen wir diese Klasse User
.
Erstellen Sie eine CDI-Bean zur Behandlung des Authentifizierungsereignisses, suchen Sie eine User
-Instanz mit dem entsprechenden Benutzernamen und weisen Sie sie dem Feld authenticatedUser
producer zu:
@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
}
}
Das Feld authenticatedUser
erzeugt eine User
Instanz, die in von Containern verwaltete Beans wie JAX-RS-Services, CDI-Beans, Servlets und EJBs eingefügt werden kann. Verwenden Sie den folgenden Code, um eine User
-Instanz einzufügen (tatsächlich handelt es sich um einen CDI-Proxy):
@Inject
@AuthenticatedUser
User authenticatedUser;
Beachten Sie, dass die CDI @Produces
Annotation anders von der JAX-RS @Produces
Annotation ist:
javax.enterprise.inject.Produces
javax.ws.rs.Produces
Stellen Sie sicher, dass Sie die Annotation CDI @Produces
in Ihrer Bean AuthenticatedUserProducer
verwenden.
Der Schlüssel hier ist das mit @RequestScoped
kommentierte Bean, mit dem Sie Daten zwischen Filtern und Ihren Beans austauschen können. Wenn Sie keine Ereignisse verwenden möchten, können Sie den Filter so ändern, dass der authentifizierte Benutzer in einer Bean mit Anforderungsbereich gespeichert und dann aus Ihren JAX-RS-Ressourcenklassen gelesen wird.
Im Vergleich zu dem Ansatz, der SecurityContext
überschreibt, können Sie mit dem CDI-Ansatz den authentifizierten Benutzer aus anderen Beans als JAX-RS-Ressourcen und -Anbietern abrufen.
Weitere Informationen zur Unterstützung der rollenbasierten Autorisierung finden Sie in meinem anderen Antwort .
Ein Token kann sein:
Siehe Details unten:
Ein Token kann ausgegeben werden, indem eine zufällige Zeichenfolge generiert und zusammen mit der Benutzerkennung und einem Ablaufdatum in einer Datenbank gespeichert wird. Ein gutes Beispiel für das Generieren einer zufälligen Zeichenfolge in Java ist hier . Sie könnten auch verwenden:
Random random = new SecureRandom();
String token = new BigInteger(130, random).toString(32);
JWT (JSON Web Token) ist eine Standardmethode zur sicheren Darstellung von Ansprüchen zwischen zwei Parteien und wird durch RFC 7519 definiert.
Es ist ein in sich geschlossenes Token, mit dem Sie Details in Claims speichern können. Diese Ansprüche werden in der Token-Payload gespeichert, die ein JSON ist, das als Base64 codiert ist. Hier sind einige Ansprüche, die im RFC 7519 registriert sind, und was sie bedeuten (lesen Sie den vollständigen RFC für weitere Details):
iss
: Principal, der das Token ausgegeben hat.sub
: Principal, der Gegenstand des JWT ist.exp
: Ablaufdatum für das Token.nbf
: Zeitpunkt, zu dem der Token zur Verarbeitung angenommen wird.iat
: Zeitpunkt, an dem das Token ausgestellt wurde.jti
: Eindeutiger Bezeichner für das Token.Beachten Sie, dass Sie keine vertraulichen Daten wie Kennwörter im Token speichern dürfen.
Die Nutzdaten können vom Client gelesen werden und die Integrität des Tokens kann auf einfache Weise überprüft werden, indem die Signatur auf dem Server überprüft wird. Die Signatur verhindert, dass das Token manipuliert wird.
Sie müssen JWT-Token nicht beibehalten, wenn Sie sie nicht verfolgen müssen. Wenn Sie die Token beibehalten, haben Sie jedoch die Möglichkeit, den Zugriff auf diese Token zu ungültig zu machen und zu widerrufen. Um den Überblick über JWT-Token zu behalten, anstatt das gesamte Token auf dem Server zu behalten, können Sie die Token-ID ( jti
claim) zusammen mit einigen anderen Details, z. B. dem von Ihnen verwendeten Benutzer, beibehalten ausgestellt das Token für, das Ablaufdatum, etc.
Wenn Sie Token behalten, sollten Sie immer die alten Token entfernen, um ein unbegrenztes Anwachsen Ihrer Datenbank zu verhindern.
Es gibt einige Java Bibliotheken, mit denen JWT-Token ausgegeben und validiert werden können, beispielsweise:
Weitere nützliche Ressourcen für die Arbeit mit JWT finden Sie unter http://jwt.io .
Akzeptiere only gültige (und nicht abgelaufene) Token zur Erfrischung. Es liegt in der Verantwortung des Kunden, die Token vor dem im Anspruch exp
angegebenen Ablaufdatum zu aktualisieren.
Sie sollten verhindern, dass die Token auf unbestimmte Zeit aktualisiert werden. Im Folgenden finden Sie einige Ansätze, die Sie in Betracht ziehen könnten.
Sie können die Aktualisierung des Tokens verfolgen, indem Sie Ihrem Token zwei Ansprüche hinzufügen (die Namen der Ansprüche liegen bei Ihnen):
refreshLimit
: Gibt an, wie oft das Token aktualisiert werden kann.refreshCount
: Gibt an, wie oft das Token aktualisiert wurde.Aktualisieren Sie das Token daher nur, wenn die folgenden Bedingungen erfüllt sind:
exp >= now
).refreshCount < refreshLimit
).Und beim Auffrischen des Tokens:
exp = now + some-amount-of-time
).refreshCount++
).Alternativ dazu können Sie die Anzahl der Erfrischungen protokollieren, indem Sie einen Anspruch angeben, der das absolute Ablaufdatum angibt (was dem oben beschriebenen Anspruch refreshLimit
ziemlich ähnlich ist). Vor dem absoluten Verfallsdatum sind beliebig viele Erfrischungen zulässig.
Ein weiterer Ansatz besteht darin, ein separates, langlebiges Aktualisierungstoken auszugeben, mit dem kurzlebige JWT-Token ausgegeben werden.
Der beste Ansatz hängt von Ihren Anforderungen ab.
Wenn Sie Token widerrufen möchten, müssen Sie den Überblick behalten. Sie müssen nicht das gesamte Token serverseitig speichern, sondern nur die Token-ID (die eindeutig sein muss) und ggf. einige Metadaten. Für die Token-ID können Sie UUID verwenden.
Der Anspruch jti
sollte verwendet werden, um die Token-ID auf dem Token zu speichern. Stellen Sie beim Überprüfen des Tokens sicher, dass es nicht widerrufen wurde, indem Sie den Wert des Anspruchs jti
mit den Tokenkennungen vergleichen, die Sie auf der Serverseite haben.
Sperren Sie aus Sicherheitsgründen alle Token für einen Benutzer, wenn dieser sein Kennwort ändert.
Diese Antwort handelt von Autorisierung und ist eine Ergänzung von meine vorherige Antwort about Authentifizierung
Warum noch eine Antwort? Ich habe versucht, meine vorherige Antwort zu erweitern, indem ich Details zur Unterstützung von JSR- hinzufügte. 250 Anmerkungen. Allerdings wurde die ursprüngliche Antwort übrigens zu lang und überschritt das maximale Länge von 30.000 Zeichen . Daher habe ich die gesamten Autorisierungsdetails in diese Antwort verschoben und die andere Antwort auf die Durchführung der Authentifizierung und die Ausgabe von Tokens konzentriert.
@Secured
_Neben dem Authentifizierungsablauf, der im anderen answer angegeben ist, kann die rollenbasierte Autorisierung auf den REST Endpunkten unterstützt werden.
Erstellen Sie eine Aufzählung und definieren Sie die Rollen gemäß Ihren Anforderungen:
_public enum Role {
ROLE_1,
ROLE_2,
ROLE_3
}
_
Ändern Sie die zuvor erstellte _@Secured
_ -Namensbindungsannotation, um Rollen zu unterstützen:
_@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured {
Role[] value() default {};
}
_
Kommentieren Sie anschließend die Ressourcenklassen und -methoden mit _@Secured
_, um die Autorisierung durchzuführen. Die Methodenanmerkungen überschreiben die Klassenanmerkungen:
_@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
...
}
}
_
Erstellen Sie einen Filter mit der Priorität AUTHORIZATION
, der nach dem zuvor definierten Prioritätsfilter AUTHENTICATION
ausgeführt wird.
Mit ResourceInfo
können die Ressource Method
und die Ressource Class
abgerufen werden, die die Anforderung verarbeiten und dann die _@Secured
_ -Anmerkungen aus extrahieren Sie:
_@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
}
}
_
Wenn der Benutzer keine Berechtigung zum Ausführen des Vorgangs hat, wird die Anforderung mit einem _403
_ (Forbidden) abgebrochen.
Informationen zum Benutzer, der die Anforderung ausführt, finden Sie unter meine vorherige Antwort . Sie können es von SecurityContext
(das bereits in ContainerRequestContext
eingestellt sein sollte) beziehen oder es mit CDI injizieren, je nachdem, welchen Ansatz Sie wählen.
Wenn für eine _@Secured
_ -Annotation keine Rollen deklariert wurden, können Sie davon ausgehen, dass alle authentifizierten Benutzer auf diesen Endpunkt zugreifen können, ohne die Rollen der Benutzer zu berücksichtigen.
Alternativ zur oben gezeigten Definition der Rollen in der Annotation _@Secured
_ können Sie auch JSR-250-Annotationen wie @RolesAllowed
, @PermitAll
in Betracht ziehen = und @DenyAll
.
JAX-RS unterstützt solche Annotationen nicht, kann jedoch mit einem Filter erstellt werden. Hier einige Überlegungen, die Sie berücksichtigen sollten, wenn Sie alle unterstützen möchten:
@DenyAll
in der Methode hat Vorrang vor @RolesAllowed
und @PermitAll
in der Klasse.@RolesAllowed
in der Methode hat Vorrang vor @PermitAll
in der Klasse.@PermitAll
in der Methode hat Vorrang vor @RolesAllowed
in der Klasse.@DenyAll
kann nicht an Klassen angehängt werden.@RolesAllowed
in der Klasse hat Vorrang vor @PermitAll
in der Klasse.Ein Berechtigungsfilter, der JSR-250-Annotationen überprüft, könnte also folgendermaßen aussehen:
_@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.");
}
}
_
Hinweis: Die obige Implementierung basiert auf dem Jersey RolesAllowedDynamicFeature
. Wenn Sie Jersey verwenden, müssen Sie keinen eigenen Filter schreiben. Verwenden Sie einfach die vorhandene Implementierung.