Apache Shiro(一)-登录认证和权限管理初识
What is Apache Shiro?
Apache Shiro是一个功能强大、灵活的,开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。
Apache Shiro的首要目标是易于使用和理解。安全通常很复杂,甚至让人感到很痛苦,但是Shiro却不是这样子的。一个好的安全框架应该屏蔽复杂性,向外暴露简单、直观的API,来简化开发人员实现应用程序安全所花费的时间和精力。
Shiro能做什么呢?
- 验证用户身份
- 用户访问权限控制,比如:1、判断用户是否分配了一定的安全角色。2、判断用户是否被授予完成某个操作的权限
- 在非 web 或 EJB 容器的环境下可以任意使用Session API
- 可以响应认证、访问控制,或者 Session 生命周期中发生的事件
- 可将一个或以上用户安全数据源数据组合成一个复合的用户 "view"(视图)
- 支持单点登录(SSO)功能
- 支持提供“Remember Me”服务,获取用户关联信息而无需登录
…
等等——都集成到一个有凝聚力的易于使用的API。
Shiro 致力在所有应用环境下实现上述功能,小到命令行应用程序,大到企业应用中,而且不需要借助第三方框架、容器、应用服务器等。当然 Shiro 的目的是尽量的融入到这样的应用环境中去,但也可以在它们之外的任何环境下开箱即用。
Apache Shiro Features 特性
Apache Shiro是一个全面的、蕴含丰富功能的安全框架。下图为描述Shiro功能的框架图:
Authentication(认证), Authorization(授权), Session Management(会话管理), Cryptography(加密)被 Shiro 框架的开发团队称之为应用安全的四大基石。那么就让我们来看看它们吧:
- Authentication(认证):用户身份识别,通常被称为用户“登录”
- Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
- Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
- Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。
还有其他的功能来支持和加强这些不同应用环境下安全领域的关注点。特别是对以下的功能支持:
- Web支持:Shiro 提供的 web 支持 api ,可以很轻松的保护 web 应用程序的安全。
- 缓存:缓存是 Apache Shiro 保证安全操作快速、高效的重要手段。
- 并发:Apache Shiro 支持多线程应用程序的并发特性。
- 测试:支持单元测试和集成测试,确保代码和预想的一样安全。
- "Run As":这个功能允许用户假设另一个用户的身份(在许可的前提下)。
- "Remember Me":跨 session 记录用户的身份,只有在强制需要时才需要登录。
注意: Shiro不会去维护用户、维护权限,这些需要我们自己去设计/提供,然后通过相应的接口注入给Shiro。
High-Level Overview 高级概述
在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。
- Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它--当前和软件交互的任何事件。
- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
- Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。
我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
快速上手
第一步:
话不多说,我们先构建一个最简单的项目,结构如下:
第二步:
编辑shiro.ini
这里面定义了和安全相关的数据: 用户,角色和权限
注释很详细了,挨个看就能理解了
#定义用户 [users] #用户名 zhang3 密码是 12345, 角色是 admin zhang3 = 12345, admin #用户名 li4 密码是 abcde, 角色是 产品经理 li4 = abcde,productManager #定义角色 [roles] #管理员什么都能做 admin = * #产品经理只能做产品管理 productManager = addProduct,deleteProduct,editProduct,updateProduct,listProduct #订单经理只能做订单管理 orderManager = addOrder,deleteOrder,editOrder,updateOrder,listOrder
第三步:
准备用户类User,用于存放账号密码
public class User { private String name; private String password; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
第四步:
编辑TestShiro
准备3个用户,前两个能在 shiro.ini 中找到,第3个不存在
然后测试登录
接着测试是否包含角色
最后测试是否拥有权限
注:Subject 在 Shiro 这个安全框架下, Subject 就是当前用户
1 public class TestShiro { 2 public static void main(String[] args) { 3 //用户们 4 User zhang3 = new User(); 5 zhang3.setName("zhang3"); 6 zhang3.setPassword("12345"); 7 8 User li4 = new User(); 9 li4.setName("li4"); 10 li4.setPassword("abcde"); 11 12 13 User wang5 = new User(); 14 wang5.setName("wang5"); 15 wang5.setPassword("wrongpassword"); 16 17 List<User> users = new ArrayList<>(); 18 19 users.add(zhang3); 20 users.add(li4); 21 users.add(wang5); 22 //角色们 23 String roleAdmin = "admin"; 24 String roleProductManager ="productManager"; 25 26 List<String> roles = new ArrayList<>(); 27 roles.add(roleAdmin); 28 roles.add(roleProductManager); 29 30 //权限们 31 String permitAddProduct = "addProduct"; 32 String permitAddOrder = "addOrder"; 33 34 List<String> permits = new ArrayList<>(); 35 permits.add(permitAddProduct); 36 permits.add(permitAddOrder); 37 38 39 40 41 42 //登陆每个用户 43 for (User user : users) { 44 if(login(user)) 45 System.out.printf("%s \t成功登陆,用的密码是 %s\t %n",user.getName(),user.getPassword()); 46 else 47 System.out.printf("%s \t成功失败,用的密码是 %s\t %n",user.getName(),user.getPassword()); 48 } 49 50 51 System.out.println("-------how2j 分割线------"); 52 53 //判断能够登录的用户是否拥有某个角色 54 for (User user : users) { 55 for (String role : roles) { 56 if(login(user)) { 57 if(hasRole(user, role)) 58 System.out.printf("%s\t 拥有角色: %s\t%n",user.getName(),role); 59 else 60 System.out.printf("%s\t 不拥有角色: %s\t%n",user.getName(),role); 61 } 62 } 63 } 64 System.out.println("-------how2j 分割线------"); 65 66 //判断能够登录的用户,是否拥有某种权限 67 for (User user : users) { 68 for (String permit : permits) { 69 if(login(user)) { 70 if(isPermitted(user, permit)) 71 System.out.printf("%s\t 拥有权限: %s\t%n",user.getName(),permit); 72 else 73 System.out.printf("%s\t 不拥有权限: %s\t%n",user.getName(),permit); 74 } 75 } 76 } 77 } 78 79 private static boolean hasRole(User user, String role) { 80 Subject subject = getSubject(user); 81 return subject.hasRole(role); 82 } 83 84 private static boolean isPermitted(User user, String permit) { 85 Subject subject = getSubject(user); 86 return subject.isPermitted(permit); 87 } 88 89 private static Subject getSubject(User user) { 90 //加载配置文件,并获取工厂 91 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); 92 //获取安全管理者实例 93 SecurityManager sm = factory.getInstance(); 94 //将安全管理者放入全局对象 95 SecurityUtils.setSecurityManager(sm); 96 //全局对象通过安全管理者生成Subject对象 97 Subject subject = SecurityUtils.getSubject(); 98 99 return subject; 100 } 101 102 103 private static boolean login(User user) { 104 Subject subject= getSubject(user); 105 //如果已经登录过了,退出 106 107 if(subject.isAuthenticated()) { 108 109 subject.logout(); 110 } 111 112 //封装用户的数据 113 UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), user.getPassword()); 114 try { 115 //将用户的数据token 最终传递到Realm中进行对比 116 subject.login(token); 117 } catch (AuthenticationException e) { 118 //验证错误 119 return false; 120 } 121 122 return subject.isAuthenticated(); 123 } 124 125 126 127 128 }
第五步:
运行 TestShiro,可以观察到如图所示的效果
某个用户是否登陆成功
某个用户是否拥有某个角色
某个用户是否拥有某种权限
最后
代码下载地址:https://gitee.com/fengyuduke/my_open_resources/blob/master/shiro.rar
在这里,账号密码,角色信息都是放在配置文件里的,真实工作的时候,肯定都是放在数据库里的。那么怎么做呢?请看下期!!!