阅读本博客预计消耗你20分钟的时间,看完此博客希望对你掌握Shiro框架有一定帮助
在学习之前我们先了解了解shiro,防止掉坑,减少一些不必要的误知,内容稍多,如果有使用经验,建议选择性阅读,没有建议从博客开始阅读,阅读本博客大约 5-15分钟。
1. 什么是Shiro?
1.1 Shiro的官网解释
Apache Shiro™是一个功能强大且易于使用的Java安全框架,它执行身份验证,授权,加密和会话管理。使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大的Web和企业应用程序
特点:属于Apache开源组织的一个产品,功能强大并且易用的Java安全框架
- 可以完成用户认证、授权、密码以及会话管理
- 可以在任何的应用系统中使用(主要针对单体项目的权限管理)
- 易于使用
- 具有Web功能,允许基于应用程序URL和Web协议(例如REST)创建灵活的安全策略,同时还提供一组JSP库来控制页面输出
- Shiro干净的API和设计模式使它易于与许多其他框架和应用程序集成。
- 支持,Spring,Grails,Wicket,Tapestry,Tynamo,Mule和Vaadin
1.2 Shiro的核心
Shiro提供了应用程序安全性API来执行以下方面(应用程序安全性的4个基石):
-
身份验证-证明用户身份,通常称为用户“登录”。
-
授权-访问控制
-
密码术-保护或隐藏数据以防窥视
-
会话管理-每个用户的时间敏感状态
2. Shiro的工作原理
2.1 Shiro的四大核心功能
-
Authentication 认证验证用户是否有相应的身份—登录认证
-
Authorization 授权,即权限认证:对已经通过认真的用户检查是否具有某个权限或者角色,从而控制是否能进行某种操作
-
Session Manager 会话管理,用户在认证成功之后创建会话,在没有退出之前,当前用户的所有信息都会保存在这个会话中,可以是普通的JavaSE应用,也可以是web应用
-
Cryptography 加密 对敏感信息进行加密处理,shiro就提供了这种加密的机制
-
支持的特性:
- Web Support --shiro提供了过滤器,可以通过过滤器拦截web请求来处理web应用的访问控制
- Caching 缓存支持 shiro可以缓存用户信息以及用户的角色权限信息,可以提高执行效率
- Concurrency shiro支持多线程应用
- Testing 提供测试支持
- Run As 允许一个用户以另一种身份去访问
- Remeber Me
- 说明:Shiro是一个安全框架,不提供用户、权限的维护(用户的权限管理需要我们自己去设计)
2.2 Shiro的核心组件
原理图:
-
Shiro的三大核心组件(Subject、Security Manager、Realms)
-
Shiro工作流程示意图:
-
Subject 表示待认证授权的用户
-
Security Manager,它是Shiro框架的核心,Shiro就是通过Security Manager 来进行内部实例的管理,并通过它来提供给安全管理的各种服务。
- Authenticator 认证器
- Ahthorizer 授权器
- SessionManager 会话管理器
- CacheManager 缓存管理器
-
Realm 相当于Shiro进行认证和授权的数据源,充当了Shiro与安全数据之间的桥梁或者连接器,也就说说对用户信息进行认证(登录)和授权(访问控制)验证时,Shiro会用应用配制的Realm中查找用户及其权限信息。
-
3. Shiro知识点详情概要
3.1 什么是权限管理?
不同身份的用户进入到系统所能完成的操作是不相同的,我们对不同用户的可执行的操作的管理称之为用户的权限管理。
3.2 如何实现权限管理
权限管理设计
-
基于主页的权限管理(不同用户使用不同的主页,权限通过主页功能菜单限制)
-
适用于权限管理比较单一、用户少、每类权限固定
-
基于用户和权限的权限管理
-
可以实现权限的动态分配,但是不够灵活
-
RBAC 基于角色的访问控制 (Role-Based Access Control )
-
表格 (用户表(所有员工)、角色表(所有职位)、用户角色表(员工的职位)、
系统权限表(权限资源)、角色权限表(统一授权)、用户权限表(单独授权))
-
3.3 认证授权
-
认证:对用户的身份进行检查(登录验证)
-
授权:对用户的权限进行检查(是否有对应的操作权限)
3.4 安全框架
- 帮助我们完成用户身份认证及身份检查功能框架
- 常用的安全框架:
- Shiro:Apache Shiro是一个功能强大且易用的Java安全框架(小而简单)
- Spring Security :基于Spring的一个安全框架,依赖于Spring
- OAuth2:第三方的授权登录
- 自定义安全认证中心
4. 如何使用Shiro
4.1 基于JavaSE的基本使用
4.1.1 创建Maven工程
不需要导入任何骨架,直接创建一个简单的Maven工程即可
4.1.2 导入Shiro的依赖库
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
4.1.3 创建Shiro配置文件
-
在resource目录下创建名为shiro.ini的文件
-
在shiro.ini文件中完成用户、角色及权限的配置
[users] zhangsan=123456,seller lisi=666666,ckmgr admin=222222,admin [roles] admin=* seller=order-add,order-del,order-list ckmgr=ck-add,ck-del,ck-list
-
实现代码:
Scanner sc = new Scanner(System.in); System.out.println("请输入账号:"); String username = sc.next(); System.out.println("请输入密码:"); String password = sc.next(); // 拿到一个安全管理器对象 DefaultSecurityManager securityManager = new DefaultSecurityManager(); // 创建一个安全数据源 IniRealm IniRealm = new IniRealm("classpath:shiro.ini"); // 将安全数据源交给安全管理器 securityManager.setRealm(IniRealm); // 将Realm设置给SecurityUtils工具 SecurityUtils.setSecurityManager(securityManager); // 通过SecurityUtils工具类获取Subject对象 Subject Subject = SecurityUtils.getSubject(); // 【认证流程】 // 将认证账号和密码封装到token对象中 UsernamePasswordToken token = new UsernamePasswordToken(username,password); // 接收验证结果 boolean result = false; try { // 将令牌交给Subject Subject.login(token); result = true; }catch (IncorrectCredentialsException e){ result = false; } System.out.println(result?"登录成功":"登录失败"); // 【授权】 // 判断登录的用户是否是特定角色 System.out.println("是不是销售员: "+Subject.hasRole("seller")); // 判断登录的用户是否具有特定权限 System.out.println("是否具有订单的删除功能: "+ Subject.isPermitted("order-del"));
4.2 SpringBoot-整合Shiro(ini文件方式)
4.2.1 导入shiro依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
4.2.2 导入thymeleaf相关依赖
<!-- 修改thymeleaf版本 -->
<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version>
<!-- 修改thymeleaf版本 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
<!-- 导入thymeleaf依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
-
Shiro配置 (Java配置方式)
-
SpringBoot默认没有提供shiro的自动配置
@Configuration public class ShiroConfig { @Bean public IniRealm getIniRealm() { // 返回一个安全数据源 从文件中读取 IniRealm iniRealm = new IniRealm("classpath:shiro.ini"); return iniRealm; } @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager (IniRealm iniRealm) { // 将数据源交给Web默认安全管理器 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(iniRealm); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean (DefaultWebSecurityManager securityManager) { // 过滤器 ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean(); // 过滤器就是shiro进行权限校验的核心,认证权限校验需要securityManager filter.setSecurityManager(securityManager); // 设置Shiro的拦截规则 /** * anon 匿名用户可访问 * authc 认证用户可访问 * user 使用RemeberMe的用户可访问 * perms 对应权限可访问 * role 对应的角色可访问 */ Map<String, String> filterMap = new HashMap<String,String>(); filterMap.put("/", "anon"); filterMap.put("/login.html", "anon"); filterMap.put("/regist.html", "anon"); filterMap.put("/user/login", "anon"); filterMap.put("/user/regist", "anon"); filterMap.put("/static/**", "anon"); filterMap.put("/**", "authc"); // 将规则发布给过滤器 filter.setFilterChainDefinitionMap(filterMap); // 设置默认的登录界面 filter.setLoginUrl("/login.html"); // 设置未授权访问的界面路径 filter.setUnauthorizedUrl("/login.html"); return filter; } }
-
-
认证测试
-
UserServiceImpl
// 将账号密码交给令牌 public void checkLogin(String username,String password) throws Exception{ Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken (username, password); subject.login(token); }
-
UserController
@Controller @RequestMapping("user") public class UserController { @Resource private UserServiceImpl userService; @GetMapping("/login") public String login(String username,String password) { try { userService.checkLogin(username, password); System.out.println("登录成功"); return "index"; } catch (Exception e) { System.out.println("登录失败"); return "login"; } } }
-
PageController
@GetMapping("/login.html") public String login() { return "login"; } @RequestMapping("/") public String login1() { return "login"; } @GetMapping("/index.html") public String index() { return "index"; }
-
4.3 SpringBoot-整合Shiro -jdbc
使用shiro内置JDBC需要对表格关系进行设计,才可使用
创建SpringBoot工程时,导入Mybatis、thymeleaf、SpringBootDevTools、SpringWeb
#####导入Shiro的spring依赖,配置yml文件
-
表名
users(用户表)、user_roles(用户角色表)、roles_permissions(角色权限表)
-
用户表字段
id(用户ID)、username(用户名)、password(用户密码)、password_salt(加盐)
-
用户角色表字段
id(用户角色表ID)、username(用户名)、role_name(角色名)
-
角色权限表
id(角色权限ID)、role_name(角色名)、permission(权限标识)
-
配置jdbcRealm
@Bean public JdbcRealm getJdbcRealm(DataSource dataSource) { JdbcRealm realm = new JdbcRealm(); // jdbcReaml会自行从数据库查询用户及权限数据(数据库的表结构要符合jdbcRealm的规格) realm.setDataSource(dataSource); // 默认只开启认证功能,手动开启权限功能 realm.setPermissionsLookupEnabled(true); return realm; }
使用jdbc的时候,我们获取realm的来源就不再是ini文件,而是数据库,ShiroDialect方法(下面有)也得加上,减少报错的机率,其余控制器,业务还有配置的代码都是一样的,面对CtrlCV编程就可以了
— 创建改变语法的方法
@Bean(name = "getShiroDialect")
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
4.4 Shiro的标签使用
Shiro提供了一套标签用于在页面来进行权限数据的呈现
- 导入依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!--可能需要改变thymeleaf的版本配置 -->
<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version>
- 模板引擎
<html lang="zh_CN" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
- 使用Shiro标签来获取显示角色的权限和用户的认证
<!-- 游客显示的内容 -->
<shiro:guest>
欢迎游客访问,<a href="login.html">登录</a>
</shiro:guest>
<!-- 登录的用户显示的内容 -->
<shiro:user>
恭喜用户[<shiro:principal/>]登录成功<br/>
当前用户为<shiro:hasRole name="admin">超级管理员</shiro:hasRole>
<shiro:hasRole name="cmanager">仓管人员</shiro:hasRole>
<shiro:hasRole name="xmanager">销售人员</shiro:hasRole>
<shiro:hasRole name="kmanager">客服人员</shiro:hasRole>
<shiro:hasRole name="zmanager">行政人员</shiro:hasRole>
</shiro:user>
<h3>仓库管理</h3>
<ul>
<shiro:hasPermission name="sys:c:save">
<li><a href="#">入库</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="sys:c:delete">
<li><a href="#">出库</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="sys:c:update">
<li><a href="#">更库</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="sys:c:find">
<li><a href="#">查库</a></li>
</shiro:hasPermission>
</ul>
<h3>订单管理</h3>
<ul>
<shiro:hasPermission name="sys:x:save">
<li><a href="#">添加订单</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="sys:x:delete">
<li><a href="#">删除订单</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="sys:x:update">
<li><a href="#">修改订单</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="sys:x:find">
<li><a href="#">查询订单</a></li>
</shiro:hasPermission>
</ul>
<h3>客户管理</h3>
<ul>
<shiro:hasPermission name="sys:k:save">
<li><a href="#">添加客户</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="sys:k:delete">
<li><a href="#">删除客户</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="sys:k:update">
<li><a href="#">修改订单</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="sys:k:find">
<li><a href="#">查询订单</a></li>
</shiro:hasPermission>
</ul>
注意事项:
- 因为我们使用的是thymeleaf的模板引擎,所以要使用动态文件夹template下,如果你的页面有外部文件,就使用thymeleaf 表达式【th:src=“@{js/vue.js}”】导入外部文件
- 登录界面
<div style="margin:0px auto;height:100px;width:100px;">
<form action="/user/login">
<input name="username" type="text" placeholder="请输入用户名..." /><br/>
<input name="password" type="password" placeholder="请输入密码...." /><br/>
<input type="submit" value="登录" />
</form>
</div>
- 主页面 (别忘记在xmlns中导入模板引擎,上面有)
<body>
<div id="dv">
<!-- 游客显示的内容 -->
<shiro:guest>
欢迎游客访问,<a href="login.html">登录</a>
</shiro:guest>
<!-- 登录的用户显示的内容 -->
<shiro:user>
恭喜用户[<shiro:principal/>]登录成功<br/>
当前用户为<shiro:hasRole name="admin">超级管理员</shiro:hasRole>
<shiro:hasRole name="cmanager">仓管人员</shiro:hasRole>
<shiro:hasRole name="xmanager">销售人员</shiro:hasRole>
<shiro:hasRole name="kmanager">客服人员</shiro:hasRole>
<shiro:hasRole name="zmanager">行政人员</shiro:hasRole>
</shiro:user>
<h3>仓库管理</h3>
<ul>
<shiro:hasPermission name="sys:c:save"><li><a href="#">入库</a></li></shiro:hasPermission>
<shiro:hasPermission name="sys:c:delete"><li><a href="#">出库</a></li></shiro:hasPermission>
<shiro:hasPermission name="sys:c:update"><li><a href="#">更库</a></li></shiro:hasPermission>
<shiro:hasPermission name="sys:c:find"><li><a href="#">查库</a></li></shiro:hasPermission>
</ul>
<h3>订单管理</h3>
<ul>
<shiro:hasPermission name="sys:x:save"><li><a href="#">添加订单</a></li></shiro:hasPermission>
<shiro:hasPermission name="sys:x:delete"><li><a href="#">删除订单</a></li></shiro:hasPermission>
<shiro:hasPermission name="sys:x:update"><li><a href="#">修改订单</a></li></shiro:hasPermission>
<shiro:hasPermission name="sys:x:find"><li><a href="#">查询订单</a></li></shiro:hasPermission>
</ul>
<h3>客户管理</h3>
<ul>
<shiro:hasPermission name="sys:k:save"><li><a href="#">添加客户</a></li></shiro:hasPermission>
<shiro:hasPermission name="sys:k:delete"><li><a href="#">删除客户</a></li></shiro:hasPermission>
<shiro:hasPermission name="sys:k:update"><li><a href="#">修改订单</a></li></shiro:hasPermission>
<shiro:hasPermission name="sys:k:find"><li><a href="#">查询订单</a></li></shiro:hasPermission>
</ul>
</div>
</body>
- yml/propertties文件配置数据库的数据源 (不配置数据源mybatis会提示错误)
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
// 日志详细信息
logging.level.web=debug
spring.mvc.log-request-details=true
- 最后给大家看一下我的工程结构,这篇菜鸟博客就写完了
有问题欢迎在博客下留言,希望能帮助到你,再会