一、Shiro简介
1、权限和认证
在权限和认证的范畴中有两个概念:
1️⃣安全实体:被权限系统保护的对象,比如工资数据等
2️⃣权限:需要被校验的行为,比如查看、修改等,也就是某个用户对于安全实体是否具有查看和修改等权力
有了安全实体和权限,就需要将某些安全实体的某些权限分配给某些用户,这是向数据库里面添加数据、或是维护数据的过程,也叫做权限分配。
在完成了权限分配之后,就需要判断某个用户或某个程序对某个安全实体是否拥有某个或某些权限,这其实是从数据库中获取相应数据进行权限匹配的过程,即权限认证。
安全实体的权限具有继承性,也就是说如果多个安全实体存在包含关系,而某个安全实体没有权限限制,则它会继承包含它的安全实体的相应权限。
权限还具有最近匹配原则,如果多个安全实体存在包含关系,而某个安全实体没有权限限制,那么它会向上寻找并匹配相应权限限制,直到找到一个离这个安全实体最近的拥有相应权限限制的安全实体为止。如果把整个层次结构都寻找完了都没有匹配到相应权限限制的话,那就说明所有人对这个安全实体都没有相应的权限限制。
2、Shiro简介
Apache Shiro 是一个强大易用的 Java 安全框架,提供了认证、授权、加密和会话管理等功能:
1️⃣认证:验证用户的身份
2️⃣授权:对用户进行访问控制,判断用户是否被允许做某事
3️⃣会话管理:可在任何环境下使用 Session API,即使没有 Web 或EJB 容器
4️⃣加密:以更简洁易用的方式使用加密功能,保护或隐藏数据防止被偷窥
Apache Shiro 还具有其他的一些功能:
1️⃣Realms:聚集一个或多个用户安全数据的数据源
2️⃣单点登录(SSO)功能
3️⃣为没有关联到登录的用户启用 "Remember Me“ 服务
3、Shiro的主要功能
Shiro 的四大核心功能:
1️⃣Authentication(身份验证):简称为“登录”,即证明用户是谁
2️⃣Authorization(授权):访问控制的过程,决定用户是否有权限访问受保护的资源
3️⃣Session Management(会话管理):管理用户特定的会话,即使在非 Web 或 EJB 应用程序
4️⃣Cryptography(加密):通过使用加密算法保证数据安全
Shiro 还提供以下扩展功能:
1️⃣Web Support:主要针对web应用提供一些常用功能
2️⃣Caching:缓存可以使应用程序运行更有效率
3️⃣Concurrency:多线程相关功能
4️⃣Testing:帮助我们进行测试相关功能
5️⃣"Run As":一个允许用户假设为另一个用户身份(如果允许)的功能,有时候在管理脚本时很有用
6️⃣" Remember Me " :记住用户身份,用于类似购物车功能
4、Shiro架构3个核心组件
1️⃣Subject :正与系统进行交互的人,或某一个第三方服务。所有 Subject 实例都被绑定到(且这是必须的)一个 SecurityManager 上
2️⃣SecurityManager:Shiro 架构的心脏,用来协调内部各安全组件,管理内部组件实例,并通过它来提供安全管理的各种服务。当 Shiro 与一个 Subject 进行交互时,实质上是幕后的 SecurityManager 处理所有繁重的 Subject 安全操作
3️⃣Realms :本质上是一个特定安全的 DAO。当配置 Shiro 时,必须指定至少一个 Realm 用来进行身份验证和/或授权。Shiro 提供了多种可用的 Realms 来获取安全相关的数据。如关系数据库(JDBC)、INI 及属性文件等。可以定义自己的 Realm 实现来自定义安全数据的数据源
5、Shiro架构
①Authenticator :在用户登录时对用户的身份进行验证的组件。Authenticator 从一个或多个 Realm 中获得数据以验证用户的身份,若存在多个realm,则接口 AuthenticationStrategy 会确定怎么样算是验证成功(例如,如果一个 Realm 成功,而其他的均失败,是否认为登录成功)
②Authorizer :验证用户能否访问应用中的受保护的资源
③SessionManager :可在任何应用或架构层一致地使用 Session API
④SessionDAO:用以支持SessionManager 执行 Session 持久化(CRUD)操作
⑤CacheManager :对 Shiro 组件提供缓存支持
⑥Cryptography:Shiro 的 API 大幅度简化了 Java API 中繁琐的密码加密
⑦Realms:Shiro 通过 Realms 来获取相应的安全数据
参考:官方文档
二、基础Demo搭建
参考官网:Shiro简单向导
1、引入依赖
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>runtime</scope>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<scope>runtime</scope>
</dependency>
</dependencies>
2、在根目录下添加两个配置文件log4j.properties和shiro.ini
log4j.properties:
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
# General Apache libraries
log4j.logger.org.apache=WARN
# Spring
log4j.logger.org.springframework=WARN
# Default Shiro logging
log4j.logger.org.apache.shiro=TRACE
# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
shiro.ini:配置用户、角色、权限等信息
[users]
# user 'root' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
darkhelmet = ludicrousspeed, darklord, schwartz
# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5
3、编写测试类:
public class MainTest {
private static final transient Logger log = LoggerFactory.getLogger(MainTest.class);
@SuppressWarnings("deprecation")
public static void main(String[] args) {
//根据shiro.ini配置文件中配置的用户、角色等创建SecurityManagerFactory
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//获取SecurityManager
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//获取当前用户
Subject currentUser = SecurityUtils.getSubject();
//获取当前用户的Session
Session session = currentUser.getSession();
//向当前用户的session中设置相关变量
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
//校验当前用户是否通过校验
if (!currentUser.isAuthenticated()) {
//相当于认证操作
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);
try {
//认证通过则尝试登录
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. "
+ "Please contact your administrator to unlock it.");
}catch (AuthenticationException ae) {
// unexpected condition? error?
}
}
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//判断当前登录者是否有某个角色
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//判断当前登录者是否有某个权限
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//更细粒度的权限校验
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. "
+ "Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
// 登出:登出后用户的session会被清理
currentUser.logout();
System.exit(0);
}
}
三、Shiro的web示例
参考官网:Shiro的Web搭建向导