grails spring security rest status 401重定向到控制器的动作以抛出自定义错误消息
Posted
技术标签:
【中文标题】grails spring security rest status 401重定向到控制器的动作以抛出自定义错误消息【英文标题】:grails spring security rest status 401 redirect to controller's action to throw custom error message 【发布时间】:2014-12-26 10:46:41 【问题描述】:我们在 grails 2.4.2 中使用spring-security-core:2.0-RC4、spring-security-rest:1.4.0 plugin。他们俩都工作正常。当用户输入无效凭据时,spring-security-rest:1.4.0 插件会给出 401,这是在 Config.groovy 中配置的
grails.plugin.springsecurity.rest.login.failureStatusCode = 401
这里是控制台输出的小sn-p
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
现在没有错误消息或响应,只是将状态 401 发送给客户端。现在我尝试在出现 401 状态时发送错误响应。
在 UrlMappings.groovy 中添加以下行
"401"(controller:'unauthorized',action:'sendErrorResponse')
创建了 UnauthorizedController.groovy 并添加了 sendErrorResponse() 如下
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()
我的想法是在 401 上会调用控制器并且方法会呈现错误响应,但它不起作用。
我的方法对吗?
任何其他最佳实践或想法来实现这一点?
感谢任何正确方向的指针。
非常感谢。
【问题讨论】:
【参考方案1】:您需要用您自己的自定义版本覆盖grails.plugin.springsecurity.rest.RestAuthenticationFailureHandler
bean。
可能是这样的:
@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();
在你的 resources.groovy 中你应该有
restAuthenticationFailureHandler(CustomRestAuthenticationFailureHandler)
statusCode = HttpServletResponse.SC_UNAUTHORIZED
messageSource = ref("messageSource")
【讨论】:
以上是关于grails spring security rest status 401重定向到控制器的动作以抛出自定义错误消息的主要内容,如果未能解决你的问题,请参考以下文章
Grails - grails-spring-security-rest - 无法从 application.yml 加载 jwt 机密
grails-spring-security-rest 插件和悲观锁定