一.shiro的简介
Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。
Shiro为解决下列问题(我喜欢称它们为应用安全的四要素)提供了保护应用的API:
- 认证 - 用户身份识别,常被称为用户“登录”;
- 授权 - 访问控制;
- 密码加密 - 保护或隐藏数据防止被偷窥;
- 会话管理 - 每用户相关的时间敏感的状态。
从2003年至今,框架选择方面的情况已经改变了不少,但今天仍有令人信服的理由让你选择Shiro。其实理由相当多,Apache Shiro:
- 易于使用 - 易用性是这个项目的最终目标。应用安全有可能会非常让人糊涂,令人沮丧,并被认为是“必要之恶”【译注:比喻应用安全方面的编程。】。若是能让它简化到新手都能很快上手,那它将不再是一种痛苦了。
- 广泛性 - 没有其他安全框架可以达到Apache Shiro宣称的广度,它可以为你的安全需求提供“一站式”服务。
- 灵活性 - Apache Shiro可以工作在任何应用环境中。虽然它工作在Web、EJB和IoC环境中,但它并不依赖这些环境。Shiro既不强加任何规范,也无需过多依赖。
- Web能力 - Apache Shiro对Web应用的支持很神奇,允许你基于应用URL和Web协议(如REST)创建灵活的安全策略,同时还提供了一套控制页面输出的JSP标签库。
- 可插拔 - Shiro干净的API和设计模式使它可以方便地与许多的其他框架和应用进行集成。你将看到Shiro可以与诸如Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin这类第三方框架无缝集成。
核心概念:Subject,SecurityManager和Realms
Subject
Subject一词是一个安全术语,其基本意思是“当前的操作用户”。称之为“用户”并不准确,因为“用户”一词通常跟人相关。在安全领域,术语“Subject”可以是人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。在代码的任何地方,你都能轻易的获得Shiro Subject,
SecurityManager
Subject的“幕后”推手是SecurityManager。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。它是Shiro框架的核心,充当“保护伞”,引用了多个内部嵌套安全组件,它们形成了对象图。但是,一旦SecurityManager及其内部对象图配置好,它就会退居幕后,应用开发人员几乎把他们的所有时间都花在Subject API调用上。
那么,如何设置SecurityManager呢?嗯,这要看应用的环境。例如,Web应用通常会在Web.xml中指定一个Shiro Servlet Filter,这会创建SecurityManager实例,如果你运行的是一个独立应用,你需要用其他配置方式,但有很多配置选项。
一个应用几乎总是只有一个SecurityManager实例。它实际是应用的Singleton(尽管不必是一个静态Singleton)。跟Shiro里的几乎所有组件一样,SecurityManager的缺省实现是POJO,而且可用POJO兼容的任何配置机制进行配置 - 普通的Java代码、Spring XML、YAML、.properties和.ini文件等。基本来讲,能够实例化类和调用JavaBean兼容方法的任何配置形式都可使用。
为此,Shiro借助基于文本的INI配置提供了一个缺省的“公共”解决方案。INI易于阅读、使用简单并且需要极少依赖。你还能看到,只要简单地理解对象导航,INI可被有效地用于配置像SecurityManager那样简单的对象图。注意,Shiro还支持Spring XML配置及其他方式,但这里只我们只讨论INI。
Realms
Shiro的第三个也是最后一个概念是Realm。Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当切实与像用户帐户这类安全相关数据进行交互,执行认证(登录)和授权(访问控制)时,Shiro会从应用配置的Realm中查找很多内容。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现
shiro架构
除前面所讲Subject、SecurityManager 、Realm三个核心组件外,Shiro主要组件还包括:
- Authenticator :认证就是核实用户身份的过程。这个过程的常见例子是大家都熟悉的“用户/密码”组合。多数用户在登录软件系统时,通常提供自己的用户名(当事人)和支持他们的密码(证书)。如果存储在系统中的密码(或密码表示)与用户提供的匹配,他们就被认为通过认证。
- Authorizer :授权实质上就是访问控制 - 控制用户能够访问应用中的哪些内容,比如资源、Web页面等等。
- SessionManager :在安全框架领域,Apache Shiro提供了一些独特的东西:可在任何应用或架构层一致地使用Session API。即,Shiro为任何应用提供了一个会话编程范式 - 从小型后台独立应用到大型集群Web应用。这意味着,那些希望使用会话的应用开发者,不必被迫使用Servlet或EJB容器了。或者,如果正在使用这些容器,开发者现在也可以选择使用在任何层统一一致的会话API,取代Servlet或EJB机制。
- CacheManager :对Shiro的其他组件提供缓存支持。
- Cryptography 提供安全的支持
ini配置权限信息(参考http://shiro.apache.org/configuration.html)
# =======================
# Shiro INI configuration
# =======================
[main]
authc.loginUrl=/login.html
#认证(登录失败)不通过 跳转到loginUrl
roles.loginUrl=/login.html
#授权(没有某个权限) 不通过 跳转到unauthorizedUrl
roles.unauthorizedUrl=/un.html
perms.loginUrl=/login.html
#授权(没有某个权限) 不通过 跳转到unauthorizedUrl
perms.unauthorizedUrl=/un.html
[users]
# 设置用户信息
# 语法是 username = password, roleName1, roleName2, …, roleNameN
jiaozi = 123456,role1
[roles]
# 角色信息和角色拥有的权限
#语法是 rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN
#权限的语法 * 表示所有权限 一般语法是 权限类型.权限动作.权限的资源id 比如 user:delete:1 表示拥有删
#除1号用户的权限 user:delete:*表示删除所有用户权限
admin = *
role1 = user:query:*, user:delete:1
[urls]
# web中的url过滤 访问这个页面时 要求你登录的账号 必须拥有某些权限
/login.html = anon
/suc.jsp = perms[user:delete:2]
创建maven项目 导入架包
<!-- 导入shiro的架包 -->
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
测试验证权限过程
package cn.et;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class TestShiro {
public static void main(String[] args) {
//从ini中读取权限信息构建 SecurityUtils对象
Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:my.ini");
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//获取当前的用户
Subject currentUser = SecurityUtils.getSubject();
//当前用户的会话
Session session = currentUser.getSession();
//判断是否登录 未登录 需要登录
/**
* 用户包括两部分
* principals and credentials
* principals(本人)表示用户的标识信息 比如用户名 用户地址等
* credentials(凭证)表示用户用于登录的凭证 比如密码 证书等
*/
if ( !currentUser.isAuthenticated() ) {
//用户输入的用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("jiaozi", "123456");
try {
currentUser.login( token );
System.out.println("登录成功");
//是否认证通过
System.out.println(currentUser.isAuthenticated());
//判断登录后用户是否有某个角色
if(currentUser.hasRole("role1")){
System.out.println("拥有role1角色");
}
//是否拥有权限
if(currentUser.isPermitted("user:delete:1")){
System.out.println("拥有与删除1的权限");
}
} catch ( UnknownAccountException uae ) {
System.out.println("账号错误");
} catch ( IncorrectCredentialsException ice ) {
System.out.println("密码不匹配");
} catch ( LockedAccountException lae ) {
System.out.println("账号被锁定");