springSecurity 退出登录

Posted by hcy on July 15, 2020

springSecurity 退出登录

​ 这个filter比较简单,它要做的事情就是拦截退出登录的请求,执行退出逻辑,执行退出完成逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		//匹配到是退出请求,默认是/logout
		if (requiresLogout(request, response)) {
			Authentication auth = SecurityContextHolder.getContext().getAuthentication();

			if (logger.isDebugEnabled()) {
				logger.debug("Logging out user '" + auth
						+ "' and transferring to logout destination");
			}
			//执行退出逻辑
			this.handler.logout(request, response, auth);
            //执行退出完成成功,回掉逻辑
			logoutSuccessHandler.onLogoutSuccess(request, response, auth);

			return;
		}

		chain.doFilter(request, response);
	}

修改默认退出路径

​ 下面这样配置可以修改默认退出路径,原理是创建LogoutFilter时,将里面的匹配规则改为我们设置的路径。

1
http.logout().logoutUrl("/my/logout")

​ 值得注意的是,如果开启csrf功能,则退出请求只支持post,否则可以支持四种请求方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
	private RequestMatcher getLogoutRequestMatcher(H http) {
		if (logoutRequestMatcher != null) {
			return logoutRequestMatcher;
		}
		if (http.getConfigurer(CsrfConfigurer.class) != null) {
			this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
		}
		else {
			this.logoutRequestMatcher = new OrRequestMatcher(
				new AntPathRequestMatcher(this.logoutUrl, "GET"),
				new AntPathRequestMatcher(this.logoutUrl, "POST"),
				new AntPathRequestMatcher(this.logoutUrl, "PUT"),
				new AntPathRequestMatcher(this.logoutUrl, "DELETE")
			);
		}
		return this.logoutRequestMatcher;
	}

默认退出逻辑

​ 在LogoutConfigurer类里可以看到,默认会添加两个退出逻辑。

​ 首先删除session内的用户信息,再删除SecurityContextHolder保存的用户信息,这样就算退出完成了。

如果项目里用户信息是保存在数据库里的,可与添加自定义的logoutHandler

1
2
3
4
5
6
7
8
9
10
11
private LogoutFilter createLogoutFilter(H http) {
    	//这是是删除session,删除SecurityContextHolder
		logoutHandlers.add(contextLogoutHandler);
       //这个是发送事件的
		logoutHandlers.add(postProcess(new LogoutSuccessEventPublishingLogoutHandler()));
		LogoutHandler[] handlers = logoutHandlers.toArray(new LogoutHandler[0]);
		LogoutFilter result = new LogoutFilter(getLogoutSuccessHandler(), handlers);
		result.setLogoutRequestMatcher(getLogoutRequestMatcher(http));
		result = postProcess(result);
		return result;
	}

添加自定义logoutHandler

​ 可以看到自己添加的LogoutHandler会添加到logoutHandlers这个List里。

1
2
3
4
5
6
7
8
9
10
11
http.logout().addLogoutHandler(new LogoutHandler() {
    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        System.out.println(authentication.getPrincipal() + "退出了");
    }
});

public LogoutConfigurer<H> addLogoutHandler(LogoutHandler logoutHandler) {
    this.logoutHandlers.add(logoutHandler);
    return this;
}

退出时自动删除Cookie

​ 下面这样配置,可以在退出时自动删除指定的Cookie

1
http.logout().deleteCookies("sessionId","usertoken");

​ 原理就是添加了CookieClearingLogoutHandler,在退出时向response添加同名cookie,将客户端的覆盖掉。

1
2
3
public LogoutConfigurer<H> deleteCookies(String... cookieNamesToClear) {
    return addLogoutHandler(new CookieClearingLogoutHandler(cookieNamesToClear));
}

退出成功执行逻辑

​ 默认会重定向到/login?logout页面上,可以自定义重定向地址

1
2
//自定义退出成功Url,会重定向到此url
http.logout().logoutSuccessUrl("/my/login");

​ 重定向的原理就是用我们指定的路径创建一个SimpleUrlLogoutSuccessHandler。这个SuccessHandler调用时会做重定向操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private LogoutSuccessHandler getLogoutSuccessHandler() {
    LogoutSuccessHandler handler = this.logoutSuccessHandler;
    if (handler == null) {
        handler = createDefaultSuccessHandler();
    }
    return handler;
}

private LogoutSuccessHandler createDefaultSuccessHandler() {
    SimpleUrlLogoutSuccessHandler urlLogoutHandler = new SimpleUrlLogoutSuccessHandler();
    urlLogoutHandler.setDefaultTargetUrl(logoutSuccessUrl);
    if (defaultLogoutSuccessHandlerMappings.isEmpty()) {
        return urlLogoutHandler;
    }
    DelegatingLogoutSuccessHandler successHandler = new DelegatingLogoutSuccessHandler(defaultLogoutSuccessHandlerMappings);
    successHandler.setDefaultLogoutSuccessHandler(urlLogoutHandler);
    return successHandler;
}

自定义 logoutSuccessHandler

​ 有时候退出后不想重定向,而是数据给前台(前后端分离的项目应该都是这样)。可以自定义退出成功执行逻辑,可以覆盖上面的重定向操作。

​ 下面这样就是退出成功后,输出字符串到前台。

1
2
3
4
5
6
http.logout().logoutSuccessHandler(new LogoutSuccessHandler() {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.getWriter().write("logou success");
    }
});

多退出路径

退出后可以对页面进行重定向,也可以返回Json数据,有时候想要两者共存怎么办呢?

1
2
如访问 /api/logout,则返回json
访问 /page/logout,执行重定向到首页	

也是可以做到的。

​ 首先自定义退出匹配规则,这样就能匹配多个退出路径了

1
2
3
4
5
6
        //自定义退出匹配规则
        http.logout().logoutRequestMatcher(new OrRequestMatcher(
           new AntPathRequestMatcher("/api/logout"),
           new AntPathRequestMatcher("/page/logout")
        ));

​ 为其中一个退出路径指定logoutSuccessHandler

1
2
3
4
5
6
7
8
//指定匹配/api/logout的退出路径,退出成功后执行逻辑
http.logout().defaultLogoutSuccessHandlerFor(new LogoutSuccessHandler() {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.getWriter().write("log success");
    }
},new AntPathRequestMatcher("/api/logout"));
        

​ 剩下的那个/page/logout没有指定处理逻辑,则会走默认的重定向到logoutSuccessUrl

这样就实现了拥有两个退出方式,且返回方式不同。

所有配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
        //1. 添加自定义退出路径
        http.logout().logoutUrl("/my/logout");
        //2. 添加自定义退出操作,如在这里修改数据库用户状态
        http.logout().addLogoutHandler(new LogoutHandler() {
            @Override
            public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
                System.out.println(authentication.getPrincipal() + "退出了");
            }
        });
        //3. 删除指定cookie
        http.logout().deleteCookies("sessionId", "usertoken");
        //4. 自定义退出成功重定向到Url
        http.logout().logoutSuccessUrl("/my/login");

        //5. 自定义退出成功Handler,此功能会覆盖4中的重定向操作
        http.logout().logoutSuccessHandler(new LogoutSuccessHandler() {
            @Override
            public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                response.getWriter().write("logou success");
            }
        });

        //6.自定义退出匹配规则,可以存在多个。会覆盖1中的自定义路径
        http.logout().logoutRequestMatcher(new OrRequestMatcher(
                new AntPathRequestMatcher("/api/logout"),
                new AntPathRequestMatcher("/page/logout")
        ));
        //7. 指定匹配/api/logout的退出路径,退出成功后执行逻辑
			//满足匹配的走这里面的逻辑,不满足的走默认逻辑,默认逻辑是 4或者5
        http.logout().defaultLogoutSuccessHandlerFor(new LogoutSuccessHandler() {
            @Override
            public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                response.getWriter().write("log success");
            }
        }, new AntPathRequestMatcher("/api/logout"));

        //8.是否删除session,删除ContentSecurityHolder内的认证
        http.logout().invalidateHttpSession(true);
        http.logout().clearAuthentication(true);
        //9.允许退出请求访问
        http.logout().permitAll();
        

总结

SpringSecurity的退出操作很简单,也很灵活。


转载请注明出处:https://www.huangchaoyu.com/2020/07/15/springSecurity-退出登录/