Apache Shiro学习笔记
Apache Shiro是一个功能强大且易于使用的Java安全框架,为开发人员提供了一个直观而全面的解决方案,用于身份验证、授权、加密和会话管理。
Apache Shiro的三大核心:
Subject:即“当前操作用户”。但是,在 Shiro 中,Subject 这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是 Shiro 的“用户”概念。Subject 代表了当前用户的安全操作,所有的Subject都要绑定到SecurityManager上,与Subject的交互实际上是被转换为与SecurityManager的交互。
SecurityManager:它是 Shiro 架构的心脏,用来协调内部各安全组件,Shiro 通过 SecurityManager 来管理内部组件实例,并通过它来提供安全管理的各种服务。当 Shiro 与一个 Subject 进行交互时,实质上是幕后的 SecurityManager 处理所有繁重的 Subject 安全操作。
Realm:本质上是一个特定安全的 DAO。当配置 Shiro 时,必须指定至少一个 Realm 用来进行身份验证或授权。Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的Realm中查找用户及其权限信息。Shiro 内置了可以连接大量安全数据源(又名目录)的 Realm,如 LDAP、关系数据库(JDBC)、类似 INI 的文本配置资源以及属性文件等。如果默认的 Realm 不能满足需求,你还可以插入代表自定义数据源的自己的 Realm 实现。
SecurityManager: 它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理
Authenticator: 认证器,负责 Subject 认证的,即确定用户是否登录成功
Authorizer: 授权器,即权限授权,给 Subject 分配权限,以此控制可访问的资源
SessionManager: 它可以帮助在不同的环境下使用 session 功能,比如非 web 环境下和分布式环境下等
SessionDAO:SessionManager 执行 Session 持久化(CRUD)操作。
CacheManager:对 Shiro 组件提供缓存支持, 缓存控制器,来管理如用户、角色、权限等的缓存的
Realm:shiro 通过 Realms 来获取相应的安全数据,它是数据访问的接口,用来获取鉴权、授权相关的数据(帐号、密码、权限、角色等)
Cryptography:密码模块,用于加密解密, Shiro 的 API 大幅度简化 Java API 中繁琐的密码加密解密。
Permission/Role: 权限是描述功能的一种声明;角色代表权限的集合
Apache Shiro授权
授权:又称访问控制—控制谁有权限在应用程序中做什么。在授权中,需要了解几个关键的对象:Subject 主体、Resource 资源、Permissions 权限、Role 角色:
Subject 主体:访问应用的用户,在 Shiro 中使用 Subject 代表该用户,用户需要授权后才能访问相应的资源
Resource 资源:在应用中用户访问的任何东西,例如:jsp、接口、图片等,都属于是资源
Permissions 权限:Shiro 安全机制最核心的元素。它在应用程序中明确声明了被允许的行为。一个格式良好的权限声明可以清晰表达出用户对该资源拥有的权限。在 Shiro 中主要通过通配符表达式来完成权限的描述
Role 角色:一个命名的实体, 通常代表一组行为或职责。 这些行为演化为在一个应用中能或者不能做的事情。角色通常分配给用户帐户。一个角色拥有一个权限的集合。授权验证时,需要判断当前角色是否拥有指定的权限。这种角色权限可以对该角色进行详细的权限描述。 Shiro官方推荐使用这种方式
授权检查的例子:用户是否能访问某个网页,编辑数据,或打使用这台打印机
授权的三要素:权限、角色和用户 。
需要在应用程序中对用户和权限建立关联:通常的做法是将权限分配给角色,然后将角色分配给一个或多个用户。
执行顺序:
Step 1:应用程序代码调用Subject.login方法,传递传递创建好的包含终端用户的principals(身份)和Credentials(凭证)的AuthenticationToken实例
Step 2:Subject实例,通常是DelegatingSubject(或子类)委托应用程序的Securitymanager通过调用securityManager.login(token)开始真正的验证工作
Step 3:SubjectManager作为一个基本的“保护伞”的组成部分,接收token以及简单地委托给内部的Authenticator实例通过调authentication.authenticate(token).这通常是一个ModularRealmAuthenticator实例,支持在身份验证中协调一个或多个Realm实例。
Step 4:每个配置的Realm用来帮助看它是否支持提交的AuthenticationToken。如果支持,那么支持Realm的getAuthenticationInfo方法将会伴随着提交的token被调用。
Apache Shiro 权限拦截
Shiro和Spring Security一样,都是基于过滤器来实现权限拦截的。
过滤器链1、NameableFilter有一个name属性,定义每一个filter的名字。在FilterChainManager中会调用配置文件中的配置属性名字来为每一个filter命名以及为默认的filter命名,如authc。
2、OncePerRequestFilter保证客户端请求后该filter的doFilter只会执行一次。
3、 AdviceFilter总体是对OncePerRequestFilter中的doFilterInternal进一步细化控制。doFilterInternal会先调用preHandle做一些前置判断,如果返回false则filter链不继续往下执行,postHandle在目标方法(即客户端请求的接口)正常(未抛出异常)执行后完成一些操作,默认不做任何操作。在finally中的cleanup方法中会调用afterCompletion方法,不管目标方法是否出现异常都会继续操作。默认也是空。
4、PathMatchingFilter主要是对preHandle做进一步细化控制,该filter为抽象类,其他路径直接通过:preHandle中,若请求的路径非该filter中配置的拦截路径,则直接返回true进行下一个filter。若包含在此filter路径中,则会在isFilterChainContinued做一些控制,该方法中会调用onPreHandle方法,所以子类可以在onPreHandle中编写filter控制流程代码(返回true或false)。
一般自定义filter可以继承以下Filter:
①OncePerRequestFilter只需实现doFilterInternal方法即可,在这里面实现filter的功能。切记在该方法中最后调用filterChain.doFilter(request, response),允许filter链继续执行下去。可以在这个自定义filter中覆盖isEnable达到控制该filter是否需要被执行(实质是doFilterInternal方法)以达到动态控制的效果,一般不建议直接继承这个类;
②AdviceFilter 中提供三个方法preHandle postHandle afterCompletion:若需要在目标方法执行前后都做一些判断的话应该继承这个类覆盖preHandle 和postHandle 。
③PathMatchingFilter中preHandle实质会判断onPreHandle来决定是否继续往下执行。所以只需覆盖onPreHandle方法即可。
④AccessControlFilter:最常用的,该filter中onPreHandle调用isAccessAllowed和onAccessDenied决定是否继续执行。一般继承该filter,isAccessAllowed决定是否继续执行。onAccessDenied做后续的操作,如重定向到另外一个地址、添加一些信息到request域等等。
Shiro会话管理
Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如Tomcat),不管是J2SE还是J2EE环境都可以使用,提供了会话管理,会话事件监听,会话存储/持久化,容器无关的集群,失效/过期支持,对Web的透明支持,SSO单点登录的支持等特性。
建议在开发中,Controller层使用原生的HttpSession对象,在Service层中使用Shiro提供的Session对象。如果在Service层中使用HttpSession对象,那么属于侵入式,并不建议这么做。Shiro提供的Session能够很好的解决这个问题。
会话管理类图会话相关API:
1、Subject.getSession()。获取会话,等价于Subject.getSession(true),即如果当前没有创建session对象会创建一个;Subject.getSession(false),如果当前没有创建session对象则返回null。
2、Subject.getSession(true)
3、session.getId():获取当前会话的唯一标识。
4、session.getHost(): 获取当前会话的主机地址。
5、session.getTimeout() & session.setTimeout(毫秒),设置/获取当前Session的过期时间。
6、session.getStartTimestamp() & session.getLastAccessTime():获取会话的启动时间及最后访问时间;如果是J2SE环境需要自己定期调用session.touch()去更新最后访问时间;如果是Web环境,每次进入ShiroFilter都会自动调用session.touch()来更新最后访问时间。
7、session.touch() & session.stop(): 更新会话最后访问时间以及销毁会话;Subject.logout()会自动调用session.stop()。在Web应用中,调用HttpSession.invalidate()也会自动调用session.stop()来销毁shiro的会话。
8、session.setAttribute(key,val) & session.getAttribute(key) & session.removeAttribute(key):设置/获取/删除 会话属性。
shiro权限缓存:
一个用户具有的权限一般不会频繁的修改,也就是每次授权的内容都是一样的,对权限信息进行缓存可以提高我们系统的性能。Shiro是通过CacheManager组件实现权限数据缓存。Shiro 自身不实现缓存,而是提供缓存接口,让其他第三方实现,默认支持EhCache和MapCache缓存。
参考文章:https://blog.51cto.com/zero01/2171397?source=dra