Well not using ejb is surely the current way. They will be reserved for scheduling in todays apps i guess. Le 9 avr. 2015 10:06, "Alex Soto" <[email protected]> a écrit :
> 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. > > >
