配置
大多数情况下,我们在访问一个系统时,会直接访问我们收藏的某个页面,或者首页,又或者是访问根路径,成功登录系统后,一般情况下,会重定向到这些页面。如果并没有明确的目的指向,或者说我们想要让系统在登录成功后,一直指向某个页面怎么办?
其实,这很简单,很显然,Spring Security 也考虑到了这一点,在原配置基础上,添加一项 defaultSuccessUrl 配置即可。
http.formLogin().loginPage("/login").defaultSuccessUrl("/index").permitAll()
接下来,启动系统,分别用几种情况测试一下。
访问个人中心页面 http://localhost:8080/springsecuritylearning/user/index,登录后,系统跳转到了个人中心页面。
查看Network标签,也确实是这样。
重启系统,或者清除浏览器缓存。
访问系统首页 http://localhost:8080/springsecuritylearning/index,系统跳转到了首页。
查看Network标签,也印证了这点。
接下来,访问系统根路径 http://localhost:8080/springsecuritylearning/,从原理上讲,应该跳转到根路径页面,由于系统配置访问根路径时,重定向到首页,所以,在用户成功登录后,会先重定向到根路径,然后又重定向到首页。
到此,可能还会有疑问,如果我要让系统不管什么情况,不管之前输入的什么地址,在成功登录后,都跳转到各自的个人中心、亦或是首页呢?
这个需求也很常见,比如,做自媒体的应该有感受,我们登录某个自媒体后台后,一般情况下,会直接跳转到后台的个人中心,展示一些统计信息,如粉丝数、收益等等。
Spring Security 也提供了这个配置项。
http.formLogin().loginPage("/login").defaultSuccessUrl("/user/index", true).permitAll()
重启系统后,再次尝试以上的几种情况,我们发现,系统果然会在登录成功后,始终跳转到个人中心页面。
原理剖析
Spring Security 框架在用户成功登录后的处理逻辑,相对来说比较复杂,比较绕,下面我们就关键逻辑,进行相关分析。
首先,框架默认的 AuthenticationSuccessHandler 为 SavedRequestAwareAuthenticationSuccessHandler。
判断当前Request是否缓存(另外需要看 Spring Security 是否开启了 Request 缓存,默认是开启的)。
......
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
......
如果 Spring Security 关闭了 Request 缓存,或者当前 Request 并没有被缓存,那么就走默认的认证成功逻辑。
否则,继续根据 alwaysUseDefaultTargetUrl 判断是否永远重定向到 defaultTargetUrl;亦或是,如果配置了targetUrlParameter 且当前 request 存在该参数值,那么,从缓存中移除当前request,并走默认的认证成功逻辑。
......
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
|| (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
......
以上情况都不满足,即 Spring Security 开启了 Request 缓存,且当前 request 被缓存了,框架即重定向到缓存 request 对应的地址。
// Use the DefaultSavedRequest URL
String targetUrl = savedRequest.getRedirectUrl();
logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
默认的认证成功逻辑,也不复杂,就是判断要重定向的地址这个逻辑,比较繁琐。
protected String determineTargetUrl(HttpServletRequest request,
HttpServletResponse response) {
if (isAlwaysUseDefaultTargetUrl()) {
return defaultTargetUrl;
}
// Check for the parameter and use that if available
String targetUrl = null;
if (targetUrlParameter != null) {
targetUrl = request.getParameter(targetUrlParameter);
if (StringUtils.hasText(targetUrl)) {
logger.debug("Found targetUrlParameter in request: " + targetUrl);
return targetUrl;
}
}
if (useReferer && !StringUtils.hasLength(targetUrl)) {
targetUrl = request.getHeader("Referer");
logger.debug("Using Referer header: " + targetUrl);
}
if (!StringUtils.hasText(targetUrl)) {
targetUrl = defaultTargetUrl;
logger.debug("Using default Url: " + targetUrl);
}
return targetUrl;
}
简而言之,就是如果 alwaysUseDefaultTargetUrl 为true,则重定向 defaultTargetUrl;如果配置了 targetUrlParameter 且其对应的值不为空,则重定向到该地址;如果配置的 useReferer 为 true 且其值不为空,则重定向到该地址;否则,则重定向到 defaultTargetUrl。
针对成功登录后的这段复杂逻辑,画了一个流程图,可以参照这些关键代码,加深一下理解。
源码
github
liuminglei/SpringSecurityLearning
gitee
luas/SpringSecurityLearning
我是银河架构师,十年饮冰,难凉热血,愿历尽千帆,归来仍是少年!
如果文章对您有帮助,请举起您的小手,轻轻【三连】,这将是笔者持续创作的动力源泉。当然,如果文章有错误,或者您有任何的意见或建议,请留言。感谢您的阅读!
文章不定时更新,可微信搜索「银河架构师」,精彩内容,先睹为快!