Grails spring security break status 401 redirect to controller action to send error message

We are using spring-security-core: 2.0-RC4 , spring-security-rest: 1.4.0 plugin with grails 2.4.2. They both work fine. When the user enters invalid credentials the spring-security-rest: 1.4.0 plugin gives 401 which is configured in Config.groovy

grails.plugin.springsecurity.rest.login.failureStatusCode =  401

      

And here is a small snippet of the console output

rest.RestAuthenticationFilter  - Actual URI is /api/login; endpoint URL is /api/login
rest.RestAuthenticationFilter  - Applying authentication filter to this request
credentials.DefaultJsonPayloadCredentialsExtractor  - Extracted credentials from JSON payload. Username: admin@asdasdmopi.com, password: [PROTECTED]
rest.RestAuthenticationFilter  - Trying to authenticate the request
authentication.ProviderManager  - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
dao.DaoAuthenticationProvider  - User 'admin@something.com' not found
rest.RestAuthenticationFilter  - Authentication failed: Bad credentials
rest.RestAuthenticationFailureHandler  - Setting status code to 401
context.HttpSessionSecurityContextRepository  - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
context.SecurityContextPersistenceFilter  - SecurityContextHolder now cleared, as request processing completed

      

Now there is no error message or response, just a 401 status being sent to the client. Now I am trying to submit a 401 error response.

Added the following line to UrlMappings.groovy

"401"(controller:'unauthorized',action:'sendErrorResponse')

      

Created by UnauthorizedController.groovy and added by sendErrorResponse () as follows

def sendErrorResponse() { 
        try{
            int errorCode = grailsApplication.config.customExceptions.account.fourZeroOne.loginNotAuthorized.errorCode
            int status = grailsApplication.config.customExceptions.account.fourZeroOne.loginNotAuthorized.status
            String message = grailsApplication.config.customExceptions.account.fourZeroOne.loginNotAuthorized.message
            String extendedMessage = grailsApplication.config.customExceptions.account.fourZeroOne.loginNotAuthorized.extendedMessage
            String moreInfo = grailsApplication.config.customExceptions.account.fourZeroOne.loginNotAuthorized.moreInfo

            throw new AccountException(status,errorCode,message,extendedMessage,moreInfo)
        }catch(AccountException e){
            log.error e.errorResponse()
            response.setStatus(e.errorResponse().status)
            render e.errorResponse()
        }
    }

      

My thinking was that on 401 the controller will be called and the method will give an error response, but it doesn't work.

Is my approach correct?

Any other best practice or idea for implementing this?

Any pointers in the right direction are appreciated.

Thanks a ton.

+3


source to share


1 answer


You need to override the grails.plugin.springsecurity.rest.RestAuthenticationFailureHandler

component grails.plugin.springsecurity.rest.RestAuthenticationFailureHandler

with your own customized version.

It could be something like this:

@Slf4j
@CompileStatic
class CustomRestAuthenticationFailureHandler implements AuthenticationFailureHandler {

    /**
     * Configurable status code, by default: conf.rest.login.failureStatusCode?:HttpServletResponse.SC_FORBIDDEN
     */
    Integer statusCode

    MessageSource messageSource

    /**
     * Called when an authentication attempt fails.
     * @param request the request during which the authentication attempt occurred.
     * @param response the response.
     * @param exception the exception which was thrown to reject the authentication request.
     */
    void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setStatus(statusCode)
        response.addHeader('WWW-Authenticate', Holders.config.get("grails.plugin.springsecurity.rest.token.validation.headerName").toString())
    def errorMessage
    if (exception instanceof AccountExpiredException) {
        errorMessage = messageSource.getMessage("springSecurity.errors.login.expired", null as Object[], LocaleContextHolder.getLocale())
    } else if (exception instanceof CredentialsExpiredException) {
        errorMessage = messageSource.getMessage("springSecurity.errors.login.passwordExpired", null as Object[], LocaleContextHolder.getLocale())
    } else if (exception instanceof DisabledException) {
        errorMessage = messageSource.getMessage("springSecurity.errors.login.disabled", null as Object[], LocaleContextHolder.getLocale())
    } else if (exception instanceof LockedException) {
        errorMessage = messageSource.getMessage("springSecurity.errors.login.locked", null as Object[], LocaleContextHolder.getLocale())
    } else {
        errorMessage = messageSource.getMessage("springSecurity.errors.login.fail", null as Object[], LocaleContextHolder.getLocale())
    }
    PrintWriter out = response.getWriter()
    response.setContentType("aplication/json")
    response.setCharacterEncoding("UTF-8");
    out.print(new JsonBuilder([message: errorMessage]).toString());
    out.flush();
    }
}

      



And in your resources you should have a game

restAuthenticationFailureHandler(CustomRestAuthenticationFailureHandler) {
    statusCode = HttpServletResponse.SC_UNAUTHORIZED
    messageSource = ref("messageSource")
}

      

0


source







All Articles