0

I am newbie to Java(came from .Net background), I am trying to write a RESTful service using Jersey framework. I referred this link http://www.vogella.com/tutorials/REST/article.html

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

Now I want to get the HTTPContext in the above method. Basically I am looking to get the logged-in user name, I hope that should be available using HttpContext.Request.

I am using windows NT authentication.

Can anybody please tell me how to get the HTTPContext/User information inside the Java RESTful service.

Atul Sureka
  • 3,085
  • 7
  • 39
  • 64
  • You're asking how to get the user, but you haven't explained how you are currently authenticating? Where is the user logging in? With what login mechanism are you using? Without authentication information, how are we supposed to know where the user is being stored? Or is "how to set up authentication (and then get the user)", the real question? If so, please state so. – Paul Samsotha Feb 24 '17 at 12:35
  • I am using windows NT authentication, User opens the browser, the web page sends the web request. – Atul Sureka Feb 25 '17 at 01:10

2 Answers2

1

You can use below code to get the HTTPContext in your REST resource.

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;
  }

}

let me know if you need any help.

Amit
  • 30,756
  • 6
  • 57
  • 88
  • Thanks for the response, won't the above code throw null reference exception? how httpServletRequest would be initialized ? – Atul Sureka Feb 24 '17 at 10:38
  • 1
    @AtulSureka it will be provided by Context annotation. please refer https://jersey.java.net/documentation/latest/user-guide.html#d0e2821 for more info on context annotation. – Amit Feb 24 '17 at 11:30
1

Short answer

The SecurityContext injected with the @Context annotation allows you to access security related information. It has a request scope and hold details about the user who is authenticated.

For other types that can be injected with @Context, check this answer.

Long answer

You must mind that REST authentication must be stateless. That is, you must not rely on sessions stored on server side, hence the user credentials must be sent in each request. In other words, each request must be authenticated/authorized. To understand this concept, this answer may be insightful.

See below how the SecurityContext can be used in a couple of approaches to secure your application:

Basic authentication

The Basic authentication scheme described in the RFC 7617 with HTTPS is a common and efficent approach to secure a REST application:

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==

[...]

You could use a ContainerRequestFilter to extract the user credentials from the Authorization request header, authenticate them against your authentication provider and then set a SecurityContext with the username for the request. The AuthenticationService implementation is up to you:

@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()));
    }
}

A custom SecurityContext implementation could be like:

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;
    }
}

Then the SecurityContext can be injected in any resource class using the @Context annotation:

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

    @Context
    private SecurityContext securityContext;
    
    ...
}

It also can be injected in a resource method parameter:

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

And then get the Principal from the SecurityContext:

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

Token based authentication

In an authentication scheme based on tokens, the token becomes a credential of the user.

Hard credentials such as username and password are exchanged for a token that must be sent in each request then the server can perform authentication/authorization. Tokens can be valid for a short amount of time, can be revoked, can carry scope details (what can be requested with the token), etc.

For more details, have a look at this answer. The SecurityContext can be used in the same way as explained above.

Community
  • 1
  • 1
cassiomolin
  • 124,154
  • 35
  • 280
  • 359