【每日進步一點點】SpringSecurity退出登錄不生效問題

SpringSecurity實現了登錄,但配置了退出登錄,始終不成功。

配置情況如下:

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
                .antMatchers("/css/**").permitAll() 
                .anyRequest().authenticated()   //任何未匹配的URL都要進行身份驗證
                .and()
                .formLogin()
                .loginPage("/login.html")
                .failureUrl("/login-error.html").permitAll()
                .defaultSuccessUrl("/home.html")
                .and()
                .logout()
                .logoutUrl("/logout")  //註銷URL
                .logoutSuccessUrl("/login.html");
}

其中配置了logoutUrl爲"/logout",

調用代碼爲:

<a th:href="@{/logout}">退出</a>

問題:點擊後根本無法註銷,瀏覽器報404,即無法找到http://localhost:8080/logout。

 

解決方案一:自定義logout實現

我們自定義了一個logout方法,實現了退出登錄,如下:

    @RequestMapping(value="/logout")
    public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null){
            new SecurityContextLogoutHandler().logout(request, response, auth);
        }
        return "redirect:/login.html";
    }

 評述:該解決辦法還是不錯,能夠達到效果,但跟springsecurity的配置沒有什麼關係了。

例如,調用修改爲:

<a th:href="@{/mylogout}">退出</a>

mylogout實現如下:

    @RequestMapping(value="/mylogout")
    public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null){
            new SecurityContextLogoutHandler().logout(request, response, auth);
        }
        return "redirect:/login.html";
    }

一樣可以成功,就說明和SpringSecurity的配置項沒有關係了。

 

解決方案二:使用SpringSecurity的LogoutFilter

我們都知道,SpringSecurity本身有LogoutFilter這個filter,專門處理退出登錄用的。

我們看一下LogoutFilter.java中doFilter的源代碼

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;

		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);
	}

進行源代碼跟蹤,發現在requiresLogout(request, response) 中未通過,繼續深入match,

@Override
	public boolean matches(HttpServletRequest request) {
		if (this.httpMethod != null && StringUtils.hasText(request.getMethod())
				&& this.httpMethod != valueOf(request.getMethod())) {
			...

			return false;
		}
        ...
    }

 這裏第一個驗證就是調用方法的驗證,跟蹤發現:

/logout要求是POST方法,而我們使用的是<a href>這種方式,這個方式是GET方法,這個就是問題的關鍵所在。

 知道問題所在後,我覺得采用ajax發送post消息來驗證,代碼如下:

<a  onclick="exit();">退出</a>


<script>
        
        function exit() {           
            $.ajax({
                type: "post",
                async: false,
                url: basePath + "/logout",
                success: function (res) {                    
                }
            });
        }
</script>

  嗯,這樣的確可以退出成功,但 .logoutSuccessUrl("/login.html"); 這個配置不生效。

  看了源代碼,主要是redirectStrategy.sendRedirect 不生效。

  百度搜了一下,用ajax調用時,無法更新整個頁面,所以這個是無效的。

  當然還是有辦法處理的。

  在ajax調用成功後,success方法中網頁重新定向到/login.html就可以了。

  代碼如下:  

<a  onclick="exit();">退出</a>


<script>
        
        function exit() {           
            $.ajax({
                type: "post",
                async: false,
                url: basePath + "/logout",
                success: function (res) {
                     window.location.href = "/login.html";
                }
            });
        }
</script>

 以上也實現了整個功能,但有缺憾,登錄成功後的跳轉路徑不生效。

 

解決方案三:正規解決辦法

以上兩個方案都實現了登錄退出的功能,但SpringSecurity的配置都有些不生效,說明這個和SpringSecurity配置的

初衷還是不一樣的,因此思考後,認爲還是應該用form的正規方式來處理,因此正規解決辦法如下:

網頁調用端代碼:

<li class="layui-nav-item">
            <form th:action="@{/logout}" method="post">
                <input id="exit" type="submit" value="退出">
            </form>
</li>

<style type="text/css">
        #exit {
            background-color: #000000;
            color: #ffffff;
            border: 0px;

            margin-left: 15px;
            margin-right: 15px;
        }
</style>

  用submit按鈕來實現,爲了和原有代碼顯示一致,使用了#exit樣式進行配置。

  SpringSecurity的Config配置如下(和前面的沒有變化,這裏只是拿出來統一展示):

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
                .antMatchers("/css/**").permitAll() 
                .anyRequest().authenticated()   //任何未匹配的URL都要進行身份驗證
                .and()
                .formLogin()
                .loginPage("/login.html")
                .failureUrl("/login-error.html").permitAll()
                .defaultSuccessUrl("/home.html")
                .and()
                .logout()
                .logoutUrl("/logout")  //註銷URL
                .logoutSuccessUrl("/login.html");
}

 OK,現在可以只需點擊“退出”這個Submit按鈕,其餘的都按照SpringSecurity的配置進行了。

 登錄退出 /logout

 登錄成功轉向頁面  /login.html

 一切都很完美!

 

 

 

  

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章