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.
