Hi

Le 9 avr. 2015 09:15, "Alex Soto" <[email protected]> a écrit :
>
> Hello, I am trying to create an application where I can mix Java EE
> security annotations in JAX-RS endpoints. I am using current TomEE 2
> Snapshot and basically I am creating two ContainerRequestFilter.
> One for authentication and another for authorization.
>
> @Provider
> @Priority(Priorities.AUTHENTICATION)
> public class JWTAuthenticationFilter implements ContainerRequestFilter {
>
>     private static final List<Class<? extends Annotation>>
> securityAnnotations =
>         Arrays.asList(DenyAll.class, PermitAll.class, RolesAllowed.class);
>
>     @Context
>     private ResourceInfo resourceInfo;
>
>     private UserService userservice = new UserService();
>
>     @Override
>     public void filter(ContainerRequestContext request) throws
IOException {
>         if (isSecuredResource()) {
>
>             String token = request.getHeaderString("x-access-token");
>
>             try {
>                 String username = getUsernameFromToken(token);
>                 final User user = userservice.findUser(username);
>
>                 request.setSecurityContext(new SecurityContext() {
>
>                     @Override
>                     public boolean isUserInRole(String role) {
>                         return user.isUserInRole(role);
>                     }
>
>                     @Override
>                     public boolean isSecure() {
>                         return false;
>                     }
>
>                     @Override
>                     public Principal getUserPrincipal() {
>                         return user;
>                     }
>
>                     @Override
>                     public String getAuthenticationScheme() {
>                         return SecurityContext.BASIC_AUTH;
>                     }
>                 });
>
>             } catch (java.text.ParseException | JOSEException |
> NullPointerException e) {
>                 request.abortWith(Response.status(404).build());
>             }
>         }
>     }
>
>     private boolean isSecuredResource() {
>
>         for (Class<? extends Annotation> securityClass :
> securityAnnotations) {
>             if (resourceInfo.getResourceMethod().isAnnotationPresent(
>                 securityClass)) {
>                 return true;
>             }
>         }
>
>         for (Class<? extends Annotation> securityClass :
> securityAnnotations) {
>             if (resourceInfo.getResourceClass().isAnnotationPresent(
>                 securityClass)) {
>                 return true;
>             }
>         }
>
>         return false;
>     }
>
>     private String getUsernameFromToken(String token)
>             throws java.text.ParseException, JOSEException {
>
>         SignedJWT signedJWT = SignedJWT.parse(token);
>         JWSVerifier verifier = new MACVerifier(SharedSecret.getSecret());
>
>         if (signedJWT.verify(verifier)) {
>             return signedJWT.getJWTClaimsSet().getSubject();
>         } else {
>             throw new JOSEException("Firm is not verified.");
>         }
>     }
> }
>
>
> note that I am creating a custom security context and for authorization:
>
> @Provider
> @Priority(Priorities.AUTHORIZATION)
> public class RolesAllowedFilter implements ContainerRequestFilter {
>
>   private static final Response NOT_FOUND = Response.status(
>       Response.Status.NOT_FOUND).entity("{\"message\": \"Resource Not
> Found\"}").build();
>
>   @Context
>   private ResourceInfo resourceInfo;
>
>   @Override
>   public void filter(ContainerRequestContext requestContext)
>       throws IOException {
>     Method resourceMethod = resourceInfo.getResourceMethod();
>
>     // DenyAll on the method take precedence over RolesAllowed and
PermitAll
>     if (resourceMethod.isAnnotationPresent(DenyAll.class)) {
>       requestContext.abortWith(NOT_FOUND);
>       return;
>     }
>
>     // RolesAllowed on the method takes precedence over PermitAll
>     RolesAllowed ra = resourceMethod.getAnnotation(RolesAllowed.class);
>     if(assertRole(requestContext, ra)) {
>       return;
>     }
>
>     // PermitAll takes precedence over RolesAllowed on the class
>     if (resourceMethod.isAnnotationPresent(PermitAll.class)) {
>       // Do nothing.
>       return;
>     }
>
>     if
(resourceInfo.getResourceClass().isAnnotationPresent(DenyAll.class))
> {
>       requestContext.abortWith(NOT_FOUND);
>     }
>
>     // RolesAllowed on the class takes precedence over PermitAll
>     ra =
resourceInfo.getResourceClass().getAnnotation(RolesAllowed.class);
>     if(assertRole(requestContext, ra)) {
>       return;
>     }
>   }
>
>   private boolean assertRole(ContainerRequestContext requestContext,
> RolesAllowed ra) {
>
>     if (ra != null) {
>       String[] roles = ra.value();
>       for (String role : roles) {
>         if (requestContext.getSecurityContext().isUserInRole(role)) {
>           return true;
>         }
>       }
>       requestContext.abortWith(NOT_FOUND);
>     }
>     return false;
>   }
> }
>
> And the business code:
>
> @Stateless
> @Path("/app")
> public class UiApplication {
>
>     @Inject
>     UserService userService;
>
>     @Inject
>     MovieService moviesService;
>
>     @POST
>     @Consumes(MediaType.APPLICATION_JSON)
>     @Produces(MediaType.APPLICATION_JSON)
>     public Response login(JsonObject jsonObject) throws JOSEException {
>
>       JsonString username = (JsonString) jsonObject.get("username");
>       JsonString password = (JsonString) jsonObject.get("password");
>
>       if (authenticate(username.getString(), password.getString())) {
>
>         String token = createToken(username.getString(), "example.com");
>
>         JsonObject responseDocument = Json.createObjectBuilder()
>           .add("user", Json.createObjectBuilder().add("username",
> username).build())
>           .add("token", token)
>           .build();
>
>         return Response.ok(responseDocument).build();
>
>       }
>
>       return Response.status(Status.NOT_FOUND).build();
>
>     }
>
>     @GET
>     @Produces(MediaType.APPLICATION_JSON)
>     @RolesAllowed("admin")
>     public Response movies() {
>         return Response.ok("{\"message\":\"a\"}").build();
>     }
>
>     private boolean authenticate(String username, String password) {
>         return this.userService.findUser(username, password);
>     }
>
>     private String createToken(String subject, String issuer) throws
> JOSEException {
>
>       JWSSigner signer = new MACSigner(SharedSecret.getSecret());
>
>       JWTClaimsSet claimsSet = new JWTClaimsSet();
>       claimsSet.setSubject(subject);
>       claimsSet.setIssueTime(new Date());
>       claimsSet.setIssuer(issuer);
>
>       SignedJWT signedJWT = new SignedJWT(new
> JWSHeader(JWSAlgorithm.HS256), claimsSet);
>       signedJWT.sign(signer);
>
>       return signedJWT.serialize();
>
>     }
>
> }
>
> If I run in this way it doesn't work because the endpoint is also an EJB
> and because security context of EJB is not shared with the JAX-RS, the
> method throws an 403 Forbidden. Is there any clean way to make that this
> don't happen? Of course I can make JAXRS class as none EJB, but this would
> be my last option.
>

Well with @Transactional it would makes sense since you handle security.
Using jaas would be the way to have custom logic in ejbs.

> Also is there a way to use @Inject in ContainerRequestFilter? Currently I
> cannot use it because the instance is not injected.
>

Not yet, dont recall what spec says about it btw

> Thank you so much.
>
> Alex.

Reply via email to