springsecurity的退出登录/注销流程


前言

首先找到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的退出功能是一定会用到的,了解一下还是很有必要的。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Security退出登录可以通过以下步骤实现: 1. 在Spring Security配置文件中添加logout配置: ``` http.logout() .logoutUrl("/logout") .logoutSuccessUrl("/login") .invalidateHttpSession(true) .deleteCookies("JSESSIONID"); ``` 2. 在Web应用程序中添加一个处理/logout请求的控制器: ``` @Controller public class LogoutController { @RequestMapping(value="/logout", method = RequestMethod.GET) public String logoutPage (HttpServletRequest request, HttpServletResponse response) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null){ new SecurityContextLogoutHandler().logout(request, response, auth); } return "redirect:/login?logout"; } } ``` 3. 在Web应用程序中添加一个退出登录的链接或按钮,链接到/logout请求: ``` <a href="/logout">退出登录</a> ``` 当用户点击退出登录链接或按钮时,将调用/logout请求,Spring Security将清除用户的身份验证信息并重定向到登录页面。 ### 回答2: Spring Security是一个强大而灵活的安全框架,它可以实现用户管理、认证授权、角色控制等安全功能。在一个Spring应用程序中,我们经常需要为用户提供退出登录的功能,这是保障用户安全的重要措施。在Spring Security中,实现退出登录的方法有以下几种: 1.使用Spring Security提供的logout()方法 Spring Security提供了logout()方法,可以使用户退出登录。可以在Spring Security的配置类里面添加这个方法指定退出登录的URL地址和成功退出登录后的跳转地址: ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .logout() .logoutSuccessUrl("/login") // 退出成功后跳转地址 .logoutUrl("/logout"); // 退出请求的URL } } ``` 2.使用Spring MVC的Controller 如果应用程序中使用了Spring MVC,那么我们也可以使用Spring MVC的Controller来实现退出登录功能。可以在Controller里面添加一个映射方法来处理退出登录请求,重定向到登录页面: ``` @Controller public class LogoutController { @GetMapping("/logout") public String logout(HttpServletRequest request) { // 手动注销当前用户 SecurityContextHolder.getContext().setAuthentication(null); // 重定向到登录页面 return "redirect:/login"; } } ``` 3.使用JavaScript实现退出登录 最后一种方法是使用JavaScript来实现退出登录功能。我们可以在页面上添加一个退出登录的按钮,当用户点击按钮时,通过JavaScript代码发送请求来实现退出登录: ``` $('#logout').click(function() { $.post("/logout", function() { window.location.href = "/login"; }); }); ``` 以上三种方法中,第一种方法使用了Spring Security提供的logout()方法来实现退出登录,最为简单快捷;第二种方法使用Spring MVC的Controller来实现退出登录,比较灵活;第三种方法使用JavaScript实现退出登录,可以在前端页面上实现交互。可以根据实际情况选择合适的方法来实现退出登录功能。 ### 回答3: 在Spring Security中,退出登录是一个常见的功能,可以让用户在不需要再次登录的情况下退出应用程序。Spring Security提供了两个默认退出登录的方式:通过URL和通过表单。以下是关于这两种方式的介绍。 通过URL退出登录 通过URL退出登录是最简单的方式。用户可以访问一个指定的URL(通常是/logout)以退出登录Spring Security将会清除已登录的用户的安全上下文,并跳转至指定的URL(通常是登录页面)。 例如,在Spring Boot应用程序中,可以通过添加以下配置来启用URL退出登录: ``` @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .logout() // 开启退出登录支持 .logoutUrl("/my/logout") // 自定义退出登录的URL地址 .logoutSuccessUrl("/my/index") // 退出登录后跳转的URL地址 .invalidateHttpSession(true) // 清除session .deleteCookies("JSESSIONID") // 删除cookie .permitAll(); } } ``` 相应地,在页面中,可以通过一个超链接或者一个button来触发退出登录操作: ``` <a href="/my/logout">退出登录</a> <button onclick="location.href='/my/logout'">退出登录</button> ``` 通过表单退出登录 通过表单退出登录需要创建一个HTML表单,并将其提交到指定的URL(通常是/logout)。该表单需要包含一个隐藏字段(CSRF)和一个提交按钮。Spring Security将检测这个表单,并告诉Spring Security配置的LogoutHandler和LogoutSuccessHandler执行退出登录操作。 例如,一个简单的表单可能看起来像这样: ``` <form th:action="@{/logout}" method="post"> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> <button type="submit">退出登录</button> </form> ``` 在Spring Boot应用程序中,可以通过添加以下配置来启用表单退出登录: ``` @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .logout() // 开启退出登录支持 .logoutUrl("/my/logout") // 自定义退出登录的URL地址 .logoutSuccessUrl("/my/index") // 退出登录后跳转的URL地址 .invalidateHttpSession(true) // 清除session .deleteCookies("JSESSIONID") // 删除cookie .permitAll() .csrf() // 开启CSRF保护 .csrfTokenRepository(csrfTokenRepository()) // 自定义CSRF令牌的实现 .and() .addFilterAfter(csrfFilter(), CsrfFilter.class); // 添加到CSRF过滤器之后 } private CsrfTokenRepository csrfTokenRepository() { HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); repository.setHeaderName("X-CSRF-TOKEN"); return repository; } private Filter csrfFilter() { return new OncePerRequestFilter() { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); if (csrfToken != null) { Cookie cookie = new Cookie("XSRF-TOKEN", csrfToken.getToken()); cookie.setPath("/"); response.addCookie(cookie); } filterChain.doFilter(request, response); } }; } } ``` 需要注意,该表单必须包含CSRF令牌。如果没有包含,Spring Security将拒绝退出登录请求。 总结 Spring Security退出登录是一个常见的功能,可以通过URL和表单两种方式来实现。其中,URL退出登录更简单,而表单退出登录需要更多的配置。在实现退出登录时,还需要注意安全性问题,如CSRF保护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值