I'm not sure if I understand you right. I also had an issue to send a proper error message with json back to the client. I have written all (filter & provider) custom, because I authenticate via HTTPHeaderFields. The error handling and sending proper messages back to the client has been the biggest issue. Short overview will follow:
At first I created custom delegatingAuthenticationEntryPoint and AccesssDeniedHandler.
Snipp of secure config
... @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() // IMPORTANT: Add Filter after "ExceptionTranslation". // If not AuthenticationException from Custom Filter or Custom Provider // will not be catched by AuthenticationEntryPoint. .addFilterAfter(httpClientFilter(), ExceptionTranslationFilter.class) .exceptionHandling() // catch AuthenticationExeption and SecureToken with authenticated=false .authenticationEntryPoint(delegatingAuthenticationEntryPoint()) // catch PermissionDenied Exeption e.g. missing in authorizeRequests() .accessDeniedHandler(new ClientRestAccessDeniedHandler()) .and() ...
The ClientRestAccessDeniedHandler looks like this
public class ClientRestAccessDeniedHandler implements AccessDeniedHandler{ @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { final Logger logger = Logger.getLogger(ClientRestAccessDeniedHandler.class); if(logger.isDebugEnabled()) logger.debug("Requered Role for this request is missing!"); HTTPAuthenticationErrorSender.sendResponse(request, response, SecurityContextHolder.getContext().getAuthentication()); } }
The Custom DelegationEntriyPoint looks very similar. Both call the HTTPAuthenticationErrorSender. It will send the HttpResponse generated by low level stuff :-).
public final class HTTPAuthenticationErrorSender { public static void sendResponse(HttpServletRequest request, HttpServletResponse response, Authentication token) throws JsonGenerationException, JsonMappingException, IOException{ final Logger logger = Logger.getLogger(HTTPAuthenticationErrorSender.class); if(!(token instanceof HTTPRestSecureToken)){ if (token != null){ response.sendError(403, "No valide AuthenticationToken found. Token instance of: "+token.getClass().toString()); if(logger.isDebugEnabled()) logger.debug("Send default HTTP Response 403. No HTTPRestSecureToken found. " + "Token is instance of: "+token.getClass().getName()); } else { response.sendError(403, "No valide AuthenticationToken found. Token is null"); if(logger.isDebugEnabled()) logger.debug("Send default HTTP Response 403. No HTTPRestSecureToken found. " + "Token is null"); } return; } HTTPRestSecureToken restToken = (HTTPRestSecureToken) token; ObjectMapper mapper = new ObjectMapper(); AuthenticationErrorResponse authErrorResponse = new AuthenticationErrorResponse(restToken.getAuthStatus().getErrorCode(),restToken.getAuthStatus().getDescription()); String content = mapper.writeValueAsString(authErrorResponse); HTTPRestPrincipal principal = (HTTPRestPrincipal) token.getPrincipal(); if(logger.isDebugEnabled()){ logger.debug("AccessDenied for request: ["+principal.getFullURI()+"] clientID: ["+principal.getClientID() + "] loginMail: ["+principal.getLoginMail()+"]"); logger.debug("Send following json response: "+content); } response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().print(content); } }
To send proper Information I track status information in my custom SecureToken. The token will run throw secure chain filter and provider and will be filled up with information.
And yes I would say it is not very small for a common issue like this. But I couldn't find another way. The default Impl. are not detailed enough for my case. I hope it will help a little or gives you a push in the right direction.