3

I am following this answer: Best practice for REST token-based authentication with JAX-RS and Jersey to implement the REST API authentication. But the JAX-RS Authentication Filter is not triggered via @Secured annotation.

I finished until the part Securing your REST endpoints by adding @Secured on the method.

This is how I am doing this.

Secured.java

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured { }

AuthenticationFilter.java

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

    private static final String AUTHENTICATION_SCHEME = "Bearer";

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        // Get the Authorization header from the request
        String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);

        // Validate the Authorization header
        if (!isTokenBasedAuthentication(authorizationHeader)) {
            abortWithUnauthorized(requestContext);
            return;
        }

        // Extract the token from the Authorization header
        String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim();

        try {
            validateToken(token);
        } catch (Exception e) {
            abortWithUnauthorized(requestContext);
        }
    }

    private boolean isTokenBasedAuthentication(String authorizationHeader) {
        // Check if the Authorization header is valid
        // It must not be null and must be prefixed with "Bearer" plus a whitespace
        // Authentication scheme comparison must be case-insensitive
        return (authorizationHeader != null &&
            authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " "));
    }

    private void abortWithUnauthorized(ContainerRequestContext requestContext) {
        // Abort the filter chain with a 401 status code
        // The "WWW-Authenticate" is sent along with the response
        requestContext.abortWith(
                Response.status(Response.Status.UNAUTHORIZED)
                    .header(HttpHeaders.WWW_AUTHENTICATE, AUTHENTICATION_SCHEME)
                    .build());
    }

    private void validateToken(String token) throws Exception {
        // Check if it was issued by the server and if it's not expired
        // Throw an Exception if the token is invalid
    }

MyApis.java

@Path("api")
public class MyApis {

    @GET
    @Secured
    @Path("me")
    @Produces(MediaType.APPLICATION_JSON)
    public Map<String, X500Name> whoAmI() {
        return ImmutableMap.of("me", legalName);
    }

    // other APIs
}

When I call /api/me, I can get the response directly without any authentication header provided. It seems like the filter is not triggered or not registered properly.

I have seen this question JAX RS, my filter is not working, but it does not solve my problem.

And how I understand the following is that web.xml is not needed right?

This solution uses only the JAX-RS 2.0 API, avoiding any vendor specific solution. So, it should work with the most popular JAX-RS 2.0 implementations, such as Jersey, RESTEasy and Apache CXF.

It's important mention that if you are using a token-based authentication, you are not relying on the standard Java EE web application security mechanisms offered by the servlet container and configurable via application's web.xml descriptor.

fluency03
  • 2,637
  • 7
  • 32
  • 62

1 Answers1

1

Late for answer, but the solution to this problem is to add AuthenticationFilter to your classes.

public class App extends Application { @Override public Set<Class<?>> getClasses() { final Set<Class<?>> classes = new HashSet<>(); classes.add(MyApis.class); classes.add(AuthenticationFilter.class); return classes; } }