Nutzer-Authentifizierung bei REST mit Jersey
Wenn man mit Jersey ab der Version 2.0 eine REST Schnittstelle aufbaut, hat man für die Authentifizierung eines Nutzers eine neue schöne Möglichkeit, mit Annotationen zu arbeiten. Hier sieht man als Beispiel zwei REST Endpunkte, bei beiden wird über eine neue Annotation UserAuthorization
definiert, dass eine erfolgreiche Authentifizierung des Nutzers notwendig ist.
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Path( "/myapp" ) public class MyResource { @POST @UserAuthorization public Response postSomething( ) {} @GET @Produces( MediaType.APPLICATION_JSON ) @UserAuthorization public List<Itinerary> getSomething( ) {} |
Mit Hilfe der neuen Java Annotation UserAuthorization
wird festgelegt, dass vor jedem Aufruf dieser REST Endpunkte bzw. der Methode der mit der Annotation verbundene Code ausgeführt werden soll. Diese Annotation muss zunächst definiert werden:
1 2 3 4 5 | @NameBinding @Target( { ElementType.TYPE, ElementType.METHOD } ) @Retention( value = RetentionPolicy.RUNTIME ) public @interface UserAuthorization {} |
Die Retention Policy gibt an, dass die Annotation zur Laufzeit (also als Teil der Java Class Dateien) verfügbar sein soll. Nun muss noch der Filter definiert werden, der zu dieser Annotation gehört:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | @Provider @UserAuthorization public class AuthFilter implements ContainerRequestFilter { @Override public void filter( ContainerRequestContext requestContext ) throws IOException { final String authHeader = requestContext.getHeaderString( HttpHeaders.AUTHORIZATION ); if ( authHeader == null ) { requestContext.abortWith( Response.status( Status.UNAUTHORIZED ) .header( HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"realm\"" ) .entity( "Page requires login." ).build( ) ); } else { final String withoutBasic = authHeader.replaceFirst( "[Bb]asic ", "" ); final String userColonPass = Base64.decodeAsString( withoutBasic ); final String[ ] asArray = userColonPass.split( ":" ); if ( asArray.length == 2 ) { final String username = asArray[ 0 ]; final String password = asArray[ 1 ]; // code to check username and password } else { // abort with error } } } } |
Die Klasse muss mit der soeben definierten Annotation versehen werden, damit die Verbindung zwischen Annotation und auszuführendem Code klar ist. Schließlich muss man den die Klasse AuthFilter
noch registrieren, was bei Jersey ab Version 2.0 nicht mehr über die web.xml
erfolgt, sondern über die zentrale Konfiguration im Quelltext:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @ApplicationPath( "api" ) public class RestService extends Application { public RestService( ) {} @Override public Set<Class<?>> getClasses( ) { final Set<Class<?>> returnValue = new HashSet<Class<?>>( ); returnValue.add( MyResource.class ); // add more resource classes returnValue.add( AuthFilter.class ); return returnValue; } } |