Apache Shiro 是一个强大且易用的 Java 安全框架,执行身份验证、授权、密码和会话管理。使用 Shiro 的易于理解的 API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
概述
简介
Apache Shiro 是一个强大且易用的 Java 安全框架
可以完成身份验证、授权、密码和会话管理
Shiro 不仅可以用在 JavaSE 环境中,也可以用在 JavaEE 环境中
主要功能
Authentication
:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization
:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager
:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
Cryptography
:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support
:Web 支持,可以非常容易的集成到 Web 环境;
Caching
:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency
:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing
:提供测试支持;
Run As
:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me
:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
从外部看
应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject;
其每个 API 的含义:
Subject
:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;
SecurityManager
:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;
Realm
:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。
也就是说对于我们而言,最简单的一个 Shiro 应用:
- 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;
- 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。
从以上也可以看出,Shiro 不提供维护用户/权限,而是通过 Realm 让开发人员自己注入。
三个核心组件:Subject,SecurityManager 和 Realms
Subject:
即“当前操作用户”。但是,在 Shiro 中,Subject 这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject 代表了当前用户的安全操作,SecurityManager 则管理所有用户的安全操作。
SecurityManager:
它是 Shiro 框架的核心,典型的 Facade 模式,Shiro 通过 SecurityManager 来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm:
Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息。
从这个意义上讲,Realm 实质上是一个安全相关的 DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给 Shiro。当配置 Shiro 时,你必须至少指定一个 Realm,用于认证和(或)授权。配置多个 Realm 是可以的,但是至少需要一个。
Shiro 内置了可以连接大量安全数据源(又名目录)的 Realm,如 LDAP、关系数据库(JDBC)、类似 INI 的文本配置资源以及属性文件等。如果缺省的 Realm 不能满足需求,你还可以插入代表自定义数据源的自己的 Realm 实现。
外部架构
Subject
:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager
:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator
:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authrizer
:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm
:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;
SessionManager
:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所有呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);
SessionDAO
:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;
CacheManager
:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
Cryptography
:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密的
认证流程
用户
提交 身份信息
、凭证信息
封装成 令牌
交由 安全管理器
认证
快速入门
拷贝案例
1、按照官网提示找到 10 分钟快速入门案例:http://shiro.apache.org/10-minute-tutorial.html
- GitHub 地址:shiro quickstart
- 从 GitHub 的文件中可以看出这个快速入门案例是一个 Maven 项目
2、新建一个 Maven 工程,删除其 src 目录,将其作为父工程
3、在父工程中新建一个 Maven 模块
4、复制快速入门案例 pom.xml
文件中的依赖
(版本号自选)
1 | <dependencies> |
5、把快速入门案例中的 resource 下的log4j.properties
拷贝到 resources 下
1 | INFO, stdout = |
6、拷贝一下 shiro.ini
文件到 resources 下
1 | [users] |
7、复制一下 Quickstart.java
文件
1 | import org.apache.shiro.SecurityUtils; |
8、运行 Quickstart.java
,得到结果
分析案例
1、通过 SecurityUtils 获取当前执行的用户 Subject
1 | Subject currentUser = SecurityUtils.getSubject(); |
2、通过 当前用户拿到 Session
1 | Session session = currentUser.getSession(); |
3、用 Session 存值取值
1 | session.setAttribute("someKey", "aValue"); |
4、判断用户是否被认证
1 | currentUser.isAuthenticated() |
5、执行登录操作
1 | currentUser.login(token); |
6、打印其标识主体
1 | currentUser.getPrincipal() |
7、注销
1 | currentUser.logout(); |
8、总览
1 | import org.apache.shiro.SecurityUtils; |
SpringBoot 集成 Shiro
创建 Shiro 环境
在之前创好的父工程中创建一个普通 SpringBoot 的 Web 工程 springboot-shiro-01,并添加依赖包
1 | <!--shiro以及shiro集成thymeleaf依赖--> |
配置 Shiro
定义 MyRealm 类
1 | package com.itjing.realm; |
ctrl+h 查看结构
定义配置类 ShiroConfig
1 | package com.itjing.config; |
定义 UserController 测试
1 | package com.itjing.controller; |
定义页面
login.html 、nopermission.html、success.html
login.html
1 | <form action="login" method="post"> |
nopermission.html
1 | <h1>没有权限请联系管理员</h1> |
success.html
1 | <h1>登录成功</h1> |
测试
打开浏览器访问 /success 以及 /admin/test 请求
由于配置/admin/test 的请求需要认证因此无法直接访问而是转向到了登录页面
而访问/success 请求因为没有配置对应的拦截所以可以访问
配置 Shiro 认证账号
修改 MyRealm 类
1 | package com.itjing.realm; |
修改 UserController
1 | "/login") ( |
测试
打开浏览器访问,当输入账号为 shangsan 密码任意,则会显示账号冻结的错误提示
输入账号 abc 密码任意,则出现账号不存在的提示
输入账号 admin 密码为 123,则出现密码错误的提示
当输入账号为 admin 密码为 123456,则户登录成功显示登录成功
认证缓存
当登录成功过一次以后我可以点击后退,然后输入任意账号和密码这时无论输入什么信息 Shiro 都会认为认证成功,这是因为 Shiro 在登录成功以后会将数据写入 Shiro 的缓存导致,因此应该在登录请求的控制器中在判断是否认证过之前添加一个登出操作,已清空缓存这样就可以重复测试登录。
修改 UserController
1 | "/login") ( |
密码加密
修改 MyRealm 类
1 | //定义一个密码,这个密码是数据库中的密码我们应该从数据库中获取 |
注意:
1、 通常数据库中存放的数据不应该是明码 123456,而是加密后的数据,例如 e10adc3949ba59abbe56e057f20f883e,这是使用 MD5 加密后的 123456,如果数据库中的密码已经是加密后的那么这里可以不选择进行加密。
2、 如果数据库中的密码已经加密那么页面中传递数据前必须要对密码进行加密才能传递,否则无法可能会登录失败。
3、 如果选择加密传递那么页面和数据库中的密码加密次数以及盐必须相同,否则登录一定失败。
修改 login.html
加入 jquery 和 md5 的 js
1 | <form action="login" method="post"> |
权限分配
需要判断用户哪个权限是否可以使用,我们就必须要先为用户分配一个权限才可以分配需要在 MyRealm 类中配置并且修改继承的父类
修改 MyRealm 类
修改 MyRealm 类继承的父类为 AuthorizingRealm 类,并实现抽象方法 doGetAuthorizationInfo()
1 | //Shiro用户授权的回调方法 |
修改 ShiroConfig
用户拥有角色和权限以后需要配置 Shiro 的权限规则,注释掉之前 zhangsan 设定的 “账号被锁定”
1 | //配置一个Shiro的过滤器bean,这个bean将配置Shiro相关的一个规则的拦截 |
测试
登录 zhangsan 的账号,访问 /admin 相关的接口,则会自动跳转到 noPermission.html 中,说明没有权限!
基于注解的权限控制
Shiro 不仅支持配置的权限控制还支持基于控制器注解的权限控制,如果需要使用 Shiro 的注解权限必须要在配置类中启动 Shiro 注解支持
修改 ShiroConfig
1 |
|
注意:
启动注解的权限控制以后需要删除在 Shiro 配置类中的权限拦截的配置规则
1 | map.put("/admin/test","authc,perms[admin:add]"); |
修改 UserController
1 | "user"}) (value = { |
注意:
Shiro 验证失败以后会抛出异常,因此这时必须要配置一个 Spring 的异常监控方法 myError 否则当前 Shiro 权限认证失败以后将无法转向到错误页面。
Shiro 标签
使用 Thymeleaf 整合 Shiro 标签
添加 Maven 依赖
1 | <dependency> |
添加配置 Bean
1 |
|
页面引入命名空间
1 | xmlns:shiro="http://www.pollix.at/thymeleaf/shiro" |
Shiro 标签语法
作为属性控制
1 | <button type="button" shiro:authenticated="true">权限控制</button> |
作为标签
1 | <shiro:hasRole name="admin"> |
常用标签说明
guest 标签
1 | <shiro:guest> </shiro:guest> |
用户没有身份验证时显示相应信息,即游客访问信息。
user 标签
1 | <shiro:user> </shiro:user> |
用户已经身份验证/记住我登录后显示相应的信息。
authenticated 标签
1 | <shiro:authenticated> </shiro:authenticated> |
用户已经身份验证通过,即 Subject.login 登录成功,不是”记住我”登录的
notAuthenticated 标签
1 | <shiro:notAuthenticated> </shiro:notAuthenticated> |
用户已经身份验证通过,即没有调用 Subject.login 进行登录,包括”记住我”自动登录的也属于未进行身份验证。
principal 标签
1 | <shiro:principal property="username"> </shiro:principal> |
相当于 ((User)Subject.getPrincipals()).getUsername()
。
lacksPermission 标签
1 | <shiro:lacksPermission name="org:create"> </shiro:lacksPermission> |
如果当前 Subject 没有权限将显示 body 体内容。
hasRole 标签
1 | <shiro:hasRole name="admin"> </shiro:hasRole> |
如果当前 Subject 有角色将显示 body 体内容。
hasAnyRoles 标签
1 | <shiro:hasAnyRoles name="admin,user"> </shiro:hasAnyRoles> |
如果当前 Subject 有任意一个角色(或的关系)将显示 body 体内容。
lacksRole 标签
1 | <shiro:lacksRole name="abc"> </shiro:lacksRole> |
如果当前 Subject 没有角色将显示 body 体内容。
hasPermission 标签
1 | <!--如果当前Subject有权限将显示body体内容。--> |
后续更新 shiro 更多用法!
发布时间: 2020-12-24
最后更新: 2024-06-24
本文标题: SpringBoot集成Shiro
本文链接: https://blog-yilia.xiaojingge.com/posts/8c925ea2.html
版权声明: 本作品采用 CC BY-NC-SA 4.0 许可协议进行许可。转载请注明出处!
