文章目录
前言
首先找到LogoutFilter类,然后在doFilter方法的第一行打上断点,然后再浏览器进行退出操作,就可以开始springsecurity的退出逻辑了。
一、LogoutFilter中的doFilter方法
public class LogoutFilter extends GenericFilterBean {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
//先过去request和response域,在此处打上断点
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//方法名称叫需要注销,意思是请求URL是否为 /logout,如果是则进入if退出逻辑
if (requiresLogout(request, response)) {
//首先获取到上下文的Authentication认证对象
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (logger.isDebugEnabled()) {
logger.debug("Logging out user '" + auth
+ "' and transferring to logout destination");
}
//this.handle=CompositeLogoutHandler,用这个处理器进行退出的操作,进入该方法
this.handler.logout(request, response, auth);
//最后用到logoutSuccessHandler处理退出成功的逻辑,如果我们不实现退出成功的处理器
//默认使用SimpleUrlLogoutSuccessHandler,进入这个方法
logoutSuccessHandler.onLogoutSuccess(request, response, auth);
return;
}
chain.doFilter(request, response);
}
}
二、doFilter方法中的this.handler.logout(request, response, auth)方法
public final class CompositeLogoutHandler implements LogoutHandler {
//这个处理器中还包含了其他的LogoutHandler,如果没有自定义退出处理器的情况下一共会有三个退出处理器
private final List<LogoutHandler> logoutHandlers;
` @Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
for (LogoutHandler handler : this.logoutHandlers) {
//遍历logoutHandlers列表,处理退出逻辑,进入这个方法
handler.logout(request, response, authentication);
}
}
}
1.第一个Logout类PersistentTokenBasedRememberMeServices
public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices {
private PersistentTokenRepository tokenRepository = new InMemoryTokenRepositoryImpl();
@Override
public void logout(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) {
//首先调用了父类AbstractRememberMeServices的logout方法,进入该方法
super.logout(request, response, authentication);
if (authentication != null) {
//方法名称叫移除用户令牌,进入该方法
//tokenRepository默认是InMemoryTokenRepositoryImpl,如果使用jdbc存储用户信息,
//这里就是JdbcTokenRepositoryImpl
tokenRepository.removeUserTokens(authentication.getName());
}
}
}
public abstract class AbstractRememberMeServices implements RememberMeServices,
InitializingBean, LogoutHandler {
public static final String SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY = "remember-me";
private String cookieName = SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY;
//进入父类的logout方法
@Override
public void logout(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) {
if (logger.isDebugEnabled()) {
logger.debug("Logout of user "
+ (authentication == null ? "Unknown" : authentication.getName()));
}
//删除cookie的方法,进入
cancelCookie(request, response);
}
protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {
logger.debug("Cancelling cookie");
//它新建了一个cookie,cookieName的值就是remember-me
Cookie cookie = new Cookie(cookieName, null);
//设置存活时间为0,这是删除cookie的做法
cookie.setMaxAge(0);
//这里设置删除cookie的路径
/*private String getCookiePath(HttpServletRequest request) {
String contextPath = request.getContextPath();
return contextPath.length() > 0 ? contextPath : "/";
}*/
cookie.setPath(getCookiePath(request));
if (cookieDomain != null) {
cookie.setDomain(cookieDomain);
}
if (useSecureCookie == null) {
cookie.setSecure(request.isSecure());
}
else {
cookie.setSecure(useSecureCookie);
}
//给response添加cookie就实现删除cookie了
response.addCookie(cookie);
}
}
public class JdbcTokenRepositoryImpl extends JdbcDaoSupport implements
PersistentTokenRepository {
public static final String DEF_REMOVE_USER_TOKENS_SQL = "delete from persistent_logins where username = ?";
private String removeUserTokensSql = DEF_REMOVE_USER_TOKENS_SQL;
public void removeUserTokens(String username) {
//这里的removeUserTokensSql就是删除数据库表persistent_logins中username为我们退出的用户的username的行
getJdbcTemplate().update(removeUserTokensSql, username);
}
}
2.第二个Logout类SecurityContextLogoutHandler
public class SecurityContextLogoutHandler implements LogoutHandler {
private boolean invalidateHttpSession = true;
private boolean clearAuthentication = true;
public void logout(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) {
Assert.notNull(request, "HttpServletRequest required");
if (invalidateHttpSession) {
//从request域中获取session
HttpSession session = request.getSession(false);
if (session != null) {
logger.debug("Invalidating session: " + session.getId());
//让session无效
session.invalidate();
}
}
if (clearAuthentication) {
//获取上下文
SecurityContext context = SecurityContextHolder.getContext();
//将上下文中存储的Authentication对象设为null
context.setAuthentication(null);
}
//使用SecurityContextHolder工具类清除上下文
SecurityContextHolder.clearContext();
}
}
3.第三个Logout类SecurityContextLogoutHandler
public final class LogoutSuccessEventPublishingLogoutHandler implements LogoutHandler, ApplicationEventPublisherAware {
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
if (eventPublisher == null) {
return;
}
if (authentication == null) {
return;
}
//发布一些事件信息,跳过
eventPublisher.publishEvent(new LogoutSuccessEvent(authentication));
}
}
三、doFilter方法中的logoutSuccessHandler.onLogoutSuccess(request, response, auth)方法
public class SimpleUrlLogoutSuccessHandler extends
AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//调用了父类AbstractAuthenticationTargetUrlRequestHandler来进行处理,进入这个方法
super.handle(request, response, authentication);
}
}
public abstract class AbstractAuthenticationTargetUrlRequestHandler {
protected void handle(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//方法的名称是确定目标url,最终得到的路径是targetUrl = /login.html?logout
String targetUrl = determineTargetUrl(request, response, authentication);
if (response.isCommitted()) {
logger.debug("Response has already been committed. Unable to redirect to "
+ targetUrl);
return;
}
//利用DefaultRedirectStrategy来进行页面的重定向
redirectStrategy.sendRedirect(request, response, targetUrl);
}
}
public class DefaultRedirectStrategy implements RedirectStrategy {
public void sendRedirect(HttpServletRequest request, HttpServletResponse response,
String url) throws IOException {
//如果配置文件写了ContextPath,会组合
String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
redirectUrl = response.encodeRedirectURL(redirectUrl);
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to '" + redirectUrl + "'");
}
//最终使用了response来进行重定向页面到/login.html?logout
//至此退出操作就全部结束了。
response.sendRedirect(redirectUrl);
}
}
总结
springsecurity的退出功能是一定会用到的,了解一下还是很有必要的。