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