开发中,碰到既要设置成功登录后跳转到指定页面,而不是springsecurity
中默认的index页面,又要登录成功后要做一些操作,比如某个用户的权限菜单获取。如果successHandler
和defaultSuccessUrl(successForwardUrl)
连用,只生效一个,并且是谁在后面谁生效。
代码如下,这样设置只有successHandler一个生效,不能跳转到默认的
"/success"
页面。同理,将defaultSuccessUrl()
放在前面。则不执行配置的successHandler
中的代码。
@Autowired
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
http.csrf().disable()
.formLogin()
.loginPage("/loginPage") //登录页面跳转请求
.loginProcessingUrl("/login") //登录发起的post请求方法
//.successForwardUrl("/success")
.defaultSuccessUrl("/success")
.successHandler(myAuthenticationSuccessHandler)
.permitAll();//登录成功页面
MyAuthenticationSuccessHandler 的代码
package com.filter;
import com.entiy.PmGroup;
import com.entiy.PmResource;
import com.model.UserDetailModel;
import com.service.PmResourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
@Component
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired
private PmResourceService resourceService;
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
/**
自己登录认证成功后的逻辑代码
*/
...........
..........
.......
......
super.onAuthenticationSuccess(request, response, authentication); //以便在某个页面session过期,刷新页面登录后能成功跳转的之前的页面。
}
代码中
onAuthenticationSuccess
的源代码。
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = this.requestCache.getRequest(request, response);
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
} else {
String targetUrlParameter = this.getTargetUrlParameter();
if (!this.isAlwaysUseDefaultTargetUrl() && (targetUrlParameter == null || !StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
this.clearAuthenticationAttributes(request);
String targetUrl = savedRequest.getRedirectUrl();
this.getRedirectStrategy().sendRedirect(request, response, targetUrl);
} else {
this.requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
}
}
}
其中,当
SavedRequest
为空时(测试发现,只有用户点退出时,此项才会为空,因为退出登录,框架会执行会清除session,项目刚启动成功,springsecurity框架会向其中添加这个session,即使没有登录,也有这个内容。
)它又调用了它父类的onAuthenticationSuccess
,其中requestCache.getRequest(request, response)
代码和父类的onAuthenticationSuccess(request, response, authentication)
的代码如下:
public SavedRequest getRequest(HttpServletRequest currentRequest, HttpServletResponse response) {
HttpSession session = currentRequest.getSession(false);
return session != null ? (SavedRequest)session.getAttribute(this.sessionAttrName) : null;
}
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
this.handle(request, response, authentication);
this.clearAuthenticationAttributes(request);
}
调用的handle具体代码如下:
package org.springframework.security.web.authentication;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
public abstract class AbstractAuthenticationTargetUrlRequestHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
private String targetUrlParameter = null;
private String defaultTargetUrl = "/";
private boolean alwaysUseDefaultTargetUrl = false;
private boolean useReferer = false;
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
protected AbstractAuthenticationTargetUrlRequestHandler() {
}
protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String targetUrl = this.determineTargetUrl(request, response, authentication);
if (response.isCommitted()) {
this.logger.debug(LogMessage.format("Did not redirect to %s since response already committed.", targetUrl));
} else {
this.redirectStrategy.sendRedirect(request, response, targetUrl);
}
}
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
return this.determineTargetUrl(request, response);
}
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
if (this.isAlwaysUseDefaultTargetUrl()) {
return this.defaultTargetUrl;
} else {
String targetUrl = null;
if (this.targetUrlParameter != null) {
targetUrl = request.getParameter(this.targetUrlParameter);
if (StringUtils.hasText(targetUrl)) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Using url %s from request parameter %s", targetUrl, this.targetUrlParameter));
}
return targetUrl;
}
}
if (this.useReferer && !StringUtils.hasLength(targetUrl)) {
targetUrl = request.getHeader("Referer");
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Using url %s from Referer header", targetUrl));
}
}
if (!StringUtils.hasText(targetUrl)) {
targetUrl = this.defaultTargetUrl;
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Using default url %s", targetUrl));
}
}
return targetUrl;
}
}
protected final String getDefaultTargetUrl() {
return this.defaultTargetUrl;
}
public void setDefaultTargetUrl(String defaultTargetUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl), "defaultTarget must start with '/' or with 'http(s)'");
this.defaultTargetUrl = defaultTargetUrl;
}
protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String targetUrl = this.determineTargetUrl(request, response, authentication);
if (response.isCommitted()) {
this.logger.debug(LogMessage.format("Did not redirect to %s since response already committed.", targetUrl));
} else {
this.redirectStrategy.sendRedirect(request, response, targetUrl);
}
}
根据debug可以看出,是defaultTargetUrl属性是最后的跳转链接
通过查看defaultSuccessUrl的源码,正常登录时,实际是生成了一个successHandler,并只将配置的
"/success"
放入其中。源码如下:
public final T defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) {
SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
handler.setDefaultTargetUrl(defaultSuccessUrl); // defaultSuccessUrl为 /success
handler.setAlwaysUseDefaultTargetUrl(alwaysUse);
this.defaultSuccessHandler = handler;
return this.successHandler(handler);
}
结论:不论是
successHandler
还是defaultSuccessUrl(successForwardUrl)
都是使用了框架中的SavedRequestAwareAuthenticationSuccessHandler方法
,定义了框架中一个defaultTargetUrl
属性,来判断登录成功的跳转地址。所以谁在后面就把前面的覆盖掉了,相对于重新定义了一次。所以只需在自己定义的successHandler
方法中,末尾添加一行代码即可,如下:
super.setDefaultTargetUrl("/medo/list"); // 自己需要跳转的url 此为添加的代码
super.onAuthenticationSuccess(request, response, authentication);