Glassfish @RolesAllowed with a special SecurityContext

The question I'm going to ask is a little more complicated, and I have not yet found the answer. Maybe because I was looking for the wrong thing. But I hope you help me with this.

I used the following tutorial to implement a custom SecurityContext that uses tokens instead of basic user / password authentication.

Basically, it initializes and injects a ResourceFilterFactory, which itself will inject a ResourceFilter for every HTTP request sent to the application.

This ResourceFilter looks for the Authentication header in the request, accepts its contents, and then authenticates the user. If the user is authenticated, he is entered into the request using SecurityContext.

I converted the code to make it work like EJB.

Here is the code:

web.xml

<init-param> <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name> <param-value>com.myapp.rest.filter.ResourceFilterFactory</param-value> </init-param> 

ResourceFilterFactory.java // This class introduces the EJB SecurityContextFilter

 @Named @Stateless @LocalBean public class ResourceFilterFactory extends RolesAllowedResourceFilterFactory{ @EJB(name="securityContextFilter") private SecurityContextFilter securityContextFilter; @PostConstruct private void init(){ System.out.println("ResourceFilterFactory initialized"); } @Override public List<ResourceFilter> create(AbstractMethod am) { //System.out.println("Creating resource filters list"); List<ResourceFilter> filters = super.create(am); if (filters == null) { filters = new ArrayList<ResourceFilter>(); } List<ResourceFilter> securityFilters = new ArrayList<ResourceFilter>(filters); securityFilters.clear(); securityFilters.add(0, securityContextFilter); return securityFilters; } } 

SecurityContextFilter.java // This EJB is called on every HTTP request that the server receives. It checks the Authentication header and gets the user associated with the token.

 @Named("securityContextFilter") @Stateless @LocalBean @Provider public class SecurityContextFilter implements ResourceFilter, ContainerRequestFilter { protected static final String HEADER_AUTHORIZATION = "Authorization"; @EJB private AuthorizationService authorizationService; @Override public ContainerRequest filter(ContainerRequest req) { System.out.println("Auth header: " + req.getHeaderValue(HEADER_AUTHORIZATION)); String sessionToken = req.getHeaderValue(HEADER_AUTHORIZATION); UserEntity entity = null; try { //entity = authorizationService.getParaUsingSessionToken(sessionToken); // removing the part that retrieves the user from the database ExternalUser user = new ExternalUser(); user.setEmailAddress(" lol@lol.lol "); user.setFirstName("lol"); user.setLastName("LOL"); user.setRole("arole"); req.setSecurityContext(new org.company.server.rest.filter.SecurityContextImpl(user)); } catch (AuthenticationException e) { System.out.println("authentication exception"); ExternalUser user = new ExternalUser(); req.setSecurityContext(new org.company.server.rest.filter.SecurityContextImpl(user)); } return req; } @Override public ContainerRequestFilter getRequestFilter() { return this; } @Override public ContainerResponseFilter getResponseFilter() { return null; } } 

SecurityContextImpl.java // This class is entered into the request and has the isUserInRole () method, which should be used by @RolesAllowed annotation

 public class SecurityContextImpl implements SecurityContext { private final ExternalUser user; public SecurityContextImpl(ExternalUser user) { //System.out.println("SecurityContext created : " + user.getFirstName()); this.user = user; } public Principal getUserPrincipal() { return user; } public boolean isUserInRole(String role) { System.out.println("Checking access rights : " + role + " / " + this.user.getRole()); return user.getRole().equalsIgnoreCase(role); } public boolean isSecure() { return false; } public String getAuthenticationScheme() { return SecurityContext.BASIC_AUTH; } } 

ExternalUser.java // An entity is created and populated with database user information.

 @XmlRootElement public class ExternalUser implements Principal { private String id; private String firstName; private String lastName; private String emailAddress; private boolean isVerified; private String phoneNumber; private String professionalId; private String role; public ExternalUser() {} public ExternalUser(UserEntity user) { this.setEmailAddress(user.getEmailAddress()); this.setFirstName(user.getFirstName()); this.setLastName(user.getLastName()); this.setRole(user.getRole().toString()); this.setPhoneNumber(user.getPhoneNumber()); this.setProfessionalId(user.getProfessionnalID()); } // Getters and setters boilerplate code... } 

Finally, the Jersey WebService:

 @Path("/account") @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) @Stateless @LocalBean public class UserRestService { @Context private SecurityContext security; @GET @Path("info") @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML }) public Response getInfo() { ExternalUser user = (ExternalUser)security.getUserPrincipal(); System.out.println("Email Address Of User : " + user.getEmailAddress()); if (!security.isUserInRole("arole")){ return Response.status(403).build(); } return Response.ok(user).build(); } } 

WebService works well , I get an instance of ExternalUser using the embedded SecurityContext. But if I use the @RolesAllowed({"arole"}) annotation, Glassfish gives me this error:

 INFO: JACC Policy Provider:Failed Permission Check: context (" org.company.app.server/org_company_app_server_internal ") , permission (" ("javax.security.jacc.EJBMethodPermission" "UserRestService" "getInfo,Local,org.company.server.rest.models.authentication.RestSession") ") WARNING: EJB5184:A system exception occurred during an invocation on EJB UserRestService, method: public javax.ws.rs.core.Response org.company.server.rest.services.UserRestService.getInfo() WARNING: javax.ejb.AccessLocalException: Client not authorized for this invocation at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1888) at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:212) at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88) at com.sun.proxy.$Proxy140.getInfoPara(Unknown Source) at org.company.server.rest.services.__EJB31_Generated__UserRestService__Intf____Bean__.getInfoPara(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60) at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$ResponseOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:205) at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75) at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288) at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147) at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108) at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147) at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84) at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1469) at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1400) at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1349) at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1339) at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:416) at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537) at com.sun.jersey.spi.container.servlet.ServletContainer.doFilter(ServletContainer.java:895) at com.sun.jersey.spi.container.servlet.ServletContainer.doFilter(ServletContainer.java:843) at com.sun.jersey.spi.container.servlet.ServletContainer.doFilter(ServletContainer.java:804) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175) at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655) at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161) at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231) at com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317) at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195) at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:849) at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:746) at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1045) at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:228) at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90) at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79) at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54) at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59) at com.sun.grizzly.ContextTask.run(ContextTask.java:71) at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532) at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513) at java.lang.Thread.run(Thread.java:722) WARNING: StandardWrapperValve[default]: PWC1406: Servlet.service() for servlet default threw exception javax.ejb.AccessLocalException: Client not authorized for this invocation at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1888) at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:212) at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88) at com.sun.proxy.$Proxy140.getInfoPara(Unknown Source) at org.company.server.rest.services.__EJB31_Generated__UserRestService__Intf____Bean__.getInfoPara(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) ... 

It seems to me that I need to configure Glassfish to use the created SecurityContextImpl. I do not know why this does not work, since it is correctly entered, and I can call its methods in my code.

I could just use the isUserInRole() method manually, but I would just avoid the problem, and not run into it. I apologize for the long post, but now I think that you have all the information necessary to help me with this. Thank you in advance for your help.

Emerik

+4
source share
2 answers

Ok, I just finished this part (actually all parts except EmailGateway).

Firstly, I thank Ian Porter for his work - its high quality. And I apologize for mistakenly referring to him as a "Porter."

Something is happening with the formatting of Stackoverflow code, so keep in mind that some code is executing code codes.

For your problem, I did this:

  • web.xml - I do not use except Faces
  • Instead of web.xml and for registering various providers, I created the AuthApplicationConfig.java class

     @ApplicationPath("rest") public class AuthApplicationConfig extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> resources = new java.util.HashSet<>(); // REST resources resources.add(HealthCheckResource.class); resources.add(PasswordResource.class); resources.add(UserResource.class); resources.add(VerificationResource.class); // Filters (Auth) resources.add(RolesAllowedDynamicFeature.class); resources.add(SecurityContextFilter.class); // Misc resources.add(GenericExceptionMapper.class); return resources; } } 

See https://java.net/jira/browse/JERSEY-1634 for why you need to register classes, even if they are annotated using @Provider

  • ResourceFilterFactory.java - I have not used
  • SecurityContextFilter.java is slightly different:

     @Provider @Priority(Priorities.AUTHENTICATION) // So it comes in before org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature public class SecurityContextFilter implements ContainerRequestFilter { @Inject Logger logger; protected static final String HEADER_AUTHORIZATION = "Authorization"; protected static final String HEADER_DATE = "x-java-rest-date"; protected static final String HEADER_NONCE = "nonce"; private AuthorizationService authorizationService; ApplicationConfig config; @Inject public SecurityContextFilter(UserRepository userRepository, UserService userService, ApplicationConfig config) { delegateAuthorizationService(userRepository, userService, config); this.config = config; } /** * If there is an Authorisation header in the request extract the session * token and retrieve the user * * Delegate to the AuthorizationService to validate the request * * If the request has a valid session token and the user is validated then a * user object will be added to the security context * * Any Resource Controllers can assume the user has been validated and can * merely authorize based on the role * * Resources with @PermitAll annotation do not require an Authorization * header but will still be filtered * * @param request * the ContainerRequest to filter * */ @Override public void filter(ContainerRequestContext requestContext) throws IOException { System.out.println("SecurityContextFilter / filter("+printContainerRequestContext(requestContext)+")"); String authToken = requestContext.getHeaderString(HEADER_AUTHORIZATION); String requestDateString = requestContext.getHeaderString(HEADER_DATE); String nonce = requestContext.getHeaderString(HEADER_NONCE); AuthorizationRequestContext context = new AuthorizationRequestContext( requestContext.getUriInfo().getPath(), requestContext.getMethod(), requestDateString, nonce, authToken); ExternalUser externalUser = authorizationService.authorize(context); requestContext .setSecurityContext(new SecurityContextImpl(externalUser)); System.out.println(String.format(" END OF SecurityContextFilter / filter - AuthorizationRequestContext: %s, externalUser:%s", context,externalUser)); } private String printContainerRequestContext(ContainerRequestContext requestContext) { return String.format("[ContainerRequestContext:%s]", requestContext); } /** * Specify the AuthorizationService that the application should use * * @param userRepository * @param userService * @param config */ private void delegateAuthorizationService(UserRepository userRepository, UserService userService, ApplicationConfig config) { System.out.println("SecurityContextFilter - requireSignedRequests?"+config.requireSignedRequests()); if (config.requireSignedRequests()) { this.authorizationService = new RequestSigningAuthorizationService( userRepository, userService, config); } else { this.authorizationService = new SessionTokenAuthorizationService( userRepository); } } @Inject public void setConfig(ApplicationConfig config) { this.config = config; } } 
  • SecurityContextImpl is the same as Iain:

     public class SecurityContextImpl implements SecurityContext { private final ExternalUser user; public SecurityContextImpl(ExternalUser user) { this.user = user; } @Override public Principal getUserPrincipal() { return user; } @Override public boolean isUserInRole(String role) { if(role.equalsIgnoreCase(Role.anonymous.name())) { return true; } if(user == null) { throw new InvalidAuthorizationHeaderException(); } System.out.println(String.format("SecurityContextImpl / isUserInRole - role:%s, user:%s", role, user)); return user.getRole().equalsIgnoreCase(role); } @Override public boolean isSecure() { return false; } @Override public String getAuthenticationScheme() { return SecurityContext.BASIC_AUTH; } } 
  • UserResource is defined as Iain and is similar to your UserRestService:

     @Path("/user") // @Component @Produces({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON }) @RequestScoped public class UserResource { // A Social thing that is not needed // private ConnectionFactoryLocator connectionFactoryLocator; @Inject Logger logger; @Inject protected UserService userService; @Inject protected VerificationTokenService verificationTokenService; @Inject protected EmailServicesGateway emailServicesGateway; @Context protected UriInfo uriInfo; // @Inject // protected ApplicationConfig config; // @Autowired // public UserResource(ConnectionFactoryLocator connectionFactoryLocator) { // this.connectionFactoryLocator = connectionFactoryLocator; // } @PermitAll @POST public Response signupUser(CreateUserRequest request) { AuthenticatedUserToken token = userService.createUser(request, Role.authenticated); verificationTokenService.sendEmailRegistrationToken(token.getUserId()); URI location = uriInfo.getAbsolutePathBuilder().path(token.getUserId()).build(); return Response.created(location).entity(token).build(); } @RolesAllowed("admin") @Path("{userId}") @DELETE public Response deleteUser(@Context SecurityContext sc, @PathParam("userId") String userId) { ExternalUser userMakingRequest = (ExternalUser) sc.getUserPrincipal(); userService.deleteUser(userMakingRequest, userId); return Response.ok().build(); } ... 
+3
source

how about your web.xml?

Have you already placed these filters inside web.xml?

 <init-param> <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name> <param-value>com.yourcompany.filter.SecurityFilter</param-value> </init-param> <init-param> <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name> <param-value>com.sun.jersey.api.container.filter.RolesAllowedResourceFilterFactory</param-value> </init-param> 
+1
source

Source: https://habr.com/ru/post/1489878/


All Articles