We are using spring-security-core:2.0-RC4 , spring-security-rest:1.4.0 plugin with grails 2.4.2. Both of them are working fine. When user enters invalid credentials, 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 the small snippet of 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 status 401 is send to client. Now I am trying to send a error response when there is 401 status.
Added following line in UrlMappings.groovy
"401"(controller:'unauthorized',action:'sendErrorResponse')
Created UnauthorizedController.groovy and added 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 render error response, but It doesn't work.
Is my Approach right?
Any other Best practice or idea to implement this?
Any pointers in right direction are appreciated.
Thanks a ton.
You need to override grails.plugin.springsecurity.rest.RestAuthenticationFailureHandler
bean with your own customized version.
It can 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.groovy you should have
restAuthenticationFailureHandler(CustomRestAuthenticationFailureHandler) {
statusCode = HttpServletResponse.SC_UNAUTHORIZED
messageSource = ref("messageSource")
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.