Ok then I need to implement in two different ways. :( This is something to
improve (I think Java EE 8 is on that way) because security is something
transversal so it should be global, not having to implement several pieces
in several places.

Well thanks then last option is my option :)

El dj., 9 abr., 2015 a les 10:02, Romain Manni-Bucau (<[email protected]>)
va escriure:

> 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