How do I change the content of a 401 response to a separate format?
I have a JAX-RS application on WildFly 10 that needs to be provided with a simple Basic Auth.
It works so far, but if authentication fails, the server responds
<html>
<head>
<title>Error</title>
</head>
<body>Unauthorized</body>
</html>
which is not my desired answer. I would prefer a custom answer (json).
How to do it?
What I have done so far:
-
I set up a new Wildfly security domain in my server config using a simple UserRolesLoginModule (which is enough in my case):
<security-domain name="MySecurityDomain" cache-type="default"> <authentication> <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required"> <module-option name="usersProperties" value="${jboss.server.config.dir}/users.properties"/> <module-option name="rolesProperties" value="${jboss.server.config.dir}/roles.properties"/> <module-option name="hashAlgorithm" value="MD5"/> <module-option name="hashEncoding" value="base64"/> <module-option name="hashCharset" value="UTF-8"/> <module-option name="unauthenticatedIdentity" value="UnauthenticatedAccess"/> </login-module> </authentication> </security-domain>
-
I have annotated all the services in the application:
@SecurityDomain("MySecurityDomain") @RolesAllowed({ "RoleFromPropertyFile", "AnotherRoleFromPropertyFile" })
-
I created jboss-web.xml with content
<jboss-web> <security-domain>MySecurityDomain</security-domain> </jboss-web>
-
I have a web.xml where I have tried many different things with no success ... :-( Current content:
<security-constraint> <display-name>Deny all HTTP methods except GET and POST</display-name> <web-resource-collection> <web-resource-name>NextTest</web-resource-name> <url-pattern>/mypattern/*</url-pattern> <http-method-omission>GET</http-method-omission> <http-method-omission>POST</http-method-omission> </web-resource-collection> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>MySecurityRealm</realm-name> </login-config> <security-role> <description>Access to all application parts</description> <role-name>all</role-name> </security-role> <!-- and some more roles -->
-
I also implemented
ExceptionMapper<EJBAccessException>
to generate my own response. But this handler is only reached when I delete all contentweb.xml
.
My guess is that the seizure is doing the authorization and handling the tamper response. If I remove the security configuration in web.xml
, the EJBs are available, but without evaluating the BasicAuth header. In this case, all requests are rejected.
I could avoid writing a servlet and use ExceptionMapper instead.
Any ideas what I missed?
source to share
I've experimented a bit with some code, and while it's not pretty, you can try something like:
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
@Provider
public class AuthBodyResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
if((responseContext.getStatus() == 401) &&
(responseContext.getEntity() instanceof String))
responseContext.setEntity("no services for you!");
}
}
I've tested it a bit and it seems to work. Surely the challenge is where else is there a 401 with a String response body? I would have to check more to see if this covers everything.
source to share
This is how I do it:
@POST
@Consumes("application/json")
@Produces("application/json")
public Response create(Entity entity) {
try {
Entity created = service().create(entity);
return Response.created(location(created)).entity(created).build();
} catch (ServiceException e) {
return Response.status(e.getStatus()).entity(e).build();
}
}
Note the return type, Response . This allows you to customize the response, including header settings, etc. This also means that you need to write some more connection code.
I am using a custom one ServiceException
here which already has a status in it and use that to set the response code. Then I pass the exception itself, which will be returned as JSON.
source to share