Java >> Java tutorial >  >> Tag >> HTTP

Java EE 7 / JAX-RS 2.0:Simpel REST API-godkendelse og godkendelse med brugerdefineret HTTP-header

REST har gjort en masse bekvemmeligheder, når det kommer til at implementere webtjenester med den allerede tilgængelige HTTP-protokol til sin rådighed. Ved blot at skyde GET, POST og andre HTTP-metoder gennem den udpegede URL, vil du sikker på at få noget gjort gennem et svar ud af en REST-tjeneste. Men uanset hvilke bekvemmeligheder REST har givet udviklerne, bør emnet sikkerhed og adgangskontrol altid behandles. Denne artikel viser dig, hvordan du implementerer simpel brugerbaseret godkendelse med brug af HTTP-headere og JAX-RS 2.0-interceptorer.



Authenticator

Lad os begynde med en godkendelsesklasse. Denne DemoAuthenticator med nedenstående koder giver de nødvendige metoder til at godkende enhver bruger, der anmoder om adgang til REST-webtjenesten. Læs venligst koderne igennem, og kommentarerne er til for at vejlede forståelsen.

Koder til DemoAuthenticator:

package com.developerscrappad.business;
 
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.security.GeneralSecurityException;
import javax.security.auth.login.LoginException;
 
public final class DemoAuthenticator {
 
    private static DemoAuthenticator authenticator = null;
 
    // A user storage which stores <username, password>
    private final Map<String, String> usersStorage = new HashMap();
 
    // A service key storage which stores <service_key, username>
    private final Map<String, String> serviceKeysStorage = new HashMap();
 
    // An authentication token storage which stores <service_key, auth_token>.
    private final Map<String, String> authorizationTokensStorage = new HashMap();
 
    private DemoAuthenticator() {
        // The usersStorage pretty much represents a user table in the database
        usersStorage.put( "username1", "passwordForUser1" );
        usersStorage.put( "username2", "passwordForUser2" );
        usersStorage.put( "username3", "passwordForUser3" );
 
        /**
         * Service keys are pre-generated by the system and is given to the
         * authorized client who wants to have access to the REST API. Here,
         * only username1 and username2 is given the REST service access with
         * their respective service keys.
         */
        serviceKeysStorage.put( "f80ebc87-ad5c-4b29-9366-5359768df5a1", "username1" );
        serviceKeysStorage.put( "3b91cab8-926f-49b6-ba00-920bcf934c2a", "username2" );
    }
 
    public static DemoAuthenticator getInstance() {
        if ( authenticator == null ) {
            authenticator = new DemoAuthenticator();
        }
 
        return authenticator;
    }
 
    public String login( String serviceKey, String username, String password ) throws LoginException {
        if ( serviceKeysStorage.containsKey( serviceKey ) ) {
            String usernameMatch = serviceKeysStorage.get( serviceKey );
 
            if ( usernameMatch.equals( username ) && usersStorage.containsKey( username ) ) {
                String passwordMatch = usersStorage.get( username );
 
                if ( passwordMatch.equals( password ) ) {
 
                    /**
                     * Once all params are matched, the authToken will be
                     * generated and will be stored in the
                     * authorizationTokensStorage. The authToken will be needed
                     * for every REST API invocation and is only valid within
                     * the login session
                     */
                    String authToken = UUID.randomUUID().toString();
                    authorizationTokensStorage.put( authToken, username );
 
                    return authToken;
                }
            }
        }
 
        throw new LoginException( "Don't Come Here Again!" );
    }
 
    /**
     * The method that pre-validates if the client which invokes the REST API is
     * from a authorized and authenticated source.
     *
     * @param serviceKey The service key
     * @param authToken The authorization token generated after login
     * @return TRUE for acceptance and FALSE for denied.
     */
    public boolean isAuthTokenValid( String serviceKey, String authToken ) {
        if ( isServiceKeyValid( serviceKey ) ) {
            String usernameMatch1 = serviceKeysStorage.get( serviceKey );
 
            if ( authorizationTokensStorage.containsKey( authToken ) ) {
                String usernameMatch2 = authorizationTokensStorage.get( authToken );
 
                if ( usernameMatch1.equals( usernameMatch2 ) ) {
                    return true;
                }
            }
        }
 
        return false;
    }
 
    /**
     * This method checks is the service key is valid
     *
     * @param serviceKey
     * @return TRUE if service key matches the pre-generated ones in service key
     * storage. FALSE for otherwise.
     */
    public boolean isServiceKeyValid( String serviceKey ) {
        return serviceKeysStorage.containsKey( serviceKey );
    }
 
    public void logout( String serviceKey, String authToken ) throws GeneralSecurityException {
        if ( serviceKeysStorage.containsKey( serviceKey ) ) {
            String usernameMatch1 = serviceKeysStorage.get( serviceKey );
 
            if ( authorizationTokensStorage.containsKey( authToken ) ) {
                String usernameMatch2 = authorizationTokensStorage.get( authToken );
 
                if ( usernameMatch1.equals( usernameMatch2 ) ) {
 
                    /**
                     * When a client logs out, the authentication token will be
                     * remove and will be made invalid.
                     */
                    authorizationTokensStorage.remove( authToken );
                    return;
                }
            }
        }
 
        throw new GeneralSecurityException( "Invalid service key and authorization token match." );
    }
}

Generel kodeforklaring:

Generelt er der kun nogle få vigtige elementer, der udgør godkendelsesværktøjet, og det er:servicenøgle , godkendelsestoken , brugernavn og adgangskode . Brugernavnet og adgangskoden vil normalt gå i par.

Servicenøgle

Tjenestenøglen kan være ny for nogle læsere; i nogle offentlige REST API-tjenester genereres en servicenøgle og nogle gange kendt som API-nøgle af systemet og sendes derefter til brugeren/klienten (enten via e-mail eller på anden måde), som har tilladelse til at få adgang til REST-tjenesten. Så udover at logge ind på REST-tjenesten med blot brugernavn og adgangskode, vil systemet også kontrollere tjenestenøglen, om brugeren/klienten har tilladelse til at få adgang til REST API'erne. Brugernavne, adgangskoder og servicenøgler er alle foruddefinerede i ovenstående koder for nu kun demoformål.

Godkendelsestoken

Efter godkendelse (via login()-metoden), genererer systemet et autorisationstoken for den godkendte bruger. Dette token sendes tilbage til brugeren/klienten gennem HTTP-svar og skal bruges til enhver REST API-indkaldelse senere. Brugeren/klienten skal finde en måde at gemme og bruge det under hele login-sessionen. Det ser vi på senere.

Påkrævet definition af HTTP-headersnavn

Fremadrettet, i stedet for at have servicenøglen og autorisationstokenet til at blive videregivet til server-side-appen som HTTP-parametre (formular eller forespørgsel), får vi det videregivet som HTTP-headere. Dette er for at tillade, at anmodningen først filtreres, før den behandles af den målrettede REST-metode. Navnene på HTTP-headerne er nedenfor:

HTTP-headernavn Beskrivelse
service_key Tjenestenøgle, der gør det muligt for en HTTP-klient at få adgang til REST Web Services. Dette er det første lag af godkendelse og godkendelse af HTTP-anmodningen.
auth_token Tokenet, der genereres ved brugernavn/adgangskodegodkendelse, som skal bruges til alle REST-webservicekald (undtagen godkendelsesmetoden vist senere).

REST API-implementering

For nemheds skyld og yderligere reduktion af kodefejl, lad os sætte HTTP-headernavnene ind i en grænseflade som statiske endelige variabler til brug i resten af ​​klasserne.

Koder til DemoHTTPHeaderNames.java:

package com.developerscrappad.intf;
 
public interface DemoHTTPHeaderNames {
 
    public static final String SERVICE_KEY = "service_key";
    public static final String AUTH_TOKEN = "auth_token";
}

Til implementering af godkendelsesprocessen og andre demometoder er metodernes signatur defineret i DemoBusinessRESTResourceProxy sammen med de relevante HTTP-metoder, parametre, og forretningsimplementeringen er defineret i DemoBusinessRESTResource.

Koder til DemoBusinessRESTResourceProxy.java:

package com.developerscrappad.intf;
 
import java.io.Serializable;
import javax.ejb.Local;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
 
@Local
@Path( "demo-business-resource" )
public interface DemoBusinessRESTResourceProxy extends Serializable {
 
    @POST
    @Path( "login" )
    @Produces( MediaType.APPLICATION_JSON )
    public Response login(
        @Context HttpHeaders httpHeaders,
        @FormParam( "username" ) String username,
        @FormParam( "password" ) String password );
 
    @GET
    @Path( "demo-get-method" )
    @Produces( MediaType.APPLICATION_JSON )
    public Response demoGetMethod();
 
    @POST
    @Path( "demo-post-method" )
    @Produces( MediaType.APPLICATION_JSON )
    public Response demoPostMethod();
 
    @POST
    @Path( "logout" )
    public Response logout(
        @Context HttpHeaders httpHeaders
    );
}

Koder til DemoBusinessRESTResource.java:

package com.developerscrappad.business;
 
import com.developerscrappad.intf.DemoBusinessRESTResourceProxy;
import com.developerscrappad.intf.DemoHTTPHeaderNames;
import java.security.GeneralSecurityException;
import javax.ejb.Stateless;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.security.auth.login.LoginException;
import javax.ws.rs.FormParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
 
@Stateless( name = "DemoBusinessRESTResource", mappedName = "ejb/DemoBusinessRESTResource" )
public class DemoBusinessRESTResource implements DemoBusinessRESTResourceProxy {
 
    private static final long serialVersionUID = -6663599014192066936L;
 
    @Override
    public Response login(
        @Context HttpHeaders httpHeaders,
        @FormParam( "username" ) String username,
        @FormParam( "password" ) String password ) {
 
        DemoAuthenticator demoAuthenticator = DemoAuthenticator.getInstance();
        String serviceKey = httpHeaders.getHeaderString( DemoHTTPHeaderNames.SERVICE_KEY );
 
        try {
            String authToken = demoAuthenticator.login( serviceKey, username, password );
 
            JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();
            jsonObjBuilder.add( "auth_token", authToken );
            JsonObject jsonObj = jsonObjBuilder.build();
 
            return getNoCacheResponseBuilder( Response.Status.OK ).entity( jsonObj.toString() ).build();
 
        } catch ( final LoginException ex ) {
            JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();
            jsonObjBuilder.add( "message", "Problem matching service key, username and password" );
            JsonObject jsonObj = jsonObjBuilder.build();
 
            return getNoCacheResponseBuilder( Response.Status.UNAUTHORIZED ).entity( jsonObj.toString() ).build();
        }
    }
 
    @Override
    public Response demoGetMethod() {
        JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();
        jsonObjBuilder.add( "message", "Executed demoGetMethod" );
        JsonObject jsonObj = jsonObjBuilder.build();
 
        return getNoCacheResponseBuilder( Response.Status.OK ).entity( jsonObj.toString() ).build();
    }
 
    @Override
    public Response demoPostMethod() {
        JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();
        jsonObjBuilder.add( "message", "Executed demoPostMethod" );
        JsonObject jsonObj = jsonObjBuilder.build();
 
        return getNoCacheResponseBuilder( Response.Status.ACCEPTED ).entity( jsonObj.toString() ).build();
    }
 
    @Override
    public Response logout(
        @Context HttpHeaders httpHeaders ) {
        try {
            DemoAuthenticator demoAuthenticator = DemoAuthenticator.getInstance();
            String serviceKey = httpHeaders.getHeaderString( DemoHTTPHeaderNames.SERVICE_KEY );
            String authToken = httpHeaders.getHeaderString( DemoHTTPHeaderNames.AUTH_TOKEN );
 
            demoAuthenticator.logout( serviceKey, authToken );
 
            return getNoCacheResponseBuilder( Response.Status.NO_CONTENT ).build();
        } catch ( final GeneralSecurityException ex ) {
            return getNoCacheResponseBuilder( Response.Status.INTERNAL_SERVER_ERROR ).build();
        }
    }
 
    private Response.ResponseBuilder getNoCacheResponseBuilder( Response.Status status ) {
        CacheControl cc = new CacheControl();
        cc.setNoCache( true );
        cc.setMaxAge( -1 );
        cc.setMustRevalidate( true );
 
        return Response.status( status ).cacheControl( cc );
    }
}

login() metoden er at autentificere brugernavnet, adgangskoden og også den rigtige servicenøgle. Efter login() , vil godkendelsestokenet blive genereret og returneret til klienten. Klienten bliver nødt til at bruge den til enhver anden metodepåkaldelse senere. demoGetMethod() og demoPostMethod() er blot dummy-metoder, som returnerer en JSON-meddelelse til demoformål, men med en særlig betingelse om, at et gyldigt autorisationstoken skal være til stede. logout() metoden er at logge brugeren ud af REST-tjenesten; brugeren identificeres med "auth_token “.

Servicenøglen og godkendelsestokenet vil blive gjort tilgængelige for REST-servicemetoderne gennem:

@Context HttpHeaders httpHeaders

httpHeaders, en forekomst af javax.ws.rs.core.HttpHeaders, er et objekt, der indeholder headernavnet og værdierne til brug af applikationen længere fremme. Men for at få REST-tjenesten til at acceptere HTTP-headeren, skal der først gøres noget gennem både REST-anmodningsinterceptoren og responseinterceptoren.

Godkendelse med HTTP-headere gennem JAX-RS 2.0-interceptorer

På grund af visse sikkerhedsbegrænsninger skal du bare ikke håbe, at nogen HTTP-headere kan sendes ved hjælp af enhver REST-klient, og forvent, at REST-tjenesten accepterer det. Sådan fungerer det bare ikke.

For at få en specifik header til at blive accepteret i REST-tjenesten, skal vi definere accepten af ​​HTTP Header meget specifikt i responsfilterinterceptoren.

Koder til DemoRESTResponseFilter.java:

package com.developerscrappad.interceptors;
 
import com.developerscrappad.intf.DemoHTTPHeaderNames;
import java.io.IOException;
import java.util.logging.Logger;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.ext.Provider;
 
@Provider
@PreMatching
public class DemoRESTResponseFilter implements ContainerResponseFilter {
 
    private final static Logger log = Logger.getLogger( DemoRESTResponseFilter.class.getName() );
 
    @Override
    public void filter( ContainerRequestContext requestCtx, ContainerResponseContext responseCtx ) throws IOException {
 
        log.info( "Filtering REST Response" );
 
        responseCtx.getHeaders().add( "Access-Control-Allow-Origin", "*" );    // You may further limit certain client IPs with Access-Control-Allow-Origin instead of '*'
        responseCtx.getHeaders().add( "Access-Control-Allow-Credentials", "true" );
        responseCtx.getHeaders().add( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT" );
        responseCtx.getHeaders().add( "Access-Control-Allow-Headers", DemoHTTPHeaderNames.SERVICE_KEY + ", " + DemoHTTPHeaderNames.AUTH_TOKEN );
    }
}

DemoRESTResponseFilter er en JAX-RS 2.0 interceptor, som implementerer ContainerResponseFilter . Glem ikke at annotere det med både @Provide og @PreMatching. For at tillade, at visse specifikke brugerdefinerede HTTP-headere kan accepteres, skal headernavnet "Access-Control-Allow-Headers " efterfulgt af værdien af ​​tilpassede overskrifter med "," da separatoren skal tilføjes som en del af værdien for tilpassede overskrifter. Dette er måden at informere browseren eller REST-klienten om de tilladte tilpassede overskrifter. Resten af ​​overskrifterne er til CORS, som du kan læse mere i en af ​​vores artikler Java EE 7 / JAX-RS 2.0 – CORS på REST (Sådan gør du REST API'er tilgængelige fra et andet domæne).

Dernæst, for at validere og verificere servicenøglen og autorisationstokenet, skal vi udtrække det fra HTTP-headerne og forbehandle det med anmodningsfilteropfangeren.

Koder til DemoRESTRequestFilter:

package com.developerscrappad.interceptors;
 
import com.developerscrappad.business.DemoAuthenticator;
import com.developerscrappad.intf.DemoHTTPHeaderNames;
import java.io.IOException;
import java.util.logging.Logger;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
 
@Provider
@PreMatching
public class DemoRESTRequestFilter implements ContainerRequestFilter {
 
    private final static Logger log = Logger.getLogger( DemoRESTRequestFilter.class.getName() );
 
    @Override
    public void filter( ContainerRequestContext requestCtx ) throws IOException {
 
        String path = requestCtx.getUriInfo().getPath();
        log.info( "Filtering request path: " + path );
 
        // IMPORTANT!!! First, Acknowledge any pre-flight test from browsers for this case before validating the headers (CORS stuff)
        if ( requestCtx.getRequest().getMethod().equals( "OPTIONS" ) ) {
            requestCtx.abortWith( Response.status( Response.Status.OK ).build() );
 
            return;
        }
 
        // Then check is the service key exists and is valid.
        DemoAuthenticator demoAuthenticator = DemoAuthenticator.getInstance();
        String serviceKey = requestCtx.getHeaderString( DemoHTTPHeaderNames.SERVICE_KEY );
 
        if ( !demoAuthenticator.isServiceKeyValid( serviceKey ) ) {
            // Kick anyone without a valid service key
            requestCtx.abortWith( Response.status( Response.Status.UNAUTHORIZED ).build() );
 
            return;
        }
 
        // For any pther methods besides login, the authToken must be verified
        if ( !path.startsWith( "/demo-business-resource/login/" ) ) {
            String authToken = requestCtx.getHeaderString( DemoHTTPHeaderNames.AUTH_TOKEN );
 
            // if it isn't valid, just kick them out.
            if ( !demoAuthenticator.isAuthTokenValid( serviceKey, authToken ) ) {
                requestCtx.abortWith( Response.status( Response.Status.UNAUTHORIZED ).build() );
            }
        }
    }
}

For at få headerværdien påberåber vi getHeaderString()-metoden for objektforekomsten af ​​ContainerRequestContext, for eksempel:

String serviceKey = requestCtx.getHeaderString( "service_key" );

Resten af ​​koderne i DemoRESTRequestFilter er ret ligetil med at validere og verificere servicenøglen og godkendelsestokenet.

REST-tjenesteimplementering

Glem ikke at få web.xml til at aktivere REST-tjenesten til at definere.

Koder til web.xml:



 
    
    
        javax.ws.rs.core.Application
        1
    
    
        javax.ws.rs.core.Application
        /rest-api/*
    
 

Til denne demo har jeg pakket de kompilerede koder ind i en krigsfil, der navngiver den RESTSecurityWithHTTPHeaderDemo.war . Jeg har valgt at implementere på Glassfish 4.0 på domænet developerscrappad.com (domænet for denne blog). Hvis du gennemgår alt i denne vejledning, kan du vælge et andet domæne. REST API-URL'erne vil være i formatet:

http://<domain>:<port>/RESTSecurityWithHTTPHeaderDemo/rest-api/path/method-path/

Under alle omstændigheder er oversigten over URL'erne for testklienten, som jeg bruger:

Metode REST URL HTTP-metode
DemoBusinessRESTResourceProxy.login() http://developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/login/ POST
DemoBusinessRESTResourceProxy.
demoGetMethod()
http://developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/demo-get-method/ GET
DemoBusinessRESTResourceProxy.
demoPostMethod()
http://developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/demo-post-method/ POST
DemoBusinessRESTResourceProxy.logout() http://developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/logout/ POST

RESTEN-klienten

For at sætte det hele, her er en REST-klient, som jeg har skrevet for at teste REST API'erne. REST-klienten er kun en HTML-fil (specifikt HTML5, som understøtter weblagring), der udnytter jQuery til REST API-kald. Hvad REST-klienten gør er som følger:

  1. For det første vil REST-klienten foretage et REST API-kald uden servicenøgle og autorisationstoken. Opkaldet vil blive afvist med HTTP Status 401 (Uautoriseret)
  2. Dernæst vil den udføre et login med den specifikke tjenestenøgle (hardkodet i øjeblikket i Authenticator.java) for "brugernavn2". Når autorisationstokenet er blevet modtaget, vil det blive gemt i sessionStorage til videre brug.
  3. Derefter kalder den dummy get- og post-metoderne.
  4. Derefter vil den udføre et logout
  5. Når brugeren er logget ud, vil klienten derefter udføre et opkald til dummy get and post-metoden, men adgangen vil blive nægtet med HTTP Status 401 på grund af udløbet af godkendelsestokenet.

Koder til rest-auth-test.html:

<html>
    <head>
        <title>REST Authentication Tester</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <div id="logMsgDiv"></div>
 
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
        <script type="text/javascript">
            var $ = jQuery.noConflict();
 
            // Disable async
            $.ajaxSetup( { async: false } );
 
            // Using Service Key 3b91cab8-926f-49b6-ba00-920bcf934c2a and username2
 
            // This is what happens when there you call the REST APIs without a service key and authorisation token
            $.ajax( {
                cache: false,
                crossDomain: true,
                url: "http://www.developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/demo-post-method/",
                type: "POST",
                success: function( jsonObj, textStatus, xhr ) {
                    var htmlContent = $( "#logMsgDiv" ).html( ) + "<p style='color: red;'>If this is portion is executed, something must be wrong</p>";
                    $( "#logMsgDiv" ).html( htmlContent );
                },
                error: function( xhr, textStatus, errorThrown ) {
                    var htmlContent = $( "#logMsgDiv" ).html( )
                            + "<p style='color: red;'>This is what happens when there you call the REST APIs without a service key and authorisation token."
                            + "<br />HTTP Status: " + xhr.status + ", Unauthorized access to demo-post-method</p>";
 
                    $( "#logMsgDiv" ).html( htmlContent );
                }
            } );
 
            // Performing login with username2 and passwordForUser2
            $.ajax( {
                cache: false,
                crossDomain: true,
                headers: {
                    "service_key": "3b91cab8-926f-49b6-ba00-920bcf934c2a"
                },
                dataType: "json",
                url: "http://www.developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/login/",
                type: "POST",
                data: {
                    "username": "username2",
                    "password": "passwordForUser2"
                },
                success: function( jsonObj, textStatus, xhr ) {
                    sessionStorage.auth_token = jsonObj.auth_token;
 
                    var htmlContent = $( "#logMsgDiv" ).html( ) + "<p>Perform Login. Gotten auth-token as: " + sessionStorage.auth_token + "</p>";
                    $( "#logMsgDiv" ).html( htmlContent );
                },
                error: function( xhr, textStatus, errorThrown ) {
                    console.log( "HTTP Status: " + xhr.status );
                    console.log( "Error textStatus: " + textStatus );
                    console.log( "Error thrown: " + errorThrown );
                }
            } );
 
            // After login, execute demoteGetMethod with the auth-token obtained
            $.ajax( {
                cache: false,
                crossDomain: true,
                headers: {
                    "service_key": "3b91cab8-926f-49b6-ba00-920bcf934c2a",
                    "auth_token": sessionStorage.auth_token
                },
                dataType: "json",
                url: "http://www.developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/demo-get-method/",
                type: "GET",
                success: function( jsonObj, textStatus, xhr ) {
                    var htmlContent = $( "#logMsgDiv" ).html( ) + "<p>After login, execute demoteGetMethod with the auth-token obtained. JSON Message: " + jsonObj.message + "</p>";
                    $( "#logMsgDiv" ).html( htmlContent );
                },
                error: function( xhr, textStatus, errorThrown ) {
                    console.log( "HTTP Status: " + xhr.status );
                    console.log( "Error textStatus: " + textStatus );
                    console.log( "Error thrown: " + errorThrown );
                }
            } );
 
            // Execute demoPostMethod with the auth-token obtained
            $.ajax( {
                cache: false,
                crossDomain: true,
                headers: {
                    "service_key": "3b91cab8-926f-49b6-ba00-920bcf934c2a",
                    "auth_token": sessionStorage.auth_token
                },
                dataType: "json",
                url: "http://www.developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/demo-post-method/",
                type: "POST",
                success: function( jsonObj, textStatus, xhr ) {
                    var htmlContent = $( "#logMsgDiv" ).html( ) + "<p>Execute demoPostMethod with the auth-token obtained. JSON message: " + jsonObj.message + "</p>";
                    $( "#logMsgDiv" ).html( htmlContent );
                },
                error: function( xhr, textStatus, errorThrown ) {
                    console.log( "HTTP Status: " + xhr.status );
                    console.log( "Error textStatus: " + textStatus );
                    console.log( "Error thrown: " + errorThrown );
                }
            } );
 
            // Let's logout after all the above. No content expected
            $.ajax( {
                cache: false,
                crossDomain: true,
                headers: {
                    "service_key": "3b91cab8-926f-49b6-ba00-920bcf934c2a",
                    "auth_token": sessionStorage.auth_token
                },
                url: "http://www.developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/logout/",
                type: "POST",
                success: function( jsonObj, textStatus, xhr ) {
                    var htmlContent = $( "#logMsgDiv" ).html( ) + "<p>Let's logout after all the above. No content expected.</p>";
                    $( "#logMsgDiv" ).html( htmlContent );
                },
                error: function( xhr, textStatus, errorThrown ) {
                    console.log( "HTTP Status: " + xhr.status );
                    console.log( "Error textStatus: " + textStatus );
                    console.log( "Error thrown: " + errorThrown );
                }
            } );
 
            // This is what happens when someone reuses the authorisation token after a user had been logged out
            $.ajax( {
                cache: false,
                crossDomain: true,
                headers: {
                    "service_key": "3b91cab8-926f-49b6-ba00-920bcf934c2a",
                    "auth_token": sessionStorage.auth_token
                },
                url: "http://www.developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/demo-get-method/",
                type: "GET",
                success: function( jsonObj, textStatus, xhr ) {
                    var htmlContent = $( "#logMsgDiv" ).html( ) + "<p style='color: red;'>If this is portion is executed, something must be wrong</p>";
                    $( "#logMsgDiv" ).html( htmlContent );
                },
                error: function( xhr, textStatus, errorThrown ) {
                    var htmlContent = $( "#logMsgDiv" ).html( )
                            + "<p style='color: red;'>This is what happens when someone reuses the authorisation token after a user had been logged out"
                            + "<br />HTTP Status: " + xhr.status + ", Unauthorized access to demo-get-method</p>";
 
                    $( "#logMsgDiv" ).html( htmlContent );
                }
            } );
        </script>
    </body>
</html>

Resultatet

rest-auth-test.html behøver ikke at være pakket med war-filen, dette er for at adskille påkaldende klientscript fra server-side-appen for at simulere en krydsoprindelsesanmodning. For at køre rest-auth-test.html, er alt hvad du skal gøre at køre det fra en webbrowser. For mig har jeg gjort dette gennem Firefox med Firebug plugin, og nedenstående er resultatet:

Resultat af rest-auth-test.html

Det fungerede ret godt. Den første og den sidste anmodning vil blive afvist som 401 (uautoriseret) HTTP-status, fordi den blev udført før godkendelse og efter logout (ugyldig auth_token ).

Afsluttende ord

Når det kommer til at håndtere tilpassede HTTP-headere i en JAX-RS 2.0-applikation, skal du bare huske at have de tilpassede HTTP-headernavne, der skal inkluderes som en del af "Access-Control-Allow-Headers ” i svarfilteret, f.eks.

Access-Control-Allow-Headers: custom_header_name1, custom_header_name2

Derefter kunne HTTP-headerne nemt hentes i REST Web Service-metoderne ved hjælp af javax.ws.rs.core.HttpHeaders gennem REST-konteksten. Glem ikke begrænsningen og indvirkningen for CORS, som skal passes i både REST Request og Response interceptorer.

Tak fordi du læste og håber denne artikel hjælper.

Relaterede artikler:

  • Java EE 7 / JAX-RS 2.0 – CORS på REST (Sådan gør du REST API'er tilgængelige fra et andet domæne)
  • http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
  • http://www.html5rocks.com/en/tutorials/cors/
  • http://www.w3.org/TR/cors/
  • https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS

Java tag