在使用已可用的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标头的名称如下:

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 httpHeaders

httpHeaders是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客户端的作用如下:

  1. 首先,REST客户端将在没有服务密钥和授权令牌的情况下进行REST API调用。 呼叫将被拒绝,HTTP状态为401(未授权)
  2. 接下来,它将使用“ username2”的特定服务密钥(目前在Authenticator.java中进行硬编码)执行登录。 收到授权令牌后,它将被存储在sessionStorage中以备将来使用。
  3. 然后,它将调用虚拟的get和post方法。
  4. 之后,它将执行注销
  5. 一旦用户注销,客户端将执行对虚拟获取和发布方法的调用,但是由于授权令牌的到期,访问将被HTTP状态401拒绝。

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身份验证和授权相关推荐

  1. input发送a.jax_Java EE 7 / JAX-RS 2.0:具有自定义HTTP标头的简单REST API身份验证和授权...

    input发送a.jax 在使用已可用的HTTP协议实施Web服务时,REST带来了很多便利. 通过仅通过指定的URL触发GET,POST和其他HTTP方法,您将确保通过REST服务的响应来完成某些工 ...

  2. java验证身份证合法性_Java安全性,第2部分:身份验证和授权

    关于本教程 本教程是关于什么的? 也许没有比应用程序安全更重要的软件工程主题. 攻击是昂贵的,无论是来自内部还是外部,而且某些攻击可能会使软件公司承担赔偿责任. 随着计算机(尤其是Internet)技 ...

  3. 身份验证协议和java安全框架

    一.身份验证协议 1.OAuth 关于 OAuth实际上是什么存在很多混淆.有些人认为 OAuth 是一种登录流程(例如,当您使用 Google Login 登录应用程序时),有些人认为 OAuth ...

  4. Java EE 7 / JAX-RS 2.0 – REST上的CORS

    Java EE REST应用程序通常在开箱即用的开发机器上运行良好,该开发机器上所有服务器端资源和客户端UI均指向" localhost"或127.0.0.1. 但是,当涉及跨域部 ...

  5. Java EE meets Web 2.0

    http://www.ibm.com/developerworks/web/library/wa-aj-web2jee/?S_CMP=cn-a-wa&S_TACT=105AGX52 Summa ...

  6. oidc_使用Java EE和OIDC构建Java REST API

    oidc "我喜欢编写身份验证和授权代码." 〜从来没有Java开发人员. 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证. ...

  7. java ee打印功能_Java EE:异步构造和功能

    java ee打印功能 介绍 Java EE具有许多API和构造以支持异步执行. 从可伸缩性和性能的角度来看,这是至关重要的. 让我们假设2个模块相互交互. 当模块A (发送方)以同步方式向模块B ( ...

  8. java jax-rs_在Java EE 7和WildFly中使用Bean验证来验证JAX-RS资源数据

    java jax-rs 我过去已经两次接触过这个主题. 首先,在我的文章< 在Java EE 6中将Bean验证与JAX-RS集成>中 ,描述了甚至在Java EE平台规范中尚未定义之前, ...

  9. 使用Java EE和OIDC构建Java REST API

    "我喜欢编写身份验证和授权代码." 〜从来没有Java开发人员. 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证. Java ...

最新文章

  1. 【PAT (Advanced Level) Practice】1008 Elevator (20 分)
  2. 强化学习Reinforcement Learning
  3. 从 Java 到 Scala(一):面向对象谈起
  4. [攻防世界 pwn]——forgot
  5. 鬼才项斌,用人工智能推动教育服务创新
  6. Word 2003中为什么修改一个段落的文章结果整篇文档的格式都变?
  7. shiro包_Shiro--从一个简单的 Realm 开始权限认证
  8. activemq mysql 配置详解_activeMQ数据库配置
  9. 虾米回应“关闭”消息:不予置评;明年 Win 10 或将原生运行安卓应用;Perl 项目治理新规| 极客头条...
  10. Ubuntu 16.04扩展swap分区(内存)
  11. pandas—pandas.DataFrame.iterrows的使用
  12. 如果生个儿子,一定得教他打篮球。
  13. Ajax方法详解以及多个Ajax并发执行
  14. 不用u盘安装linux真机,无需u盘和光盘安装linux
  15. 央视《家有妙招》整理版,值得永远收藏!
  16. 东方证券万字报告:微信视频号进入稳定的发展期
  17. Ogre 3DMax导出插件的制作
  18. FrameMaker从零到学习编码
  19. iOS:开发者账号申请
  20. 基于JSF框架的在线棋牌游戏平台

热门文章

  1. JAVA基础学习大全(笔记)
  2. 3-1 Apache Shiro权限管理框架介绍
  3. tcp协议中的长连接和短连接服务器,谈谈HTTP协议中的短轮询、长轮询、长连接和短链接...
  4. 多智能体强化学习_基于多智能体强化学习主宰星际争霸游戏
  5. bmp180气压传感器工作原理_陕西压力传感器的工作原理信息推荐
  6. 网络——提交表单数据(post方式)
  7. java开发指南_Java 12新功能完整指南
  8. react api_使用Java EE 8中的React式API加速服务
  9. python异常值如何处理_如何处理异常
  10. junit数据驱动测试_使用Junit和Easytest进行数据驱动的测试