项目框架:spring boot + jpa,maven管理
昨天在项目中使用shiro的时候,发现shiro认证成功后一直跳转的界面是"/"这个界面,报一个404异常,自己配置的successUrl感觉没有起作用,总结前人博客,并且查看源码,知道了原因:
首先,shiro在认证成功后,会调用onLoginSuccess方法,
onLoginSuccess(token, subject, request, response);
追踪这个方法
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
ServletRequest request, ServletResponse response) throws Exception {
issueSuccessRedirect(request, response);
//we handled the success redirect directly, prevent the chain from continuing:
return false;
}
发现内部调用了issueSuccessRedirect(request,response)方法,继续追踪
protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception {
WebUtils.redirectToSavedRequest(request, response, getSuccessUrl());
}
发现它内部又调用了WebUtils工具类的redirectToSavedRequest()方法,继续追踪
public static void redirectToSavedRequest(ServletRequest request, ServletResponse response, String fallbackUrl)
throws IOException {
String successUrl = null;
boolean contextRelative = true;
SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request);
if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase(AccessControlFilter.GET_METHOD)) {
successUrl = savedRequest.getRequestUrl();
contextRelative = false;
}
if (successUrl == null) {
successUrl = fallbackUrl;
}
if (successUrl == null) {
throw new IllegalStateException("Success URL not available via saved request or via the " +
"successUrlFallback method parameter. One of these must be non-null for " +
"issueSuccessRedirect() to work.");
}
WebUtils.issueRedirect(request, response, successUrl, null, contextRelative);
}
}
在这个方法里面终于找到了与successUrl相关的内容,分析逻辑关系,我们可以知道,在第一个if语句中,如果savedRequest不会null,则会覆盖掉我们设置的successUrl,所以,我们自己配置的successUrl只有在SavedRequest对象为null的时候,才会生效。
最后,我们需要了解这个savedRequest对象是怎么来的,继续追踪,进入WebUtils.getAndClearSavedRequest(request)方法
public static final String SAVED_REQUEST_KEY = "shiroSavedRequest";
public static SavedRequest getAndClearSavedRequest(ServletRequest request) {
SavedRequest savedRequest = getSavedRequest(request);
if (savedRequest != null) {
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.removeAttribute(SAVED_REQUEST_KEY);
}
return savedRequest;
}
public static SavedRequest getSavedRequest(ServletRequest request) {
SavedRequest savedRequest = null;
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession(false);
if (session != null) {
savedRequest = (SavedRequest) session.getAttribute(SAVED_REQUEST_KEY);
}
return savedRequest;
}
从上段源码我们可以看出来,savedRequest对象是绑定在session中的,并且它的key值为shiroSavedRequest。
到这里,我们基本清楚了我们设置的successUrl为什么没有生效了。
解决方案:
需要在我们自己的继承了FormAuthenticationFilter的类中重写onloginSuccess()方法。
/**
* 因为发现设置的successUrl没生效,所以追踪源码发现如果SavedRequest对象不为null,则它会覆盖掉我们设置
* 的successUrl,所以我们要重写onLoginSuccess方法,在它覆盖掉我们设置的successUrl之前,去除掉
* SavedRequest对象,SavedRequest对象的获取方式为:
* savedRequest = (SavedRequest) session.getAttribute(SAVED_REQUEST_KEY);
* public static final String SAVED_REQUEST_KEY = "shiroSavedRequest";
* 解决方案:从session对象中移出shiroSavedRequest
*/
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
ServletResponse response) throws Exception {
if (!StringUtils.isEmpty(getSuccessUrl())) {
// getSession(false):如果当前session为null,则返回null,而不是创建一个新的session
Session session = subject.getSession(false);
if (session != null) {
session.removeAttribute("shiroSavedRequest");
}
}
return super.onLoginSuccess(token, subject, request, response);
}