原文地址,转载请注明出处: https://blog.csdn.net/qq_34021712/article/details/84722252 ©王赛超
前面所有的博客登出都是使用的shiro
默认自带的登出,使用方式也很简单,不用我们去实现退出功能,只要去访问一个退出的url
(该 url
是可以不存在),由LogoutFilter
拦截住,清除session
。(如果没有什么特殊需求,我建议直接使用shiro
的登出) 具体如下:
只要拦截到访问/logout
的请求,就会被走logout
对应的 LogoutFilter
,自动登出。
为什么要实现自定义登出?
shiro
的默认登出也会清理用户的session
信息,并且也会清理掉 redis
中缓存的用户 身份认证
和 权限认证
的相关信息,但是为什么还要实现自定义登出呢?
但是有时候,我们还有自己的一些业务需要处理,比如说前面做的 限制用户的登录次数 和 并发登录的人数,使用shiro
默认登出这些key
是不会被删除的,假如我们想要删除这些key
呢? 或者说 在用户登出时,要记录日志 当前用户在线时长,这个时候 我们就需要自定义登出。
登出的两种实现方式
第一种是 不配置 默认的logout,自己写一个Controller方法对外提供http接口,在该Controller方法中实现 登出的逻辑。
/**
* 登出 这个方法没用到,用的是shiro默认的logout
* @param session
* @param model
* @return
*/
@RequestMapping("/logout")
public String logout(HttpSession session, Model model) {
Subject subject = SecurityUtils.getSubject();
subject.logout();
model.addAttribute("msg","安全退出!");
return "login";
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
第二种是 继承LogoutFilter过滤器,并重写preHandle方法。
第一步:自定义实现LogoutFilter
package com.springboot.test.shiro.config.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
-
@author: wangsaichao
-
@date: 2018/11/27
-
@description: 自定义 LogoutFilter
*/
public class ShiroLogoutFilter extends LogoutFilter {/**
-
自定义登出,登出之后,清理当前用户redis部分缓存信息
-
@param request
-
@param response
-
@return
-
@throws Exception
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {//登出操作 清除缓存 subject.logout() 可以自动清理缓存信息, 这些代码是可以省略的 这里只是做个笔记 表示这种方式也可以清除
Subject subject = getSubject(request,response);
DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
ShiroRealm shiroRealm = (ShiroRealm) securityManager.getRealms().iterator().next();
PrincipalCollection principals = subject.getPrincipals();
shiroRealm.clearCache(principals);//登出
subject.logout();//获取登出后重定向到的地址
String redirectUrl = getRedirectUrl(request,response,subject);
//重定向
issueRedirect(request,response,redirectUrl);
return false;
}
-
}
- 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
- 44
- 45
- 46
第二步:ShiroConfig中配置 shiroLogoutFilter
/**
* 配置LogoutFilter
* @return
*/
public ShiroLogoutFilter shiroLogoutFilter(){
ShiroLogoutFilter shiroLogoutFilter = new ShiroLogoutFilter();
//配置登出后重定向的地址,等出后配置跳转到登录接口
shiroLogoutFilter.setRedirectUrl("/login");
return shiroLogoutFilter;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
第三步:将配置的shiroLogout给ShiroFilterFactoryBean
/** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * 注意:初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager * Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截 * @param securityManager * @return */ @Bean(name = "shirFilter") public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ShiroFilterFactoryBean</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//必须设置 SecurityManager,Shiro的核心安全接口</span> shiroFilterFactoryBean<span class="token punctuation">.</span><span class="token function">setSecurityManager</span><span class="token punctuation">(</span>securityManager<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//这里的/login是后台的接口名,非页面,如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面</span> shiroFilterFactoryBean<span class="token punctuation">.</span><span class="token function">setLoginUrl</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//这里的/index是后台的接口名,非页面,登录成功后要跳转的链接</span> shiroFilterFactoryBean<span class="token punctuation">.</span><span class="token function">setSuccessUrl</span><span class="token punctuation">(</span><span class="token string">"/index"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//未授权界面,该配置无效,并不会进行页面跳转</span> shiroFilterFactoryBean<span class="token punctuation">.</span><span class="token function">setUnauthorizedUrl</span><span class="token punctuation">(</span><span class="token string">"/unauthorized"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> LinkedHashMap<span class="token generics function"><span class="token punctuation"><</span>String<span class="token punctuation">,</span> Filter<span class="token punctuation">></span></span> filtersMap <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedHashMap</span><span class="token operator"><</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//限制同一帐号同时在线的个数</span> filtersMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"kickout"</span><span class="token punctuation">,</span> <span class="token function">kickoutSessionControlFilter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//配置自定义登出 覆盖 logout 之前默认的LogoutFilter</span> filtersMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"logout"</span><span class="token punctuation">,</span> <span class="token function">shiroLogoutFilter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> shiroFilterFactoryBean<span class="token punctuation">.</span><span class="token function">setFilters</span><span class="token punctuation">(</span>filtersMap<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 配置访问权限 必须是LinkedHashMap,因为它必须保证有序</span> <span class="token comment">// 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 --> : 这是一个坑,一不小心代码就不好使了</span> LinkedHashMap<span class="token generics function"><span class="token punctuation"><</span>String<span class="token punctuation">,</span> String<span class="token punctuation">></span></span> filterChainDefinitionMap <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedHashMap</span><span class="token operator"><</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//配置不登录可以访问的资源,anon 表示资源都可以匿名访问</span> <span class="token comment">//配置记住我或认证通过可以访问的地址</span> filterChainDefinitionMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"/login"</span><span class="token punctuation">,</span> <span class="token string">"anon"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> filterChainDefinitionMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token string">"anon"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> filterChainDefinitionMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"/css/**"</span><span class="token punctuation">,</span> <span class="token string">"anon"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> filterChainDefinitionMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"/js/**"</span><span class="token punctuation">,</span> <span class="token string">"anon"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> filterChainDefinitionMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"/img/**"</span><span class="token punctuation">,</span> <span class="token string">"anon"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> filterChainDefinitionMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"/druid/**"</span><span class="token punctuation">,</span> <span class="token string">"anon"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//解锁用户专用 测试用的</span> filterChainDefinitionMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"/unlockAccount"</span><span class="token punctuation">,</span><span class="token string">"anon"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> filterChainDefinitionMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"/Captcha.jpg"</span><span class="token punctuation">,</span><span class="token string">"anon"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//logout是shiro提供的过滤器,这是走自定义的 shiroLogoutFilter 上面有配置</span> filterChainDefinitionMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"/logout"</span><span class="token punctuation">,</span> <span class="token string">"logout"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//此时访问/user/delete需要delete权限,在自定义Realm中为用户授权。</span> <span class="token comment">//filterChainDefinitionMap.put("/user/delete", "perms[\"user:delete\"]");</span> <span class="token comment">//其他资源都需要认证 authc 表示需要认证才能进行访问 user表示配置记住我或认证通过可以访问的地址</span> <span class="token comment">//如果开启限制同一账号登录,改为 .put("/**", "kickout,user");</span> filterChainDefinitionMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"/**"</span><span class="token punctuation">,</span> <span class="token string">"kickout,user"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> shiroFilterFactoryBean<span class="token punctuation">.</span><span class="token function">setFilterChainDefinitionMap</span><span class="token punctuation">(</span>filterChainDefinitionMap<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> shiroFilterFactoryBean<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 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
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
上面的代码中 filtersMap.put("logout", shiroLogoutFilter());
表示 logout
走自定义的filter
不再走默认的LogoutFilter
。
可能出现的问题
redis
中的身份认证缓存,无法清除
无论是使用 subject.logout();
还是使用 shiroRealm
中的自定义方法 shiroRealm.clearCache(principals);
都只能清除 权限的缓存信息
却无法清除 身份认证的缓存信息
,如下:
为什么会造成这个问题? 我会在下一章中讲解为什么会出现这个问题。
具体参考下一篇博客:springboot整合shiro-关于登出时,redis中缓存没有清理干净的问题(十七)
</div>