我正在使用grails以及spring security和angularjs。当用户会话已过期并且用户单击页面上的ajax操作,而不是使用401响应时,应用程序会尝试重定向到登录页面,而该页面没有原始ajax操作的响应。
我仍在使用传统的登录页面,而我的应用程序仍有一些传统的页面链接,因此当会话过期且用户点击页面链接时,我想重定向到登录页面。
如果用户点击ajax请求,我想获得401响应而不是重定向的html响应,以便我可以在我的javascript中进行重定向。
我有以下配置设置。
grails.plugin.springsecurity.providerNames = ['hriLoginClientAuthenticationProvider']
grails.plugin.springsecurity.useSecurityEventListener = true
grails.plugin.springsecurity.failureHandler.defaultFailureUrl = '/login?error=1'
grails.plugin.springsecurity.auth.loginFormUrl = '/login'
grails.plugin.springsecurity.logout.postOnly = false
如何让ajax请求不重定向到登录页面,我需要做什么?
答案 0 :(得分:1)
我遇到了类似的问题,并在过滤器链中实现了一个过滤器来检测AJAX请求并使用自定义的HTTP状态进行响应(如果愿意,可以将其更改为401)。
基本上有三个部分。第一个是过滤器。它是一个servlet过滤器,检查请求以及会话中的身份验证状态。其次,在Resources.groovy
中将过滤器定义为应用程序上下文中的bean。最后,将它插入Spring Security过滤器链中,我在Bootstrap.groovy
中完成了。
我现在就告诉你。
首先是servlet过滤器(在src / java下)
package com.xyz.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.security.web.util.ThrowableCauseExtractor;
import org.springframework.web.filter.GenericFilterBean;
public class AjaxTimeoutRedirectFilter extends GenericFilterBean {
// private static final Logger logger =
// LoggerFactory.getLogger(AjaxTimeoutRedirectFilter.class);
private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
private int customSessionExpiredErrorCode = 901;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
// logger.debug("Chain processed normally");
} catch (IOException ex) {
throw ex;
} catch (Exception ex) {
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
RuntimeException ase = (AuthenticationException) throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class,
causeChain);
if (ase == null) {
ase = (AccessDeniedException) throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class,
causeChain);
}
if (ase != null) {
if (ase instanceof AuthenticationException) {
throw ase;
} else if (ase instanceof AccessDeniedException) {
if (authenticationTrustResolver
.isAnonymous(SecurityContextHolder.getContext()
.getAuthentication())) {
// logger.info("User session expired or not logged in yet");
String ajaxHeader = ((HttpServletRequest) request)
.getHeader("X-Requested-With");
if ("XMLHttpRequest".equals(ajaxHeader)) {
// logger.info("Ajax call detected, send {} error code",
// this.customSessionExpiredErrorCode);
HttpServletResponse resp = (HttpServletResponse) response;
resp.sendError(this.customSessionExpiredErrorCode);
} else {
// logger.info("Redirect to login page");
throw ase;
}
} else {
throw ase;
}
}
}
}
}
private static final class DefaultThrowableAnalyzer extends
ThrowableAnalyzer {
/**
* @see org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap()
*/
protected void initExtractorMap() {
super.initExtractorMap();
registerExtractor(ServletException.class,
new ThrowableCauseExtractor() {
public Throwable extractCause(Throwable throwable) {
ThrowableAnalyzer.verifyThrowableHierarchy(
throwable, ServletException.class);
return ((ServletException) throwable)
.getRootCause();
}
});
}
}
public void setCustomSessionExpiredErrorCode(
int customSessionExpiredErrorCode) {
this.customSessionExpiredErrorCode = customSessionExpiredErrorCode;
}
}
其次,在Resources.groovy
beans = {
ajaxTimeoutRedirectFilter(com.xyz.security.AjaxTimeoutRedirectFilter)
}
最后,将过滤器放入Spring Security过滤器链(我为此使用了BootStrap.groovy
)
import grails.plugin.springsecurity.SecurityFilterPosition
import grails.plugin.springsecurity.SpringSecurityUtils
class BootStrap {
def init = { servletContext ->
SpringSecurityUtils.clientRegisterFilter('ajaxTimeoutRedirectFilter', SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order + 10)
}
def destroy = {
}
}
答案 1 :(得分:0)
您是否考虑过"锁定屏幕"当用户在客户端闲置时?当然,您应该在服务器端处理会话结束,但事实上它似乎比等待来自客户端的操作更清晰,更安全(特别是如果用户在屏幕上左右移动了一些敏感数据)。
查看此ng-idle指令。