Java RESTful 服务中的 httpcontext

httpcontext in Java RESTful service

我是 Java 的新手(来自 .Net 背景),我正在尝试使用 Jersey 框架编写 RESTful 服务。我提到了这个 link http://www.vogella.com/tutorials/REST/article.html

  @GET
  @Produces(MediaType.TEXT_PLAIN)
  public String sayPlainTextHello() {
    return "Hello Jersey";
  }

现在我想获取上面方法中的HTTPContext。基本上我希望获得登录的用户名,我希望应该可以使用 HttpContext.Request.

我正在使用 windows NT 身份验证。

谁能告诉我如何在 Java RESTful 服务中获取 HTTPContext/User 信息。

您可以使用以下代码在您的 REST 资源中获取 HTTPContext

public class RestResource {

    @Context
    private HttpServletRequest httpServletRequest;


 @GET
  @Produces(MediaType.TEXT_PLAIN)
  public String sayPlainTextHello() {
    RequestContext requestContext = (RequestContext) httpServletRequest.getAttribute(RequestConstants.REQUEST_CONTEXT);
 // Get whatever the value set in your request context
 String userName = requestContext.requestContext.getQueryString("userName");
    return userName;
  }

}

如果您需要任何帮助,请告诉我。

简答

SecurityContext injected with the @Context 注释允许您访问与安全相关的信息。它有一个请求范围并保存有关已通过身份验证的用户的详细信息。

对于可以用@Context, check this answer注入的其他类型。

长答案

您必须注意 REST 身份验证必须是无状态的。也就是说,您不能依赖存储在服务器端的会话,因此必须在每个请求中发送用户凭据。也就是说,每个请求必须是authenticated/authorized。理解这个概念,这个answer可能有见地

请参阅下文如何使用 SecurityContext 两种方法来保护您的应用程序:

基本身份验证

RFC 7617 中描述的使用 HTTPS 的基本身份验证方案是保护 REST 应用程序的一种常用且有效的方法:

2. The 'Basic' Authentication Scheme

The Basic authentication scheme is based on the model that the client needs to authenticate itself with a user-id and a password for each protection space ("realm"). [...] The server will service the request only if it can validate the user-id and password for the protection space applying to the requested resource.

[...]

To receive authorization, the client

  1. obtains the user-id and password from the user,

  2. constructs the user-pass by concatenating the user-id, a single colon (:) character, and the password,

  3. encodes the user-pass into an octet sequence,

  4. and obtains the basic-credentials by encoding this octet sequence using Base64 into a sequence of US-ASCII characters.

[...]

If the user agent wishes to send the user-id "Aladdin" and password "open sesame", it would use the following header field:

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

[...]

您可以使用 ContainerRequestFilterAuthorization 请求中提取用户凭据 header,根据您的身份验证提供商对它们进行身份验证,然后使用请求的用户名设置 SecurityContextAuthenticationService 实现由您决定:

@Provider
@Priority(Priorities.AUTHENTICATION)
class AuthenticationFilter implements ContainerRequestFilter {

    @Inject
    private AuthenticationService authenticationService;

    @Override
    public void filter(ContainerRequestFilter requestContext) {
        
        // Get the HTTP Authorization header from the request
        String authorizationHeader = 
            requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
        
        // Check if the HTTP Authorization header is present and formatted correctly 
        if (authorizationHeader == null || !authorizationHeader.startsWith("Basic ")) {
            throw new NotAuthorizedException("Authorization header must be provided");
        }
        
        // Extract the Basic authentication token from the HTTP Authorization header
        String token = authorizationHeader.substring("Basic".length()).trim();
        
        // Decodes the token
        String credentials = Base64.getDecoder().decode(token);
        String[] split = decoded.split(":");
        
        try {

            // Authenticate the credentials against in your authentication provider
            authenticationService.authenticate(split[0], split[1]);

        } catch (Exception e) {
            requestContext.abortWith(
                Response.status(Response.Status.UNAUTHORIZED).build());
        }
        
        // Updates the security context for the request
        SecurityContext securityContext = requestContext.getSecurityContext(); 
        requestContext.setSecurityContext(
            new CustomSecurityContext(split[0], securityContext.isSecure()));
    }
}

自定义 SecurityContext 实现可能是这样的:

public class CustomSecurityContext implements SecurityContext {

    private final String username;
    private final boolean secure;
    
    public BasicSecurityContext(String username, boolean secure) {
        this.username = username;
        this.secure = secure;
    }
    
    @Override
    public Principal getUserPrincipal() {
        return new Principal() {
            @Override
            public String getName() {
                return username;
            }
        };
    }
    
    @Override
    public String getAuthenticationScheme() {
        return SecurityContext.BASIC_AUTH;
    }
    
    @Override
    public boolean isSecure() {
        return secure;
    }
    
    @Override
    public boolean isUserInRole(String role) {
        return true;
    }
}

然后可以使用 @Context 注释将 SecurityContext 注入到任何资源 class 中:

@Path("/example")
public class MyResource {

    @Context
    private SecurityContext securityContext;
    
    ...
}

也可以在资源方法参数中注入:

@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response myResourceMethod(@PathParam("id") Long id, 
                                 @Context SecurityContext securityContext) {
    ...
}

然后从SecurityContext得到Principal:

Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();

基于令牌的身份验证

在基于令牌的身份验证方案中,令牌成为用户的凭据。

用户名和密码等硬凭证被交换为必须在每个请求中发送的令牌,然后服务器才能执行 authentication/authorization。令牌可以在短时间内有效,可以撤销,可以携带范围详细信息(令牌可以请求什么)等。

有关详细信息,请查看此答案SecurityContext 的使用方法与上述相同。