Shiro的其他机制
0 Views with
本文字数:2,295 字 | 阅读时长 ≈ 9 min

Shiro的其他机制

0 Views with
本文字数:2,295 字 | 阅读时长 ≈ 9 min

Shiro的拦截器和缓存的实现

Shiro实现拦截器

Shiro使用了与Servlet一样的Filter接口进行扩展;其基础类包括以下:

1、NameableFilter:
NameableFilter给Filter起个名字,如果没有设置默认就是FilterName;

2、OncePerRequestFilter
OncePerRequestFilter用于防止多次执行Filter的;也就是说请求只会走一次拦截器链;另外提供enabled属性,表示是否开启该拦截器实例,默认enabled=true表示开启,如果不想让某个拦截器工作,可以设置为false。

3、ShiroFilter
ShiroFilter是整个Shiro的入口点,用于拦截需要安全控制的请求进行处理;

4、AdviceFilter
AdviceFilter提供了AOP风格的支持,类似于SpringMVC的Interceptor:

5、PathMatchingFilter

PathMatchingFilter提供了基于Ant风格的请求匹配功能及拦截器参数解析的功能,如roles[admin,user]自动根据,分割解析一个请求参数配置并绑定到相应的路径:

6、AccessControlFilter

AccessControlFilter提供了访问控制的基础功能:

拦截器链

Shiro对Servlet容器的FilterChain进行了代理,即ShiroFilter在继续Servlet容器的Filter链的执行之前,通过ProxiedFilterChain对Servlet容器的FilterChain进行了代理;即先走Shiro自己的Filter体系,然后再委托给Servlet容器的FilterChain进行Servlet容器级别的Filter链执行;Shiro的ProxiedFilterChain执行流程:1、先执行Shiro自己的Filter链;2、再执行Servlet容器的Filter链(即原始的Filter)。

而ProxiedFilterChain是通过FilterChainResolver根据配置文件中的[urls]部分是否与请求的URL是否匹配解析得到的。

FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain chain);

即传入原始的Chain,得到一个代理的Chain。

Shiro内部还提供了一个路径匹配的FilterChainResolver实现:PathMatchingFilterChainResolver,其根据[urls]中配置的URL模式和请求的URL是否匹配解析得到的配置的拦截器链的;而PathMatchingFilterChainResolver内部通过FilterChainManager维护着拦截器链。因此我们可以通过FilterChainManager进行动态增加URL模式与拦截器链的关系。

DefaultFilterChainManager会默认添加org.apache.shiro.web.filter.mgt.DefaultFilter中声明的拦截器;

默认拦截器

Shiro提供的默认拦截器有以下几种:

默认拦截器名 说明
身份验证相关的
authc 基于表单的拦截器,如:/=authc 那么未经身份验证的页面不能访问,将会自动跳转到登录页面
authcBasic Basic HTTP身份验证拦截器,主要属性:applicationName,弹出登录框显示的信息(application)
logout 退出拦截器,主要属性:redirectUrl,退出成功后重定向的地址,如:/logout=logout
user 用户登录拦截器,用户已经身份验证、记住我登录的都可;如:/**=user
anon 匿名拦截器,即不需要登录就能访问,一般用于静态资源的过滤;如:/static/**=anon
授权相关的
roles 角色授权拦截器,验证用户是否有拥有所有角色;主要属性:loginUrl,登录页面地址(/login.jsp);unauthorizedUrl,未经授权后重定向的地址;如:/admin/**=roles[admin]
perms 权限授权拦截器,验证用户是否拥有所有权限;属性和roles一样;如:/user/**=perms["user:create"]
rest rest风格的拦截器,会自动根据请求方法构建全权限字符串(GET=read,POST=create,PUT=update…)构建权限字符串:/users=rest[user],会自动拼接处user:read,user:create,user:update…权限字符串进行权限匹配
ssl SSL拦截器,自由请求协议是https,才能通过,否则会自动跳转到https端口(443)
其他
noSessionCreation 不创建会话拦截器

Shiro实现会话管理

会话:即用户访问应用时保持的连接关系。在多次交互中应用能够识别出当前访问的用户是谁,且可以在多次交互中保存一些数据。如访问一些网站时登录成功后,网站可以记住用户,且在退出之前都可以识别当前用户是谁。
如:

Subject subject = SecurityUtils.getSubject();
subject.login(token);
Session session = subject.getSession();

登录成功后使用Subject.getSession()即可获取会话;其等价于Subject.getSession(true),即当前如果没有创建Session对象会自动创建一个;而Subject.getSession(false),若当前没有创建Session,会返回null(不过默认情况下如果启用会话储存功能的话在创建Subject时会主动创建一个Session)。
其常用方法如下:

session.getId(); //获取当前会话的唯一标识

session.getHost(); //获取当前Subject的主机地址,该地址是通过HostAuthenticationToken.getHost()提供的

session.getTimeout();
session.getTimeout(毫秒); //设置当前Session的过期时间;如果不设置默认是会话管理器的全局过期时间

session.getStartTimestamp();
session.getLastAccessTime(); //获取会话的启动时间和最后访问时间;

session.touch();
session.stop(); //更新会话最后访问时间及销毁会话

session.setAttribute("key", "123");
session.getAttribute("key");
session.removeAttribute("key"); // 设置、获取、删除会话属性,在整个会话范围内都可以对这些属性进行操作

会话管理器

会话管理器管理着所有Subject的会话的创建、维护、删除、失效、验证等工作。是shiro的核心组件,顶层组件SecurityManager直接继承了SessionManager。且提供了SessionSecurityManager实现直接把会话管理委托给相应的SessionManager,DefaultSecurityManager及DefaultWebSecurityManager默认SecurityManager都继承了SessionSecurityManager。

Session start(SessionContext context); //启动会话
Session getSession(SessionKey key) throws SessionException; //根据会话Key获取会话

boolean isServletContainerSessions(); //是否使用Servlet容器的会话

void validateSessions(); //验证所有会话是否过期

会话监听器

会话监听器用于监听会话创建、过期及停止事件

public class MySessionListener implements SessionListener {
    @Override
    public void onStart(Session session) {
        System.out.println("会话创建:" + session.getId());
    }

    @Override
    public void onStop(Session session) {
        System.out.println("会话过期:" + session.getId());
    }

    @Override
    public void onExpiration(Session session) {
        System.out.println("会话停止:" + session.getId());
    }
}

ini配置

[main]
authc.loginUrl=/login

sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager=$sessionManager
mySessionListener=com.shiro.listener.MySessionListener
sessionManager.sessionListeners=$mySessionListener
#设置全局会话过期时间:1分钟
sessionManager.globalSessionTimeout=6000

[users]
tycoding=123,admin

[urls]
#格式:url=拦截器[参数],拦截器[参数]
/login=anon

Shiro实现缓存

Shiro提供了类似于Spring的Cache抽象,即Shiro本身不实现Cache,但是又对Chache进行了抽象,方便更换不同的底层Cache实现。

Shiro提供了CachingRealm,其实现了CacheManagerAware接口,提供了缓存的一些基础实现;另外AuthenticatingRealm及AuthorizingRealm分别提供了对AuthenticationInfo和AuthorizationInfo信息的缓存。

测试案例

ini配置

cacheManager=org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile=classpath:shiro-ehcache.xml
securityManager.cacheManager=$cacheManager

Test

@Test
public void testClearCachedAuthenticationInfo() {   
    login(u1.getUsername(), password);
    userService.changePassword(u1.getId(), password + "1");
    RealmSecurityManager securityManager = (RealmSecurityManager) SecurityUtils.getSecurityManager();
    UserRealm userRealm = (UserRealm) securityManager.getRealms().iterator().next();
    userRealm.clearCachedAuthenticationInfo(subject().getPrincipals());
    login(u1.getUsername(), password + "1");
} 

解释: 上面的测试代码中,模拟出了Shiro缓存作用:1、用户登录成功后,先调用service方法改变原数据库中的密码;2、此时调用clearCachedAuthenticationInfo清空之前缓存的AuthenticationInfo;3、使用新密码进行登录。如果没有清空缓存,下次登录的时候获取到的还是原来未修改的密码。
注意: 以上代码仅仅是用来解释Shiro实现的缓存机制的案例,没有完整的代码,详细整合过程请看之后的文章。


交流

如果大家有兴趣,欢迎大家加入我的Java交流群:671017003 ,一起交流学习Java技术。博主目前一直在自学JAVA中,技术有限,如果可以,会尽力给大家提供一些帮助,或是一些学习方法,当然群里的大佬都会积极给新手答疑的。所以,别犹豫,快来加入我们吧!


联系

If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.

如果你觉得这篇文章帮助到了你,你可以帮作者买一杯果汁表示鼓励

Jul 08, 2018