我创建了一个自定义Spring安全过滤器来为我们的客户端make api调用执行基于HMAC令牌的auth。
以下是过滤器的样子:
class BqCustomTokenFilter extends GenericFilterBean implements ApplicationEventPublisherAware {
def authenticationManager
def customProvider
AuthenticationSuccessHandler authenticationSuccessHandler
AuthenticationFailureHandler authenticationFailureHandler
SessionAuthenticationStrategy sessionAuthenticationStrategy
ApplicationEventPublisher applicationEventPublisher
@Override
void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
ServletException {
HttpServletResponse response = (HttpServletResponse) resp
/* wrap the request in order to read the inputstream multiple times */
MultiReadHttpServletRequest request = new MultiReadHttpServletRequest((HttpServletRequest) req);
if (!request.getRequestURI().startsWith('/api/')) {
// Should not happen
chain.doFilter(request, response)
return
}
logger.trace("filter called from remote IP = ${req.remoteAddr} for URL ${request.getRequestURI()}")
def requestBody = request.inputStream.getText()
final AuthHeader authHeader = HmacUtil.getAuthHeader(request);
if (authHeader == null) {
// invalid authorization token
logger.warn("Authorization header is missing");
authenticationFailureHandler.onAuthenticationFailure(request.getRequest(), response, null)
//unsuccessfulAuthentication(request, response, null)
return
}
final String apiKey = authHeader.getApiKey();
logger.trace("got request for apiKey = ${apiKey}")
if (apiKey) {
def myAuth = new BqAuthenticationToken(
credentials: apiKey,
authHeader: authHeader,
payload: requestBody,
requestDetails: [
scheme : request.getScheme(),
host : request.getServerName() + ":" + request.getServerPort(),
method : request.getMethod(),
resource : request.getRequestURI(),
contentType: request.contentType,
date1 : request.getHeader(HttpHeaders.DATE)
],
authenticated: false
)
try {
myAuth = authenticationManager.authenticate(myAuth)
if (!myAuth.authenticated) {
logger.warn("Authorization header does not match", ex)
//unsuccessfulAuthentication(request, response, ex)
//return
logger.warn("Could not authenticate")
SecurityContextHolder.clearContext()
response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
return
}
if (logger.isDebugEnabled()) {
logger.debug("Successfully Authenticated!!")
}
sessionAuthenticationStrategy.onAuthentication(myAuth, request, response)
} catch (BadCredentialsException | Exception ex) {
logger.warn("Authorization header does not match", ex)
//unsuccessfulAuthentication(request, response, ex)
//return
logger.warn("Could not authenticate")
SecurityContextHolder.clearContext()
response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
//respo.setStatus(statuscode)
//authenticationFailureHandler.onAuthenticationFailure((HttpServletRequest) request, response, ex)
}
try {
chain.doFilter(request, response)
return
} catch (IllegalStateException ex) {
logger.warn("=====> IllegalStateException", ex)
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED)
}
} else {
logger.warn("No API Key found in Request")
SecurityContextHolder.clearContext()
authenticationFailureHandler.onAuthenticationFailure((HttpServletRequest) request, response, null)
}
}
void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher
}
}
resources.groovy
sessionAuthenticationStrategy(NullAuthenticatedSessionStrategy)
bqCustomTokenFilter(com.bq.security.client.BqCustomTokenFilter) {
authenticationManager = ref("authenticationManager")
customProvider = ref("bqTokenAuthenticationProvider")
authenticationSuccessHandler = ref('authenticationSuccessHandler')
authenticationFailureHandler = ref('authenticationFailureHandler')
sessionAuthenticationStrategy = ref('sessionAuthenticationStrategy')
}
Config.groovy中
grails.plugin.springsecurity.providerNames = [
'bqTokenAuthenticationProvider',
'daoAuthenticationProvider',
'anonymousAuthenticationProvider',
'rememberMeAuthenticationProvider']
grails.plugin.springsecurity.filterChain.filterNames = [
'securityContextPersistenceFilter',
'logoutFilter',
'authenticationProcessingFilter',
'bqCustomTokenFilter',
'concurrencyFilter',
'switchUserProcessingFilter',
'rememberMeAuthenticationFilter',
'anonymousAuthenticationFilter',
'exceptionTranslationFilter',
'filterInvocationInterceptor',
]
grails.plugin.springsecurity.filterChain.chainMap = [
'/api/**': 'bqCustomTokenFilter',
'/**' : 'JOINED_FILTERS,-bqCustomTokenFilter'
]
我还安装了multitenant-single-db插件。身份验证工作绝对正常但是,偶尔会出现以下错误:
2016-10-19 14:06:51,120 +0530 [http-nio-8080-exec-1] ERROR UrlMappingsFilter:213 - Error when matching URL mapping [/api/execution/updateResult]:Cannot set request attribute - request is not active anymore!
java.lang.IllegalStateException: Cannot set request attribute - request is not active anymore!
at grails.plugin.multitenant.core.servlet.CurrentTenantServletFilter.doFilter(CurrentTenantServletFilter.java:53)
at com.bq.security.client.BqCustomTokenFilter$$EQ0438yG.doFilter(BqCustomTokenFilter.groovy:142)
at grails.plugin.springsecurity.web.filter.DebugFilter.invokeWithWrappedRequest(DebugFilter.java:102)
at grails.plugin.springsecurity.web.filter.DebugFilter.doFilter(DebugFilter.java:69)
at com.brandseye.cors.CorsFilter.doFilter(CorsFilter.java:100)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
这通常发生在客户经常发送帖子请求时。由于我在客户端内置了重试逻辑,因此工作正常,但这种异常非常烦人。 非常感谢任何帮助。