Shiro学习01:使用Shiro实现身份管理和权限验证
Shiro学习01:使用Shiro实现身份管理和权限验证
- Shiro的基本概念
- Shiro入门实例1: 通过ini配置文件实现身份验证
- 项目准备
- 使用默认Realm组件
- 使用自定义Realm
- 使用密文存储密码
- Shiro入门实例2: 通过ini配置文件实现权限管理
- BRAC权限管理
- 使用ini配置文件为用户授权
- 使用自定义Realm为用户授权
- Shiro权限管理的执行流程
Shiro的基本概念
Shiro的基本功能:
Authentication
: 身份验证,即登录Authorization
: 权限验证,即授权Cryptography
: 加密,将密码以密文形式存储进数据库
Shiro的三个基本概念:
Subject
,SecurityManager
,Realms
Subject
表示当前操作的用户,可以通过Subject currentUser = SecurityUtils.getSubject()
获得当前Subject
.SecurityManager
安全控制器,是实现Shiro功能的核心,与其他组件进行交互,实现Subject
委托的各种功能,类似于Spring MVC中的DispatcherServlet
.Realms
安全数据源,Shiro从Realm
获取安全数据(如用户,角色,权限).
Shiro结构:
Subject
: 当前登录的用户SecurityManager
: 安全控制器,Shiro的核心,它管理着所有Subject
,且负责进行认证和授权,及会话,缓存的管理Authenticator
: 认证器,负责用户登录Authorizer
: 授权器,控制用户能访问哪些功能Realm
: 安全数据源,用于获取安全实体,一般在应用中我们需要自己实现Realm
.
Shiro入门实例1: 通过ini配置文件实现身份验证
项目准备
新建MAVEN项目,在pom.xml
中导入如下依赖:
<dependencies><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.2.2</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency>
</dependencies>
使用默认Realm组件
在不进行配置的情况下,Shiro会使用从ini文件中读取的数据创建数据源.
在
main/resources
目录下创建shiro.ini
文件如下,用来构建Realm
数据源[users] # 模拟用户数据库列表: 账号=密码 user1 = password1 user2 = password2
编写代码测试登录功能
@Test public void TestLogin() {// 通过ini配置文件创建SecurityManager工厂对象Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");// 通过工厂对象生产SecurityManager对象SecurityManager securityManager = factory.getInstance();// 将SecurityManager绑定到当前运行环境中SecurityUtils.setSecurityManager(securityManager);// 创建当前的登陆主体Subject currentUser = SecurityUtils.getSubject();// 模拟收集当前登录主体的身份凭证UsernamePasswordToken token = new UsernamePasswordToken("user1", "password1");// 尝试登陆账号try {currentUser.login(token);// 可以通过当前Subject对象的isAuthenticated()方法判断当前登录状态System.out.println("当前登录状态" + currentUser.isAuthenticated());} catch (UnknownAccountException uae) {System.out.println("username wasn't in the system");} catch (IncorrectCredentialsException ice) {System.out.println("password didn't match");} catch (LockedAccountException lae) {System.out.println("account for that username is locked");}// 退出账号currentUser.logout();System.out.println("当前登录状态" + currentUser.isAuthenticated()); }
输出:
当前登录状态true 当前登录状态false
若认证操作失败,系统可能抛出如下三种常见的异常:
UnknownAccountException
: 账号不存在IncorrectCredentialsException
: 账号存在但密码错误LockedAccountException
: 账户被锁定
观察源代码,发现程序的执行过程如下
使用自定义Realm
实现我们自定义的
Realm
类,继承自AuthorizingRealm
.[外链图片转存失败(img-TZxVWmQI-1565533563049)(1565340862010.png)]
package cn.maoritian.realm;public class MyRealm extends AuthorizingRealm {// 使用该方法返回值区分不同Realmpublic String getRealm() {return "MyRealm";}// doGetAuthorizationInfo()进行授权操作protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}// doGetAuthenticationInfo()进行认证操作protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// 从token中获取用户名String username = (String) token.getPrincipal();// 模拟根据用户名在数据库中查询密码String password;if (!"user1".equals(username)) {return null;} else {password = "password1";}// 返回登录比对信息AuthenticationInfo info = new SimpleAuthenticationInfo(username, password, getName());return info;} }
编写
shiro.ini
配置文件,指定使用我们自定义的Realm
[main] # 自定义realm myRealm =cn.maoritian.realm.MyRealm # 指定SecurityManager的realms实现 securityManager.realms=$myRealm
编写代码测试登录功能,测试代码与上个测试代码完全相同
@Test public void TestLoginMyRealm() {Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);Subject currentUser = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken("user1", "password1");try {currentUser.login(token);System.out.println("当前登录状态" + currentUser.isAuthenticated());} catch (UnknownAccountException uae) {System.out.println("username wasn't in the system");} catch (IncorrectCredentialsException ice) {System.out.println("password didn't match");} catch (LockedAccountException lae) {System.out.println("account for that username is locked");}currentUser.logout();System.out.println("当前登录状态" + currentUser.isAuthenticated()); }
使用密文存储密码
在实际项目中,我们都是将密码以加盐且多次迭代的方式存储在数据库中,使用shiro内置的方法,我们可以完成对明文的加密.
String password = "password"; // 明文
Md5Hash md5Hash = null; // 密文// 直接对明文进行1次md5加密
md5Hash = new Md5Hash(password);
System.out.println(md5Hash); // 5f4dcc3b5aa765d61d8327deb882cf99 // 对明文加盐进行1次md5加密
md5Hash = new Md5Hash(password, "username");
System.out.println(md5Hash); // d51c9a7e9353746a6020f9602d452929// 对明文加盐进行3次md5加密
md5Hash = new Md5Hash(password, "username", 3);
System.out.println(md5Hash); // c69d335dc6b83db4ca13ee576672ddf7
下面我们测试在自定义的Realm中使用密文进行登陆验证
编写
shiro.ini
配置文件,配置匹配凭证器[main] # 定义凭证匹配器 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher # 定义散列算法 credentialsMatcher.hashAlgorithmName=md5 # 定义散列迭代次数 credentialsMatcher.hashIterations=3# 自定义realm myRealm=cn.maoritian.realm.MyRealm # 指定SecurityManager的realms实现 securityManager.realms=$myRealm
配置自定义Realm,模拟从数据库中查询密文密码,注意此时的密文密码应为原密码不加盐迭代三次的结果
package cn.maoritian.realm;public class MyRealm extends AuthorizingRealm {// 使用该方法返回值区分不同Realmpublic String getRealm() {return "MyRealm";}// doGetAuthorizationInfo()进行授权操作protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}// doGetAuthenticationInfo()进行认证操作protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// 从token中获取用户名String username = (String) token.getPrincipal();// 模拟根据用户名在数据库中查询密码String password;if (!"user1".equals(username)) {return null;} else {// 此时返回的密文密码应为明文密码"password"不加盐迭代三次的结果password = "918f43923202882e53f05b3f02cb68f1";}// 返回登录比对信息AuthenticationInfo info = new SimpleAuthenticationInfo(username, password, getName());return info;} }
编写代码测试登录功能,测试代码与上面测试代码完全相同
@Test public void TestLoginWithCredential() {Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);Subject currentUser = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken("user1", "password1");try {currentUser.login(token);System.out.println("当前登录状态" + currentUser.isAuthenticated());} catch (UnknownAccountException uae) {System.out.println("username wasn't in the system");} catch (IncorrectCredentialsException ice) {System.out.println("password didn't match");} catch (LockedAccountException lae) {System.out.println("account for that username is locked");}currentUser.logout();System.out.println("当前登录状态" + currentUser.isAuthenticated()); }
Shiro入门实例2: 通过ini配置文件实现权限管理
BRAC权限管理
BRAC为基于角色的权限管理,有如下三个概念:
user
: 用户,指当前操作用户role
: 角色,是权限的集合permission
: 权限,对资源的操作许可
角色和权限的对应关系可以使用权限表达式来表示,其形式为资源:操作:实例
,示例如下:
权限表达式 | 意义 |
---|---|
user:create 或user:create:*
|
对用户资源的创建权限 |
user:update:001
|
对用户实例001的修改权限 |
user:*:001
|
对用户实例001的所有权限 |
使用ini配置文件为用户授权
编写
shiro.ini
文件,为用户配置权限[users] # 模拟用户数据库列表: 账号=密码,权限 user1=password1,role1,role2 user2=password2,role3[roles] # 角色role1对资源user具有所有权限 role1=user:* # 角色role2对资源user具有create,delete权限 role2=user:create,user:delete # 角色role3对资源user具有create权限 role3=user:create
编写测试代码如下:
@Test public void TestRoles() {// 使用user1登录系统Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);Subject currentUser = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken("user1", "password1");currentUser.login(token);// 判断当前用户是否拥有某角色System.out.println(currentUser.hasRole("role1"));// 判断当前用户是否拥有列表中所有角色System.out.println(currentUser.hasAllRoles(Arrays.asList("role1", "role2")));// 以数组形式返回当前用户是否拥有某角色,此方法可以减少hasRole()方法的调用次数System.out.println(Arrays.toString(currentUser.hasRoles(Arrays.asList("role1", "role2", "role3"))));// 若当前用户没有某角色则报UnauthorizedException异常currentUser.checkRole("role1");// 若当前用户没有所有角色则报UnauthorizedException异常currentUser.checkRoles("role1", "role2", "role3"); }
@Test public void TestPermissions() {// 使用user1登录系统Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);Subject currentUser = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken("user1", "password1");currentUser.login(token);// 判断当前用户是否拥有某权限System.out.println(currentUser.isPermitted("user:delete"));// 判断当前用户是否拥有列表中所有权限System.out.println(currentUser.isPermittedAll("user:delete", "user:list"));// 以数组形式返回当前用户是否拥有某权限,此方法可以减少isPermitted()方法的调用次数System.out.println(Arrays.toString(currentUser.isPermitted("user:delete", "user:list"))); }
使用自定义Realm为用户授权
实现自定义
Realm
,重写其doGetAuthorizationInfo()
方法public class MyRealm extends AuthorizingRealm {public String getRealm() {return "MyRealm";}// doGetAuthorizationInfo()进行授权操作protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// 获取用户名,用于查询角色权限信息String username = (String) principals.getPrimaryPrincipal();// 模拟根据用户名到数据库中查询角色和权限信息List<String> roles = new ArrayList<String>(); //角色集合List<String> permissions = new ArrayList<String>(); //权限集合roles.add("role1");permissions.add("user:*");// 将角色和权限信息封装进infoAuthorizationInfo对象SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.addRoles(roles);info.addStringPermissions(permissions);return info;}// doGetAuthenticationInfo()进行认证操作protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// 从数据库中查询所有用户名} }
测试代码与上面完全相同,略
Shiro权限管理的执行流程
当我们调用Subject
对象的isPermitted()
或hasRole()
方法,会被委托给SecurityManager
,进而被委托给Authorizer
.Authorizer
在进行授权之前,会调用PermissionResolver
将字符串转化为相应的Permission
实例.若有多个Realm,会委托给ModularRealmAuthorizer
进行循环判断.
Shiro学习01:使用Shiro实现身份管理和权限验证相关推荐
- shiro学习系列:shiro自定义filter过滤器
shiro学习系列:shiro自定义filter过滤器 自定义JwtFilter的hierarchy(层次体系) 上代码 package com.finn.springboot.common.conf ...
- shiro学习(1):shiro简介
Apache Shiro是Java的一个安全框架.对比另一个安全框架Spring Sercurity,它更简单和灵活. Shiro可以帮助我们完成:认证.授权.加密.会话管理.Web集成.缓存等. A ...
- Shiro学习笔记四(Shiro集成WEB)
这两天由于家里出了点事情,没有准时的进行学习.今天补上之前的笔记 -----没有学不会的技术,只有不停找借口的人 学习到的知识点: 1.Shiro 集成WEB 2.基于角色的权限控制 3.基于权限的控 ...
- shiro学习(8):shiro连接数据库 三
工具idea 先看看数据库 shiro_role_permission 数据 shiro_user shiro_user_role 数据 我们先看一下目录结构 首先 log4j.properties ...
- Shiro学习(24)在线回话管理
有时候需要显示当前在线人数.当前在线用户,有时候可能需要强制某个用户下线等:此时就需要获取相应的在线用户并进行一些操作. 本章基于<第十六章 综合实例>代码构建. 会话控制器 Java代码 ...
- shiro学习(4):shiro认证流程
Shiro登录校验流程实现与分析 什么是Shiro Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地 ...
- shiro学习(7):shiro连接数据库 方式二
工具idea 先看看数据库 shiro_role_permission 数据 shiro_user shiro_user_role 数据 我们先看一下目录结构 首先 jar包引入 pom.xml文件 ...
- shiro学习(6):shiro连接数据库
首先我们先看一下数据库 再看看数据库的测试数据 在我们创建好的maven项目中看一下目录结构 在pom.xml引入 <dependency><groupId>com.mchan ...
- linux用户没有创建文件的权限设置密码,Linux学习第五章用户身份与文件权限
一.用户身份与能力 Linux系统中一共有三种用户 第一种:管理员 root UID =0 第二种:系统用户 不需要登录系统 负责单一服务的运行 UID = 0-1000 第三种:普通用户 日 ...
- SpringBoot整合Shiro学习(上)
SpringBoot整合Shiro(上) 基于[编程不良人]2020最新版Shiro教程,整合SpringBoot项目实战教程 哔哩哔哩链接:https://www.bilibili.com/vide ...
最新文章
- oracle 越南字符,ORACLE 12.2RAC之问题 ora.chad OFFLINE
- 命令行的基本使用方法(权限)
- Ubuntu中启用 ThinkPad指纹识别
- XSuperTooltip - Office 2007 Super Tooltip class
- U3D assetbundle加载
- Discuz! $_DCACHE数组变量覆盖漏洞
- Python基础--Python3基础语法
- mysql创建存储时覆盖_总结到位的MySQL 的覆盖索引与回表
- 工作225:当前导致name报错
- python常用开放工具_python学习笔记16-各种模块和开放工具收集整理
- AutoFac文档9(转载)
- python and or 详解
- 最长双重叠字符串java_java – 重复但重叠的字符串的算法
- 自编Python机器人,内置词库可改写。
- 计算机专业实习报告-5000字+,以及计算机专业实习周记-15篇
- 形式语言与自动机 第五章 课后题答案
- Redis单机版和集群搭建部署
- 快速傅里叶变换(FFT)学习
- ajax data=text,jQuery ajax dataType值为text json探索分享
- iOS - 解决Warning: Attempt to present which is already presenting