一、什么是Shiro
Shiro是java的一个安全框架,可以非常容易的开发出足够好的应用。Shiro可以帮助我们完成:认证、授权、加密、会话管理、与web集成、缓存等功能。
二、使用Shiro的好处
1、Shiro是将安全认证相关的功能抽取出来组成一个框架,使用Shiro就可以快速完成认证、授权等功能的开发,降低系统成本。
2、Shiro使用广泛,可以运行在web应用、非web应用、集群分布式应用中。
(在java领域中,由Spring提供的Spring security也是一个类似的权限管理框架。但是和Shiro相比,Shiro就相对更加独立,使用更加简单、灵活。)
三、Shiro的主要功能
1、Authentication
身份认证/登录,验证用户是不是拥有相应的身份。
2、Authorization
授权,即权限验证。验证已认证的某个用户是否拥有某个权限。
3、Session Manager
会话管理,即用户登陆后就是一次会话,在没有退出之前,他的所有信息都在会话之中。会话可以是普通javaSE环境的,也可以是web环境的。
4、Cryptography
加密,保护数据的安全性。例如对密码进行加密存储到数据库,而不是明文存储。
5、Web Support
Web支持,可以非常容易的集成到web环境。
6、Caching
缓存,比如用户登录之后,其用户信息,拥有的角色权限不必每次起查,提高效率。
7、Concurrency
Shiro支持多线程应用的并发验证,即如果在一个线程中开启另外一个线程,能把权限信息自动传输过去。
8、Testing
提供测试支持
9、Run As
允许一个用户假装为另外一个用户(如果另外一个用户允许)的身份进行访问。
10、Remember Me
记住我
(Shiro不会为我们去维护用户,维护权限,这些部分需要我们自己去设计提供。)
四、第一个Shiro程序
(在此,入门程序采用非web环境进行演示,并且不连接数据库,通过shrio.ini文件来配置相关信息完成用户的认证。)
1、创建maven工程,导入Shiro相关core坐标依赖。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
2、在resources目录下创建shiro.ini文件,用于充当数据库保存相关的配置信息
#配置用户
[users]
zhangsan=123456
lisi=123456
3、创建测试类用于进行用户认证测试
package com.xsh.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
/**
* 该类为测试类,用于读取shiro.ini文件的用户信息并进行认证
*/
public class TestAuthenticationApp {
public static void main(String[] args) {
//自定义用户名密码用于测试
String username="zhangsan";
String password="1234561";
//1、创建安全管理器的工厂对象
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
//2、使用工厂对象创建安全管理器
SecurityManager securityManager=factory.getInstance();
//3、将获取到的安全管理器绑定到当前的线程
SecurityUtils.setSecurityManager(securityManager);
//4、使用SecurityUtils从安全管理器中获取主体对象
Subject subject=SecurityUtils.getSubject();
//5、将用户名密码封装到主体对象中
AuthenticationToken token=new UsernamePasswordToken(username,password);
//6、进行认证
try{
subject.login(token);
System.out.println("认证通过");
}catch (AuthenticationException e){
//在进行认证的过程中,如果出现认证失败,则会抛出异常,Shiro中定义了多种异常来分别反应
//不同的情况,例如用户名不存在,密码错误,密码失效等等,AuthenticationException是这些异常的父异常
System.out.println("用户名或密码不正确");
}
}
}
补充:入门案例中出现可能出现错误的解决方案
NoClassDefFoundError: org/apache/commons/logging/LogFactory
解决方案:导入commons-logging.jar包
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
4、修改入门案例添加用户的角色和权限验证
(在此之前,需要明确角色和权限的概念。权限代表该登录用户操作某些数据的权限,例如是否对user表拥有查询权限。角色为权限的集合,一般一个角色拥有多个权限,然后某些用户则会拥有多个角色。)
①修改shiro.ini文件,添加相关的角色和角色对应的权限,并且给用户赋予角色
#配置用户
[users]
zhangsan=123456,role1
lisi=123456,role2
wangwu=123456,role2,role3
#声明角色
[roles]
role1=user:query,user:add,user:update,user:delete
role2=user:query
role3=user:delete
role4=*:*
(上述配置中,user:query表示对user的查询权限,role4的权限是统配写法,表示拥有全部权限。)
②在测试类中,通过Shiro的方法对登录用户的角色进行检验
(除了对角色和权限的检验之外,还会补充其他与认证相关的方法)
package com.xsh.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
/**
* 该类为测试类,用于读取shiro.ini文件的用户信息并进行认证
*/
public class TestAuthenticationApp {
public static void main(String[] args) {
//自定义用户名密码用于测试
String username="zhangsan";
String password="123456";
//1、创建安全管理器的工厂对象
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
//2、使用工厂对象创建安全管理器
SecurityManager securityManager=factory.getInstance();
//3、将获取到的安全管理器绑定到当前的线程
SecurityUtils.setSecurityManager(securityManager);
//4、使用SecurityUtils从安全管理器中获取主体对象
Subject subject=SecurityUtils.getSubject();
//5、将用户名密码封装到主体对象中
AuthenticationToken token=new UsernamePasswordToken(username,password);
//6、进行认证
try{
subject.login(token);
System.out.println("认证通过");
}catch (AuthenticationException e){
//在进行认证的过程中,如果出现认证失败,则会抛出异常,Shiro中定义了多种异常来分别反应
//不同的情况,例如用户名不存在,密码错误,密码失效等等,AuthenticationException是这些异常的父异常
System.out.println("用户名或密码不正确");
}
//角色判断,角色和权限判断必须在认证成功之后进行
//hasRole();方法用于判断当前登录用户是否具有某个角色
boolean role1 = subject.hasRole("role1");
System.out.println("当前用户是否拥有role1角色:"+role1);
//hasRoles();方法的参数需要传递一个角色名称的集合,返回一个布尔类型的数组,
// Shiro会循环判断当前用户是否拥有该角色集合中的所有角色
List<String> rolenames= Arrays.asList("role1","role2","role3");
boolean[] booleans = subject.hasRoles(rolenames);
for (boolean b:booleans) {
System.out.println(b);
}
//hasAllRoles();方法的参数也是一个角色名称的集合,不过匹配方式是如果当前用户有一个角色对应不上则返回false
boolean b = subject.hasAllRoles(rolenames);
System.out.println(b);
//权限判断
//isPermitted();方法用于判断当前登录用户是否具有某个具体的权限
//同时,该方法也支持传递可变参数,此时用法效果与hasRoles();方法类似
boolean permitted = subject.isPermitted("user:query");
System.out.println(permitted);
boolean[] permitted1 = subject.isPermitted("user.query", "user.add");
for (boolean boo:permitted1) {
System.out.println(b);
}
//isPermittedAll();方法参数传递多个权限名称,使用效果与hasAllRoles方法类似
//该方法支持传递可变参数,也支持传递集合
boolean permittedAll = subject.isPermittedAll("user.query", "user.add");
System.out.println(permittedAll);
//补充方法
//判断用户是否认证通过
boolean authenticated = subject.isAuthenticated();
//用户退出,当该方法用在权限和角色验证之前时,则权限和角色验证无法成功,因为登录用户已经被登出
subject.logout();
}
}