shiro的使用

本文详细介绍了Apache Shiro的使用,从Shiro的基本概念、为何使用Shiro到具体的配置步骤,包括 Realm 的机制和权限校验配置。Shiro提供权限校验、授权、会话管理和加密等功能,简化了RBAC系统的开发工作。通过导入Shiro相关jar包,创建shiro.ini配置文件,创建SecurityManager对象,以及编写Realm来实现数据库中的用户权限验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Shiro概述

1.1 Shiro是什么

Shiro是一个非常强大的、易于使用的、开源的、权限框架。它包括了权限校验、权限授予、会话管理、安全加密等组件。

1.2 为什么需要使用Shiro

如果你是需要设计RBAC(Role Based Access Control)基础系统,需要编写大量用于权限控制的代码时。那么你需要使用Shiro。因为Shiro已经将RBAC系统大量的代码封装好,可以减少我们大量的工作量。

1.3 Shiro需要的jar包

shiro需要的核心jar包

1.4 Shiro结构图

Shiro结构图

Authentication:权限校验,每次操作校验用户是否有访问权限
Authorization:授权,用户登录时,授予用户对应的权限
Session Management:会话管理,用于记录用户的登录状态
Cryptography:加密,加密算法的实现(SHA、MD5)
web Support:对Web项目的支持,Shiro的标签!!

2.Shiro入门

2.1.访问流程图

访问流程图
1.首先应用访问(可以使用远程调用,可以是Web请求等),Shiro通过一个Subject对象来标识当前访问的身份。这句话告诉我们,第一次访问的时候,Shiro肯定会创建一个Subject对象标签当前请求(用户)的身份。
2.SecurityManger容器创建一个Subject对象验证请求的参数,SecurityManager的作用是统一管理Subject。这句话意味着,一个SecurityManager对象管理多个Subject的对象。
3.Subject通过SecurityManger获得操作当前用户的权限,在启动的那一刻,SecurityManger就会加载shiro.ini权限配置文件,在用户登录成功后,可以根据shiro配置的信息,获得用户对应的权限。
shiro配置:是一个权限控制信息文件,里面必须包括用户的验证信息,权限的信息

2.2 .配置步骤说明

根据访问流程图,我们要使用访问Shiro权限框架的功能。首先需要有一个配置文件shiro.ini配置文件配置了用户的权限认证信息。然后通过SessionManager对象读取配置文件,获得用户身份Subject,
客户端代码通过当前用户访问有权限的操作。

由此,得出配置步骤:

第一步:任何框架都需要导入包
第二步:创建一个shiro.ini配置文件。(文件名任意编写,后缀必须为ini)
第三步:编写测试代码

2.3.配置步骤

2.3.1.第一步:导入包

所需jar包

2.3.2.第二步:shiro.ini配置文件

创建一个shiro.ini配置,编写权限认证信息。

注意事项:
1.shiro.ini文件名可以任意编写,但后缀必须是ini
shiro.ini配置文件放在classpath根目录

shiro.ini规则说明
在这里插入图片描述

在这里插入代码片

[users]
 jim = jim,admin

[roles]
 admin = *

2.3.3.第三步:创建SecurityManager对象

package cn.gzsxt.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;

public class ShiroDemo {
public static void main(String[] args) {
		// 1.获得SecurityManager对象
		IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
		SecurityManager securityManager = factory.createInstance();
		
		//2.设置SecurityUtils使用的安全管理器是securityManager
		SecurityUtils.setSecurityManager(securityManager);
		
		//2.获得subject
		Subject subject = SecurityUtils.getSubject();
		AuthenticationToken token=new UsernamePasswordToken("jim", "jim");
		//3.校验用户名密码是否正确
	
		try {
			Subject resultSubject = securityManager.login(subject, token);
			//获得用户名
			System.out.println(resultSubject.getPrincipal());
			//判断是否拥有admin角色
			boolean hasRole = resultSubject.hasRole("admin");
			System.out.println(hasRole);
		} catch (AuthenticationException e) {
			System.out.println("校验失败,用户名或者密码不正确");
			e.printStackTrace();
		}
	}
}

3.Realm的使用

3.1.Realm机制的必要性

以上案例,我们看到,我们的用户验证信息来自于配置文件的[users]以及[roles]标记。这样的难以符合我们实际的需求。

我们希望可以将Shiro校验的用户信息存储在数据库里面,在从数据库里面读取出来。

解决方案:Shiro是通过Realm机制,实现将配置文件的校验用户信息存放在数据库、LDAP等数据存储系统里面。

说白了,现在我们要做的事情,就是从数据库里面获得用户的验证信息!!!

3.2.访问流程图说明

.访问流程图

如图所示:
1.我们需通过Subject封装访问用户的信息
2.我们需要一个SecurityManager对象来管理所有用户的权限
3.我们需要ini配置文件配置获得Realm对象
4.我们需要在Realm进行权限验证以及授权

3.3.配置步骤说明

第一步:导入包
第二步:创建shiro.ini配置文件
第三步:创建入口的测试类对象
第四步:创建Realm对象
第五步:配置shiro.ini调用Realm对象

3.4.权限校验-配置入门

3.4.1.第一步:导入包

在这里插入图片描述

3.4.2.第二步:创建shiro.ini配置文件

shiro.ini配置文件

3.4.3.第三步:编写一个测试类

package cn.gzsxt.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

public class PermissionTest {
	
	@Test
	public void authc(){
		
		try {
			//第一步:获得SecurityManager对象
			IniSecurityManagerFactory ismf=new IniSecurityManagerFactory("classpath:shiro.ini");
			SecurityManager securityManager = ismf.createInstance();
			
			
			//第二步:构造一个subject
			SecurityUtils.setSecurityManager(securityManager);
			
			Subject subject = SecurityUtils.getSubject();
			
			//第三步:封装用户名密码(身份信息)
			UsernamePasswordToken token=new UsernamePasswordToken("zhangsan", "123456");
			
			//第四步:校验(登录)
			Subject resultSubject = securityManager.login(subject, token);
			
			
			//第五步:验证是否通过
			System.out.println(resultSubject.isAuthenticated());
		} catch (AuthenticationException e) {
			e.printStackTrace();
		}
	    
	    
		
	}
}

第四步:编写Realm

package cn.gzsxt.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * 自动一个MyRealm类,用于权限验证以及权限授予
 * 
 * @author ranger
 *
 */
public class MyRealm extends AuthorizingRealm {

	/**
	 * 权限验证 所谓的权限验证,就是验证访问者(subject).是否使用有使用权限的身份。 说白了,就验证用户名密码是否正确
	 * 
	 * 如果校验成功,我们就返回AuthenticationInfo对象 如果校验失败,我们需要返回一个异常
	 * UnknownAccountException
	 * 
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("用户名:" + token.getPrincipal());
		if (token.getPrincipal().equals("zhangsan")) {
			return new SimpleAuthenticationInfo(token.getPrincipal(), "123456",
					this.getName());
		
		} else {
			return null;
		}
	}

	/**
	 * 授权 根据通过校验的身份(subject),说白了就是登录成功的访问者,我们给予什么权限。
	 * 将查询到的权限信息封装在AuthorizationInfo里面返回!!
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection collection) {
		return null;
	}

}

3.4.5.加密

需求:我们的密码是明文的,我们需要将密码Md5加密。
问题:我们发现我们自己写的Md5的类,无法传递给SimpleAuthenticationInfo对象,作为密码校验。如何解决的这个问题呢?

答:shiro框架自带的密码加密的功能。

1.SimpleHash类:用于生成指定的Hash算法。

在这里插入图片描述

2.HashedCredentialsMatcher类:用于让Realm校验时,校验指定的Hash算法
在这里插入图片描述
3.ByteSource 用于给Hash算法加盐的

在这里插入图片描述

4.–创建Md5密码

package cn.gzsxt.test;

import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;

//注意事项:设置的创建密码的参数必须与校验器的参数保持一致
public class CreatePasswordUtils {
	
	public static void main(String[] args) {
	   //加密方式
        String hashAlgorithmName = "MD5";
        //明文密码
        Object credentials = "123456";
        //盐值
        Object salt = ByteSource.Util.bytes("nchu234we");
        //迭代加密的次数,
        int hashIterations = 1;
        //返回加密的结果
        Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
        //加密后的密码
        System.out.println(result);
	}

}

5.修改配置文件

[main]
# 对象名 =类全限制名 ,就是创建一个对象
myRealm = cn.gzsxt.realm.MyRealm

#加密的对象
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#设置加密的算法为md5 ,属性对应的是set方法
credentialsMatcher.hashAlgorithmName=md5
#设置md5加密次数为1次
credentialsMatcher.hashIterations=1
#设置md5算法是加盐
credentialsMatcher.hashSalted=true

#将加密对象指定给对应给myRealm对象
myRealm.credentialsMatcher=$credentialsMatcher


#将realm对象加入到securityManager容器里面
#引用 securityManager.realms = $对象名
#securityManager对象名是shiro固定的。用于指定securityManager容器
#对象的引用格式为: 对象名.属性名 = $对象名;
securityManager.realms = $myRealm

6.修改校验代码

package cn.gzsxt.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**
 * Realm :所有Realm的父接口
 * AuthenticatingRealm :只有权限校验,没有权限授权的Realm
 * AuthorizingRealm : 既有权限校验,权限授权的Realm (主要就是使用它)
 * JdbcRealm:使用实现的对数据库 操作的代码的Realm。它已经写死了表名,必要要按它定义的表名定义数据库表。(灵活性差)
 * 
 * 注意事项:使用Realm,将验证认证信息与获得授权信息,都放在一起!!!!!
 * @author ranger
 * 
 *
 */
public class MyRealm extends AuthorizingRealm {
	
	/**
	 * 用于权限校验
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		try {
			System.out.println("=权限校验=");
			
			//第一步:获得请求过来的用户名
			Object principal = token.getPrincipal();
			
			//第二步:根据用户查询数据库,返回用户信息 (模拟数据,查询到了"admin")
			if("admin".equals(principal)){
				//第三步:如果用户名相同,校验密码,如果密码正确,不会报异常,如果校验不通过就报异常
				//参数1:用户名
				//参数2:校验的密码,从数据库查询出来的
				//参数3:realm的名字 。随便设置一个唯一字符串
			     
				//4). md5加密盐值
		        ByteSource salt = ByteSource.Util.bytes("nchu234we");
		        
				SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(principal,"8763d15228c560fed665e1fe73b2f601",salt,this.getName());
				return authenticationInfo;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 权限授权
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) {
		return null;
	}
}

3.5.权限授予-配置入门

3.5.1.第一步:检验Subject对象的权限校验方法

package cn.gzsxt.test;

import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;

/**

  • 注意:根据示例
  • 1.身份信息是放在Subject这个身份对象里面的
  • 2.Shiro的所有对象是通过securityManager来管理
  • 3.可以在配置文件配置,认证的信息(数据库里面的表数据)
  • @author ranger

*/
public class ShiroTest {

public static void main(String[] args) {
	//第一步:读取配置文件
	IniSecurityManagerFactory factory= new IniSecurityManagerFactory("classpath:shiro.ini");
	//第二步:获得安全管理器
	SecurityManager securityManager = factory.createInstance();
	//第三步:通过安全管理器帮助类构建一个Subject(身份)
	SecurityUtils.setSecurityManager(securityManager);
	Subject subject = SecurityUtils.getSubject();
	
	//第四步:构建身份信息(用户名/密码)
	UsernamePasswordToken token = new  UsernamePasswordToken("admin","123456");
	
	//第五步:校验是否成功,将一个空的身份,以及身份信息通过登录方法,如果成功返回带身份信息的Subject对象。如果不成功就报异常
	try {
		Subject result = securityManager.login(subject, token);
		System.out.println("认证成功,用户名"+result.getPrincipals());
		
		boolean flag = result.isAuthenticated();
		//使用校验方法,校验用户对应的角色
		boolean hasRole = result.hasRole("roleAdmin");
		System.out.println("是否有指定的角色:"+hasRole);
		List<String> hasRoles=new ArrayList<>();
		hasRoles.add("roleAdmin");
		hasRoles.add("roleEdu");
		boolean allRoles = result.hasAllRoles(hasRoles);
		System.out.println("是否都包括集合的所有角色:"+allRoles);
		
		//校验用户是否有对应的权限
		boolean permitted = result.isPermitted("user:add");
		System.out.println("是否有user:add权限:"+permitted);
		
		boolean permitted2 = result.isPermitted("modular:add");
		System.out.println("是否有modular:add权限:"+permitted2);
		
		System.out.println(flag);
	} catch (AuthenticationException e) {
		System.out.println("用户名或者密码出错");
		e.printStackTrace();
	}
}

}

—配置文件

#users标签:用于指定用户信息,以及用户的角色
#注意:shiro的支持一个用于有多个角色的
#用户名= 密码, 角色1, 角色2, …, 角色N
#如果出现多个角色,用于取的是角色的并集
[users]
   admin = 123456,roleAdmin,roleEdu
#roles标签:用于设置角色的信息,包括的角色以及权限
#权限字符串的格式为:模块名:操作:操作...  类似我们的子权限的设置  
[roles]
   roleAdmin = user:query,user:add,user:delete,modular:*
   roleEdu = edu:query,edu:add

3.5.2.第二步:在Realm授权

后期可以用spring mvc中的接口注入
package cn.gzsxt.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import cn.gzsxt.javabean.ActiveUser;
import cn.gzsxt.javabean.Role;

/**
 * Realm :所有Realm的父接口
 * AuthenticatingRealm :只有权限校验,没有权限授权的Realm
 * AuthorizingRealm : 既有权限校验,权限授权的Realm (主要就是使用它)
 * JdbcRealm:使用实现的对数据库 操作的代码的Realm。它已经写死了表名,必要要按它定义的表名定义数据库表。(灵活性差)
 * 
 * 注意事项:使用Realm,将验证认证信息与获得授权信息,都放在一起!!!!!
 * @author ranger
 * 
 *
 */
public class MyRealm extends AuthorizingRealm {
	
	/**
	 * 用于权限校验
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		try {
			System.out.println("=权限校验=");
			
			//第一步:获得请求过来的用户名
			Object principal = token.getPrincipal();
			
			//第二步:根据用户查询数据库,返回用户信息 (模拟数据,查询到了"admin")
			if("admin".equals(principal)){
				//第三步:如果用户名相同,校验密码,如果密码正确,不会报异常,如果校验不通过就报异常
				//参数1:用户名
				//参数2:校验的密码,从数据库查询出来的
				//参数3:realm的名字 。随便设置一个唯一字符串
				
				ActiveUser user=new ActiveUser();
				user.setUserId(1);
				user.setAge(20);
				user.setPassword("8763d15228c560fed665e1fe73b2f601");
				user.setUsername("admin");
				user.setStatus(0);
			     
				//4). md5加密盐值
		        ByteSource credentialsSalt = ByteSource.Util.bytes("nchu234we");
		        //参数1:用于设置认证信息,返回给调用对象的
		        //参数2:校验的密码
		        //参数3:如果配置了Md5加密,而且设置了需要加盐,该参数就是密码的盐
		        //参数4:指定realm的名字,随便写个唯一的字符串就可以。建议直接使用this.getName();
				SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user,"8763d15228c560fed665e1fe73b2f601",credentialsSalt,this.getName());
				return authenticationInfo;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
		return null;
	}

	/**
	 * 权限授权,必须在通过验证后才执行的方法
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) {
		
		System.out.println("===权限授予====");
		//获得当前通过验证的用户,为什么可以获得校验后的用户信息呢?
		//答:因为就是如果通不过校验,肯定就会有有授权,进入了授权肯定就有校验的认证用户了
		
		ActiveUser user=(ActiveUser) token.getPrimaryPrincipal();
		
		
		SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
		
		authorizationInfo.addStringPermission("user:query");
		authorizationInfo.addStringPermission("user:add");
		authorizationInfo.addStringPermission("user:delete");
		authorizationInfo.addStringPermission("user:edit");
		authorizationInfo.addStringPermission("modular:*");
		
		authorizationInfo.addRole("roleAdmin");
		
		//将权限设置就是里面
		role.setPermisssion(authorizationInfo.getStringPermissions());

		return authorizationInfo;
	}
}

4.总结

1.Shiro是什么
一个权限控制框架。
2.Shiro的作用是什么
就是在实现Rbac系统的时候,使用它来做权限验证,可以减少我们的开发的代码。
3.我们将使用的API记住
IniSecurityManagerFactory : 用于加载配置文件,创建SecurityManager对象
SecurityManager :就是整个Shiro的控制对象
SecurityUtils :SecurityManager 工具类,用于获得Subject对象
Subject :身份类
UsernamePasswordToken 身份信息构建类 (Token 令牌)
AuthorizingRealm 支持校验与授权的Realm
AuthenticationInfo 校验成功返回的信息的父类
SimpleAuthenticationInfo 校验成功返回信息类
Md5Hash Md5加密类
ByteSource 用于构造Md5加盐的类。
HashedCredentialsMatcher Md5算法校验器,用于支持Md5校验
AuthorizationInfo 授权成功返回的信息类的父类
PrincipalCollection 授予是获得验证信息的类
SimpleAuthorizationInfo 授权成功返回的信息类的实现类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值