Shiro(Java权限框架)入门
什么是Shiro?
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro。
Shiro组成部分
- subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
- securityManager:安全管理器,主体进行认证和授权都是通过securityManager进行。securityManager是一个集合,真正做事的不是securityManager而是它里面的东西。
- authenticator:认证器,主体进行认证最终通过authenticator进行的。
- authorizer:授权器,主体进行授权最终通过authorizer进行的。
- sessionManager:web应用中一般是用web容器(中间件tomcat)对session进行管理,shiro也提供一套session管理的方式。
- shiro不仅仅可以用于web管理也可以用于cs管理,所以他不用web容器的session管理。
- SessionDao: 通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao(如果用tomcat管理session就不用SessionDao,如果要分布式的统一管理session就要用到SessionDao)。
- cache Manager:缓存管理器,主要对session和授权数据进行缓存(权限管理框架主要就是对认证和授权进行管理,session是在服务器缓存中的),比如将授权数据通过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理(redis是缓存框架)。
- realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据(原来是通过数据库取的)。注意:authenticator认证器和authorizer授权器调用realm中存储授权和认证的数据和逻辑。
- cryptography:密码管理,比如md5加密,提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。比如 md5散列算法(md5只有加密没有解密)。
核心组件
在shiro中核心的概念有三个:
1.subject
即“当前操作用发户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程(比如微信QQ等等)、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
2.Security Manager
它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务,且它管理着所有Subject;可以看出它是Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器。
3.Realm
域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源,同时也可以是配置文件。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro 特点
(1)易于理解的 Java Security API;
(2)简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
(3)对角色的简单的签权(访问控制),支持细粒度的签权;
(4)支持一级缓存,以提升应用程序的性能;
(5)内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境;
(6)异构客户端会话访问;
(7)非常简单的加密 API,自带的加密API完全够用;
(8)不跟任何的框架或者容器捆绑,可以独立运行
实现简单案例
1.添加依赖:
<!-- shiro核心包 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> </dependency> <!-- 添加shiro web支持 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version> </dependency>
2.添加安全数据源shiro-permission.ini文件(官方推荐使用ini文件格式):
[users] zs=123,role1,role2,role3 ls=123,role2 ww=123,role3 admin=123,role1,role2,role3,admin [roles] role1=user:create,user:update,user:delete,user:view,user:load role2=user:create,user:delete role3=user:create admin=user:*
3.添加Demo测试类:
package com.star.demo; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; 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 java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Demo { public static void main(String[] args) { //org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token 密码错误认证失败 //org.apache.shiro.authc.UnknownAccountException: Realm [org.apache.shiro...] was unable to find account data // 账户不存在错误 //1:获取securityFactory工厂类 IniSecurityManagerFactory iniSecurityManagerFactory = new IniSecurityManagerFactory("classpath:shiro" + "-permission" + ".ini"); //2.获取安全管理器对象 SecurityManager instance = iniSecurityManagerFactory.getInstance(); //3.设置subjectManager对象,把subjec对象交给SecurityUtils管理 SecurityUtils.setSecurityManager(instance); //4.获取subject对象 Subject subject = SecurityUtils.getSubject(); //5.生成token UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("admin","123"); //6.进行验证 try { //7.登陆(身份认证) subject.login(usernamePasswordToken); System.out.println("身份认证成功!"); } catch (AuthenticationException e) { e.printStackTrace(); System.out.println("身份认证失败!"); } //8.用户授权 try { // if(subject.hasRole("role6")){//1.普通授权方式,检查单个授权 //2.传集合用户授权,有一个未授权的就返回false // List<String> list= new ArrayList<>(); // list.add("role1"); // list.add("role6"); // list.add("role3"); // if(subject.hasAllRoles(list)){//传集合判断权限 //3.hasRoles方式判断权限 boolean[] booleans = subject.hasRoles(Arrays.asList("role1", "role2", "role3")); boolean f = false; int n = 0; for (int i = booleans.length - 1; i >= 0; i--) { if(booleans[i]) ++n; if(n==booleans.length){ f = true; break; } } if(f){ System.out.println("用户授权成功!"); }else { System.out.println("用户未授权!"); } } catch (Exception e) { e.printStackTrace(); } //9.check方式检查授权,未授权直接报错 try { //1.checkRole方式不存在权限直接报错org.apache.shiro.authz.UnauthorizedException: Subject does not have role [role7] // subject.checkRole("role7"); //2.checkRoles方式不存在直接报错,可以传可变的String参数或者是数组 subject.checkRoles(Arrays.asList("role1","role2","role3")); subject.checkRoles("role1","role2","role3"); System.out.println("用户已授权~"); } catch (Exception e) { e.printStackTrace(); System.out.println("用户未授权!"); } //10.permission方式检查权限 try { // //isPermitted可以接受String可变参数,集合等 // if(subject.isPermitted("user:*")) // System.out.println("用户已授权!"); // else // System.out.println("用户未授权!"); //checkPermitted方式(*代表所有权限不管权限是否存在) subject.checkPermission("user:234234"); System.out.println("用户已授权!"); } catch (Exception e) { e.printStackTrace(); System.out.println("用户未授权!"); } } }