目录
1.介绍
1)基本功能
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web支持,可以非常容易的集成到Web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
2)架构
可以看到:应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject;其每个API的含义:
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;
Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;
doGetAuthenticationInfo()方法:用来验证当前登录的用户,获取认证信息
doGetAuthorizationInfo()方法:用来为当前登陆成功的用户授予权限和角色(已经登陆成功了)
SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;这样的话,比如我们在Web环境用,刚开始是一台Web服务器;接着又上了台EJB服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到Memcached服务器);
SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。
3)优缺点
1、简单易懂,开源框架。
2.可以实现, 认证、授权、加密、会话管理、与Web集成、缓存
简单性,Shiro 在使用上较 Spring Security 更简单,更容易理解。
灵活性,Shiro 可运行在 Web、EJB、IoC、Google App Engine 等任何应用环境,却不依赖这些环境。而 Spring Security 只能与 Spring 一起集成使用。
Shiro 是一个强大而灵活的开源安全框架,能够非常清晰的处理认证、授权、管理会话以及密码加密。如下是它所具有的特点:
· 易于理解的 Java Security API;
· 简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
· 对角色的简单的签权(访问控制),支持细粒度的签权;
· 支持一级缓存,以提升应用程序的性能;
· 内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境;
· 异构客户端会话访问;
· 非常简单的加密 API;
· 不跟任何的框架或者容器捆绑,可以独立运行。
2.配置
1)web.xml
2)spring-context-shiro.xml
web.xml中的filter-name与spring-context-shiro.xml中的bean 的id要相同。和spring-security一样,DelegatingFilterProxy是一个代理过滤器,目标过滤器是shiroFilter。而shiroFilter在配置项中是一个ShiroFilterFactoryBean工厂。其实在创建实例的时候ShiroFilterFactoryBean会创建一个SpringShiroFilter类,所以最终代理的是SpringShiroFilter。shiro与web和spring的集成写在pom.xml里
3.标签和注解
1)标签
标签库的导入:<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
标签库定义在shiro-web.jar包下的META-INF/shiro.tld中定义。
1、<!-- guest :用户没有验证时显示相应信息 ,如登录等相关信息-->
<shiro:guest>
<a href="login.jsp">登录</a><br><br>
</shiro:guest>
2、<!-- user: 用户已经经过认证/记住我登录后 显示相应的信息 -->
<shiro:user>
<a href="logout">登出</a><br><br>
</shiro:user>
3、<!-- authenticated 用户已经经过身份验证,但不是记住我登录的 -->
<shiro:authenticated>
<shiro:principal />已经经过身份验证<br><br>
</shiro:authenticated>
4、<!-- 用户没有进行身份验证,记住我自动登录的属于没有进行身份验证 -->
<shiro:notAuthenticated>
用户没有进行身份验证,记住我自动登录的属于没有进行身份验证<br><br>
</shiro:notAuthenticated>
5、principal 标签 :输出当前用户信息,通常为登录帐号信息。
Hello, <shiro:principal/>, how are you today?
6、<!-- 当前用户是否拥有该角色,有就显示相关信息 -->
<shiro:hasRole name="admin">
<a href="admin.jsp">Admin Page</a><br><br>
</shiro:hasRole>
7、<!-- 当前用户没有相应的角色,将显示body中的信息 -->
<shiro:lacksRole name="manager">
<shiro:principal></shiro:principal>没有manager角色<br><br>
</shiro:lacksRole>
8、<!-- 当前用户有任意一个角色将会显示body体中的内容 -->
<shiro:hasAnyRoles name="admin,user,manager">
<shiro:principal></shiro:principal>拥有admin/user/manager中的角色<br><br>
</shiro:hasAnyRoles>
9、<!-- 当前用户有相应的权限,将显示body体中的信息 -->
<shiro:hasPermission name="customer:delete">
<shiro:principal />拥有customer:delete权限<br><br>
</shiro:hasPermission>
10、<!-- 当前用户没有相应的权限,将显示body体中的信息 -->
<shiro:lacksPermission name="customer:delete">
没有权限customer:delete<br><br>
</shiro:lacksPermission>
标签可以按照已有标签进行自定义添加!
2)注解
Shiro注解支持AspectJ、Spring、Google-Guice等,可根据应用进行不同的配置。
1、@ RequiresAuthentication
可以用户类/属性/方法,用于表明当前用户需是经过认证的用户。
@RequiresAuthentication
public void updateAccount(Account userAccount) {
//this method will only be invoked by a
//Subject that is guaranteed authenticated
...
}
使用这个注解之前,需要先在spring-mvc.xml加入一段代码(一定要写在最先加载的xml中,写在后面加载的xml中也不起作用),lifecycleBeanPostProcessor和securityManager是在shiro配置文件中定义好的。
2、@ RequiresGuest
表明该用户需为”guest”用户
3、@ RequiresPermissions
当前用户需拥有制定权限
4、@RequiresRoles
当前用户需拥有制定角色
@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND) 表示需要admin和user
5、@ RequiresUser
当前用户需为已认证用户或已记住用户
4.会话和缓存
1)会话
shiro提供了一个完整的企业级会话管理解决方案,不再依赖web容器。可以在web和非web环境下使用。
shiro的session特性:
基于POJO/J2SE:shiro中session相关的类都是基于接口实现的简单的java对象(POJO),兼容所有java对象的配置方式,扩展也更方便,完全可以定制自己的会话管理功能 。
简单灵活的会话存储/持久化:因为shiro中的session对象是基于简单的java对象的,所以你可以将session存储在任何地方,例如,文件,各种数据库,内存中等。
容器无关的集群功能:shiro中的session可以很容易的集成第三方的缓存产品完成集群的功能。例如,Ehcache + Terracotta, Coherence, GigaSpaces等。你可以很容易的实现会话集群而无需关注底层的容器实现。
异构客户端的访问:可以实现web中的session和非web项目中的session共享。
会话事件监听:提供对对session整个生命周期的监听。
保存主机地址:在会话开始session会存用户的ip地址和主机名,以此可以判断用户的位置。
会话失效/过期的支持:用户长时间处于不活跃状态可以使会话过期,调用touch()方法,可以主动更新最后访问时间,让会话处于活跃状态。
透明的Web支持:shiro全面支持Servlet2.5中的session规范。这意味着你可以将你现有的web程序改为shiro会话,而无需修改代码。
单点登录的支持:shiro session基于普通java对象,使得它更容易存储和共享,可以实现跨应用程序共享。可以根据共享的会话,来保证认证状态到另一个程序。从而实现单点登录。
public interface Session {
//类似于HttpSession, 定义了一些标准的session属性与操作,
//包括Session唯一标识、会话超时时间、会话开始时间、最后访问时间、主机IP、停止会话、通过attribute绑定、删除、数据等
}
//Session管理器,负责创建、持久化和删除会话实例及其生命周期。
public interface SessionManager{
Session start(SessionContext context); //创建一个新的会话
//根据SessionKey获取会话,如果没有找到会话,返回null.
//如果找到一个无效会话(过期或停止),抛出SessionException
Session getSession(SessionKey key) throws SessionException;
}
public interface SessionDAO {
//将一个新的Session记录保存到EIS(关系型数据库、文件系统、持久缓存,等等)
Serializable create(Session session);
//从EIS中检索Session
Session readSession(Serializable sessionId) throws UnknownSessionException;
//更新Session信息
void update(Session session) throws UnknownSessionException;
//删除Session,如果EIS中没有该Session,什么都不做
void delete(Session session);
//返回全部存活的Session
Collection<Session> getActiveSessions();
}
shiro通过这三个接口实现会话管理。
Shiro中一切的操作都是通过SecurityManager来实现的。实际上SecurityManager继承了 SessionManager,实际操作也是通过SessionsSecurityManager来进行的。查看SessionsSecurityManager代码,可以看到SecurityManager是委托给一个可用的SessionManager来执行,默认使用DefaultSessionManager。实际上Shiro只为SessionManager提供了 DefaultSessionManager 一个可用的实现类。
2)缓存
每一次给用户授权时,都是通过realm从数据库中查询权限,为了避免频繁的查询数据库,shiro给我们提供了缓存的能力。
用户认证通过后:
第一次授权:通过realm从数据库中查询
第二次授权:直接从缓存中查询
引入包
1. shiro-ehcache.jar
2. ehcache-core.jar
jeesite中,cache和session结合到一起:
5.授权和认证
1)授权
授权有三个核心元素:权限,角色,用户。
1、权限
权限的粗细粒度:粗粒度权限管理,对资源类型的权限管理。资源类型比如:菜单、url连接、用户添加页面、用户信息、类方法、页面中按钮。
粗粒度权限管理比如:超级管理员可以访问户添加页面、用户信息等全部页面。
部门管理员可以访问用户信息页面包括 页面中所有按钮。
细粒度权限管理,对资源实例的权限管理。资源实例就资源类型的具体化。细粒度权限管理就是数据级别的权限管理。
Shiro权限声明通常是使用以冒号分隔的表达式。就像前文所讲,一个权限表达式可以清晰的指定资源类型,允许的操作,可访问的数据。同时,Shiro权限表达式支持简单的通配符,可以更加灵活的进行权限设置。
下面以实例来说明权限表达式。
可查询用户数据
User:view
可查询或编辑用户数据
User:view,edit
可对用户数据进行所有操作
User:* 或 user
可编辑id为123的用户数据
User:edit:123
2、角色
Shiro支持两种角色模式:
1、传统角色:一个角色代表着一系列的操作,当需要对某一操作进行授权验证时,只需判断是否是该角色即可。这种角色权限相对简单、模糊,不利于扩展。
2、权限角色:一个角色拥有一个权限的集合。授权验证时,需要判断当前角色是否拥有该权限。这种角色权限可以对该角色进行详细的权限描述,适合更复杂的权限设计。
下面将详细描述对两种角色模式的授权实现。oa:oaNotify:view,oa:oaNotify:edit
3、授权的实现
Shiro支持三种方式实现授权过程:
编码实现
注解实现
JSP Taglig实现
1、基于传统角色授权实现
验证用户是否拥有某个角色: SecurityUtils.getSubject();
相关验证方法如下:
Subject方法 描述
hasRole(String roleName) 当用户拥有指定角色时,返回true
hasRoles(List<String> roleNames) 按照列表顺序返回相应的一个boolean值数组
hasAllRoles(Collection<String> roleNames) 如果用户拥有所有指定角色时,返回true
2、基于权限对象的实现:SecurityUtils.getSubject();
相关方法如下:
Subject方法 描述
isPermitted(Permission p) Subject拥有制定权限时,返回treu
isPermitted(List<Permission> perms) 返回对应权限的boolean数组
isPermittedAll(Collection<Permission> perms) Subject拥有所有制定权限时,返回true
2)认证
1、创建token令牌,token中有用户提交的认证信息即账号和密码
2、执行subject.login(token),最终由securityManager通过Authenticator进行认证
3、Authenticator的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码,这里使用的是IniRealm(shiro自带)
4、IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。