认证与授权 — Shiro(一)
文章目录
认证与授权一般主流的框架就是
Shiro
和SpringSecurity
,Shiro
相对来说较于简单, 而且我们注意到一个神奇的东东, Spring 竟然也在用Shiro
真是有图有真相啊…
而且帅帅最近和朋友一起开发的项目也使用了
Shiro
, 因此做一些Shiro
的分享.
1. Shiro 简介
- Apache Shiro 是 Java 的一个安全(权限)框架
- Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境
- Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存 等
2. 功能简介
Apache Shiro是具有许多功能的全面的应用程序安全框架。下图显示了Shiro的核心功能:
Shiro以Shiro开发团队所谓的“应用程序安全性的四个基石”为目标-身份验证,授权,会话管理和加密:
- Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
- Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用 户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户 对某个资源是否具有某个权限;
- Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有 信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
在不同的应用程序环境中,还具有其他功能来支持和加强这些问题,主要有:
- Web Support:Web 支持,可以非常容易的集成到Web 环境;
- Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可 以提高效率;
- Concurrency:Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能
- 把权限自动传播过去;
- Testing:提供测试支持;
- Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
- Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登 录了
3. Shiro 架构
3.1 从 Shiro 外部来看
Subject
:应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject。Subject 代表了当前“用户”, 这个用户不一定 是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫, 机器人等;与 Subject 的所有交互都会委托给 SecurityManager; Subject 其实是一个门面,SecurityManager 才是实际的执行者;SecurityManager
:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且其管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与 Shiro 的其他组件进行交互,它相当于 SpringMVC 中 DispatcherServlet 的角色Realm
:Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色/ 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource
3.2 从 Shiro 内部来看
Subject
: 任何可以与应用交互的“用户”;SecurityManager
: 相当于SpringMVC 中的 DispatcherServlet;是 Shiro 的核心; 所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进 行认证、授权、会话及缓存的管理。Authenticator
: 负责 Subject 认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过了;Authorizer
: 授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控 制着用户能访问应用中的哪些功能;Realm
:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要实现自己的 Realm;SessionManager
: 管理 Session 生命周期的组件;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境CacheManager
: 缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能Cryptography
: 密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密。
4. Quickstart
Shiro 为我们提供了一些入门学习的实例代码, 我们可以下载 https://downloads.apache.org/shiro/1.5.3/shiro-root-1.5.3-source-release.zip 学习.
4.1 创建一个 Maven 项目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>club.javafamily</groupId>
<artifactId>shiro01</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<log4j.version>1.2.17</log4j.version>
<slf4j.version>1.7.26</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>runtime</scope>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>runtime</scope>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>runtime</scope>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</project>
4.2 Log4j.properties
# 注意我这里把 Log 级别提到了 Error
log4j.rootLogger=ERROR, 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=INFO
# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
4.3 shiro.ini
[users]
# 用户 jack, 密码为 JavaFamily, 拥有 dev 和 test 两个 role
jack = JavaFamily, dev, test
[roles]
# admin role 拥有所有权限
admin = *
# dev role
dev = code:commit, code:checkout, code:test:write, code:test:read
# test role
test = code:checkout, code:test:read
** 使用 * 表示 role 拥有所有权限 **
4.4 创建程序入口类 Quickstart.java 并检查代码结构
4.5 代码分析
package club.javafamily.shiro01;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class Quickstart {
public static void main(String[] args) {
// 通过 ini 文件创建一个 SecurityManager 的工厂(生产环境不会这么用)
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
// 获取 SecurityManager
SecurityManager securityManager = factory.getInstance();
// 设置 SecurityManager 为单例, 以后通过 SecurityUtils(与 Spring 整合后没有意义)
SecurityUtils.setSecurityManager(securityManager);
// 获取 Subject 对象
Subject currentUser = SecurityUtils.getSubject();
// 测试使用 Session 对象 ---> 注意这是 SE 项目, 不是 web 项目, 依然可以用 Session
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
System.out.println("===测试 Session 对象: someKey = " + value);
// 测试登录
if (!currentUser.isAuthenticated()) { // 如果用户没有登录
// 通过用户名和密码构造 UsernamePasswordToken 用于验证
// 这里的的 jack---JavaFamily 用户名和密码来自于 shiro.ini 文件
UsernamePasswordToken token = new UsernamePasswordToken("jack", "JavaFamily");
// 设置 RememberMe
token.setRememberMe(true);
try {
// 执行登录
currentUser.login(token);
} catch (UnknownAccountException uae) {
System.out.println("账户不存在" + token.getPrincipal());
return;
} catch (IncorrectCredentialsException ice) {
System.out.println("密码不正确: " + token.getPrincipal());
return;
} catch (LockedAccountException lae) {
System.out.println("账户已经登录: " + token.getPrincipal());
return;
}
catch (AuthenticationException ae) {
System.out.println("登录失败! 请过会再试!");
return;
}
}
System.out.println("用户 " + currentUser.getPrincipal() + " 已成功登录!");
// 测试 Role
if (currentUser.hasRole("dev")) {
System.out.println("用户" + currentUser.getPrincipal() + " 属于 dev Role");
} else {
System.out.println("用户" + currentUser.getPrincipal() + " 不是 dev Role");
}
// 测试权限
if (currentUser.isPermitted("code:commit")) {
System.out.println("用户 " + currentUser.getPrincipal() + " 拥有代码提交的权限.");
} else {
System.out.println("用户 " + currentUser.getPrincipal() + " 没有代码提交的权限.");
}
if (currentUser.isPermitted("code:test:write")) {
System.out.println("用户 " + currentUser.getPrincipal() + " 拥有 code:test:write 权限");
} else {
System.out.println("用户 " + currentUser.getPrincipal() + " 没有 code:test:write 权限");
}
if (currentUser.isPermitted("code:test:delete")) {
System.out.println("用户 " + currentUser.getPrincipal() + " 拥有 code:test:delete 权限");
} else {
System.out.println("用户 " + currentUser.getPrincipal() + " 没有 code:test:delete 权限");
}
//注销
currentUser.logout();
System.exit(0);
}
}
** 分析帅帅已经用注释给大家标注了, 就不多啰嗦了. **
4.6 运行结果
** 最近几期文章可能都是关于 Shiro 的哦, 大家不要着急, 因为最近和朋友开发一个项目要用, 所以临时做一些 Shiro 的学习分享, 后面也有 Spring Security 系列的, 感兴趣的持续关注哦! **