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
一切都很完美!