Java EE 7 / JAX-RS 2.0:具有自定义HTTP标头的简单REST API身份验证和授权
在使用已可用的HTTP協議實施Web服務時,REST帶來了很多便利。 通過僅通過指定的URL觸發GET,POST和其他HTTP方法,您將確保通過REST服務的響應來完成某些工作。 但是,無論REST給開發人員帶來了什么便利,安全性和訪問控制的主題都應始終得到解決。 本文將向您展示如何使用HTTP標頭和JAX-RS 2.0攔截器來實現基于用戶的簡單身份驗證。
認證者
讓我們從一個Authenticator類開始。 具有以下代碼的DemoAuthenticator提供了必要的方法,用于對請求訪問REST Web服務的所有用戶進行身份驗證。 請通讀代碼,并在此處提供注釋以指導理解。
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 databaseusersStorage.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." );} }通用代碼說明:
通常,只有幾個重要的項目組成了身份驗證器,即: 服務密鑰 , 授權令牌 , 用戶名和密碼 。 用戶名和密碼通常成對使用。
服務密鑰
服務密鑰對于某些讀者而言可能是新的。 在某些公共REST API服務中,系統會生成服務密鑰(有時也稱為API密鑰),然后將其發送到允許訪問REST服務的用戶/客戶端(通過電子郵件或其他方式)。 因此,除了僅使用用戶名和密碼登錄REST服務外,系統還將檢查服務密鑰,是否允許用戶/客戶端訪問REST API。 用戶名,密碼和服務密鑰均已在上面的代碼中預定義,僅用于演示目的。
授權令牌
進行身份驗證后(通過login()方法),系統將為已身份驗證的用戶生成授權令牌。 該令牌通過HTTP響應傳遞回用戶/客戶端,以后將用于任何REST API調用。 用戶/客戶端將必須找到一種方法來在整個登錄會話中進行存儲和使用。 我們稍后再討論。
必需的HTTP標頭名稱定義
向前發展,而不是將服務密鑰和授權令牌作為HTTP參數(Form或Query)傳遞到服務器端應用程序,我們將其作為HTTP Header傳遞。 這是為了使請求在被目標REST方法處理之前先被過濾。 HTTP標頭的名稱如下:
| service_key | 使HTTP客戶端能夠訪問REST Web服務的服務密鑰。 這是認證和授權HTTP請求的第一層。 |
| auth_token | 用戶名/密碼認證時生成的令牌,將用于任何REST Web Service調用(稍后顯示的認證方法除外)。 |
REST API實施
為了方便和進一步減少代碼錯誤,讓我們將HTTP標頭名稱作為靜態最終變量放入接口中,以供其余類使用。
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"; }為了實現身份驗證過程和其他演示方法,在DemoBusinessRESTResourceProxy中定義了方法的簽名,在適當的HTTP方法,參數中定義了業務簽名,并在DemoBusinessRESTResource中定義了業務實現。
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); }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;@Overridepublic 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();}}@Overridepublic Response demoGetMethod() {JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();jsonObjBuilder.add( "message", "Executed demoGetMethod" );JsonObject jsonObj = jsonObjBuilder.build();return getNoCacheResponseBuilder( Response.Status.OK ).entity( jsonObj.toString() ).build();}@Overridepublic Response demoPostMethod() {JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();jsonObjBuilder.add( "message", "Executed demoPostMethod" );JsonObject jsonObj = jsonObjBuilder.build();return getNoCacheResponseBuilder( Response.Status.ACCEPTED ).entity( jsonObj.toString() ).build();}@Overridepublic 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()方法用于驗證用戶名,密碼以及正確的服務密鑰。 在login()之后 ,將生成授權令牌并將其返回給客戶端。 客戶端稍后將不得不將其用于任何其他方法調用。 demoGetMethod()和demoPostMethod()只是偽方法,它們出于演示目的返回JSON消息,但有一個特殊條件,即必須存在有效??的授權令牌。 logout()方法用于使用戶退出REST服務; 用戶由“ auth_token ”標識。
服務密鑰和授權令牌將通過以下方式提供給REST服務方法:
@Context HttpHeaders httpHeadershttpHeaders是javax.ws.rs.core.HttpHeaders的一個實例,是一個包含標題名稱和值的對象,供以后使用該應用程序。 但是為了使REST服務接受HTTP標頭,首先需要通過REST請求攔截器和響應攔截器來完成某些操作。
通過JAX-RS 2.0攔截器使用HTTP標頭進行身份驗證
由于某些安全限制,只是不要希望可以使用任何REST客戶端傳遞任何HTTP標頭,并希望REST服務接受它。 就是那樣行不通。
為了使特定的標頭在REST服務中被接受,我們必須非常明確地在響應過濾器攔截器中定義HTTP標頭的接受。
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() );@Overridepublic 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是一個實現ContainerResponseFilter的JAX-RS 2.0攔截器。 不要忘記同時使用@Provide和@PreMatching對其進行注釋。 為了允許某些特定的自定義HTTP標頭被接受,標頭名稱“ Access-Control-Allow-Headers ”后跟帶有“,”的標頭值,因為分隔符必須作為自定義標頭值的一部分添加。 這是通知瀏覽器或REST客戶端允許的自定義標頭的方法。 其余的標頭用于CORS,您可以在我們的文章Java EE 7 / JAX-RS 2.0 – REST上的CORS(如何使REST API從其他域訪問)中 。
接下來,要驗證和驗證服務密鑰和授權令牌,我們需要從HTTP標頭中將其提取出來,并使用請求過濾器攔截器對其進行預處理。
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() );@Overridepublic 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 keyrequestCtx.abortWith( Response.status( Response.Status.UNAUTHORIZED ).build() );return;}// For any pther methods besides login, the authToken must be verifiedif ( !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() );}}} }要獲取標頭值,我們調用ContainerRequestContext對象實例的getHeaderString()方法,例如:
String serviceKey = requestCtx.getHeaderString( "service_key" );DemoRESTRequestFilter中的其余代碼在驗證和驗證服務密鑰和授權令牌方面非常簡單。
REST服務部署
不要忘記定義用于啟用REST服務的web.xml。
web.xml的代碼:
javax.ws.rs.core.Application1javax.ws.rs.core.Application/rest-api/*對于此演示,我將編譯后的代碼打包到一個名為RESTSecurityWithHTTPHeaderDemo.war的war文件中。 我選擇在開發人員域crappad.com(此博客的域)上的Glassfish 4.0上進行部署。 如果您正在閱讀本教程中的所有內容,則可以選擇自己的其他域。 REST API URL將采用以下格式:
http://<domain>:<port>/RESTSecurityWithHTTPHeaderDemo/rest-api/path/method-path/無論如何,我正在使用的測試客戶端的URL摘要是:
| 方法 | REST URL | HTTP方法 |
| DemoBusinessRESTResourceProxy.login() | http://developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/login/ | 開機自檢 |
| DemoBusinessRESTResourceProxy。 demoGetMethod() | http://developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/demo-get-method/ | 得到 |
| DemoBusinessRESTResourceProxy。 demoPostMethod() | http://developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/demo-post-method/ | 開機自檢 |
| DemoBusinessRESTResourceProxy.logout() | http://developerscrappad.com:8080/RESTSecurityWithHTTPHeaderDemo/rest-api/demo-business-resource/logout/ | 開機自檢 |
REST客戶端
總而言之,這是我編寫的用于測試REST API的REST客戶端。 REST客戶端只是一個HTML文件(特別是HTML5,它支持Web存儲),該文件利用jQuery進行REST API調用。 REST客戶端的作用如下:
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>結果
rest-auth-test.html不需要與war文件打包在一起,這是為了將調用客戶端腳本與服務器端應用程序分開以模擬跨域請求。 要運行rest-auth-test.html,您需要做的就是從Web瀏覽器執行它。 對我來說,我已經通過Firefox使用Firebug插件完成了此操作,結果如下:
rest-auth-test.html的結果
效果很好。 第一個和最后一個請求將被拒絕為401(未經授權)HTTP狀態,因為它是在身份驗證之前和注銷之后執行的(無效auth_token )。
最后的話
在JAX-RS 2.0應用程序中處理自定義HTTP標頭時,只需記住在響應過濾器中將自定義HTTP標頭名稱作為“ Access-Control-Allow-Headers ”的一部分包含在內即可,例如
Access-Control-Allow-Headers: custom_header_name1, custom_header_name2之后,可以通過REST上下文在javax.ws.rs.core.HttpHeaders的幫助下,在REST Web服務方法中輕松獲得HTTP標頭。 不要忘記對CORS的限制和影響,在REST請求和響應攔截器中都應注意這一點。
感謝您的閱讀,希望本文對您有所幫助。
相關文章:
- Java EE 7 / JAX-RS 2.0 – REST上的CORS(如何使REST API從其他域訪問)
- http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
- http://www.html5rocks.com/zh-CN/tutorials/cors/
- http://www.w3.org/TR/cors/
- https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
翻譯自: https://www.javacodegeeks.com/2014/10/java-ee-7-jax-rs-2-0-simple-rest-api-authentication-authorization-with-custom-http-header.html
總結
以上是生活随笔為你收集整理的Java EE 7 / JAX-RS 2.0:具有自定义HTTP标头的简单REST API身份验证和授权的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 申请微信新账号
- 下一篇: 分生区细胞特点 分生区细胞特点介绍