Java全栈开发---Java ERP系统开发:商业ERP(十五)ERP系统的权限验证框架Shiro

权限验证框架Shiro

一、Shiro简介

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

三个核心组件: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实现。

二、Shiro内置的过滤器

在这里插入图片描述
在这里插入图片描述

三、ERP整合Shiro

1、添加依赖

(1)给erp_parent父工程添加依赖

定义版本号:
在这里插入图片描述

<shiro.ver>1.2.3</shiro.ver>

在这里插入图片描述

		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>${shiro.ver}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-web</artifactId>
			<version>${shiro.ver}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>${shiro.ver}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-aspectj</artifactId>
			<version>${shiro.ver}</version>
		</dependency>

(2)配置web.xml,添加过滤器代理DelegatingFilterProxy,要放在struts2的核心过滤器之间

在这里插入图片描述

	<!-- shiro过滤器 -->
	<filter>
		<!-- 代理过滤器,指向spring容器当中的id=shiroFilter的Bean来处理 -->
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>*.action</url-pattern>
		<url-pattern>*.html</url-pattern>
		<!-- 拦截没有后缀名的 -->
		<url-pattern>*</url-pattern>
	</filter-mapping>

(3)添加shiro核心控制器的spring配置文件

1)创建applicationContext_shiro.xml文件到erp_web的资源目录下

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- shiro的过滤工厂,相当默认的加载了9个过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- 安全管理器,shiro核心组件(大脑) Facade模式 -->
		<property name="securityManager" ref="securityManager" />
		<property name="filters">
			<map>
				<entry key="perms" value-ref="erpAuthorizationFilter"></entry>
			</map>
		</property>
		<!-- 用户如果没有登陆,当他在访问资源的时候,就会自动跳转到登陆的页面 -->
		<property name="loginUrl" value="/login.html" />
		<!-- 当用户没有访问某项资源权限的时候,跳转到该页面 -->
		<property name="unauthorizedUrl" value="/error.html" />
		<!-- 过滤链的定义:定义URL访问的时候对应的认证或授权时处理的过滤器 -->
		<property name="filterChainDefinitions">
			<value>
				/error.html = anon
				/login_*.action = anon
				/login_*=anon
				
				/*_list=perms[]
				
				/dep_*=perms["部门"]
				/supplier.html=perms["供应商管理","客户管理"]
				/supplier_*=perms["供应商管理","客户管理"]
				/role.html=perms["角色设置"]
				/role_*=perms["角色设置","角色菜单设置"]
				/emp.html=perms["员工"]
				/emp_*=perms["用户角色设置","重置密码"]
				/goodstype.html=perms["商品类型"]
				/goodstype_*=perms["商品类型"]
				/goods.html=perms["商品"]
				/goods_*=perms["商品"]
				/store.html=perms["仓库"]
				/store_*=perms["仓库"]
				/dep.html=perms["部门管理"]
				/dep_*=perms["部门管理"]
				/orders.html=perms["我的采购订单","采购订单申请","采购订单查询","采购订单审核","采购订单确认","采购订单入库","销售订单查询","销售订单录入","销售订单出库"]
				/orders_*=perms["我的采购订单","采购订单申请","采购订单查询","采购订单审核","采购订单确认","采购订单入库","销售订单查询","销售订单录入","销售订单出库"]
				/report_order.html=perms["销售统计报表"]
				/report_*=perms["销售统计报表","销售趋势报表"]
				/report_trend.html=perms["销售趋势报表"]
				
				
				/*.html = authc
				/*.action=authc
				/*=authc
			</value>
		</property>
	</bean>

	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		
	</bean>

</beans>
2)创建error.html

在这里插入图片描述

2、认证

(1)需求分析

判断当前用户是否登录,如果没有登录侧跳转到登录页面

(2)认证现实(重点)

1)Subject的login方法

修改LoginAction的checkUser方法
步骤:
创建令牌:UsernamePasswordToken
获取subject
执行subject.login()方法
在这里插入图片描述

public void checkUser() {
    
    
		// 在登录的时候可能会发送异常
		try {
    
    
			/*
			 * // 查询是否存在 Emp loginUser = empBiz.findByUsernameAndPwd(username, pwd);
			 * System.out.println(loginUser+"emp"); if (loginUser != null) { // 记录当前登录的用户
			 * ActionContext.getContext().getSession().put("loginUser", loginUser);//
			 * 先把用户的信息放到session当中 ajaxReturn(true, "用户名或密码正确"); } else { ajaxReturn(false,
			 * "用户名或密码不正确"); }
			 */

			// 1、创建令牌(通行证)身份认证,身份证明
			UsernamePasswordToken upt = new UsernamePasswordToken(username, pwd);
			// 2、获取主机subject:封装当前用户的操作
			Subject subject = SecurityUtils.getSubject();
			// 3、指向login
			subject.login(upt);
			ajaxReturn(true, "用户名或密码正确"); 
		} catch (Exception e) {
    
    
			e.printStackTrace();
			ajaxReturn(false, "登录失败");
		}
	}

3、自定义Realm

我们改用subject.login方法后,并不会调用登陆的业务层进行登陆的验证查询,即不会从数据库查找登陆的用户名和密码是否正确,而是将这项工作交给 shiro去完成。

那 shiro,是怎么知道登陆的用户名和密码是否正确的呢?其实它也需要用到我的登陆验证业务,这时它就得向“别人”打听一下,那就是Realm了。

真正实现登陆验证的是Realm,而shiro只是去调Realm

Realm: Realm 充当了 Shiro与应用安全数据间的“桥梁"或者"连接器"。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的 Realm中查找用户及其权限信息。

从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的逐接细节,并在需要时将相关数据提供给 Shiro。当配置 Shiro,时,你必须至少指定一个 Realm,用于认证和或授权。
配置多个Realm 是可以的,但是至少需要一个。

1) 在erp_web子工程下创建包 com.itzheng.erp.realm:

在这里插入图片描述

2)创建ErpRealm类继承自AuthorizingRealm

在这里插入图片描述
在这里插入图片描述

package com.itzheng.erp.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class ErpRealm extends AuthorizingRealm {
    
    
	/*
	 * 授权
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
    
		System.out.println("执行的授权的方法。。。");
		return null;
	}
	/*
	 * 认证
	 * return null;认证失败,返回AuthenticationInfo实现类,认证成功
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
		System.out.println("执行的授权的方法。。。");
		return null;
	}
}

3)修改applicationContext_shiro.xml

在这里插入图片描述

<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="erpRealm"></property>
	</bean>
	<!-- 自定义的realm -->
	<bean id="erpRealm" class="com.itzheng.erp.realm.ErpRealm">
		<property name="empBiz" ref="empBiz"></property>
	</bean>

4)修改ErpRealm的doGetAuthenticationInfo方法

在这里插入图片描述

public class ErpRealm extends AuthorizingRealm {
    
    
	private IEmpBiz empBiz;
	/*
	 * 授权
	 */
	public void setEmpBiz(IEmpBiz empBiz) {
    
    
		this.empBiz = empBiz;
	}
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
    
		System.out.println("执行的授权的方法。。。");
		return null;
	}
	/*
	 * 认证 return null;认证失败,返回AuthenticationInfo实现类,认证成功
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
		System.out.println("执行的授权的方法。。。");
		//通过令牌 用户名和密码?
		UsernamePasswordToken upt = (UsernamePasswordToken) token;
		//得到密码
		String pwd = new String(upt.getPassword());
		//调用登录查询
		Emp emp = empBiz.findByUsernameAndPwd(upt.getUsername(), pwd);
		if(null != emp) {
    
    
			//构造参数1,主角=登录用户
			//参数2:授权码
			//参数3,realm的名称
			SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(emp,pwd,getName());
			
			return info;
		}
		return null;
	}
}

5)修改applicationContext_shiro.xml

在这里插入图片描述

6)修改LoginAction,以及对应的配置文件

在这里插入图片描述

/*
	 * 显示登录用户
	 */
	public void showName() {
    
    
		// 获取当前登录的用户
		//Emp emp = (Emp) ActionContext.getContext().getSession().get("loginUser");// 先把用户的信息放到session当中
		// session是否会超时,用户是否登录过
		
		//获取主题
		Subject subject = SecurityUtils.getSubject();
		//提取主角,拿到emp
		Emp emp =(Emp)subject.getPrincipal();
		System.out.println(emp);
		if (null != emp) {
    
    
			System.out.println(emp + "yyyy");
			ajaxReturn(true, emp.getName());
		} else {
    
    
			ajaxReturn(false, "");
		}
	}

在这里插入图片描述


	/*
	 * 退出登录
	 * 
	 */
	public void loginOut() {
    
    
		// ActionContext.getContext().getSession().remove("loginUser");
		SecurityUtils.getSubject().logout();
	}

applicationContext_shiro.xml
在这里插入图片描述

7)修改BaseAction的getLoginUser方法

在这里插入图片描述

/*
	 * 获取登录的用户信息
	 */
	public Emp getLoginUser() {
    
    
		//Emp emp = (Emp) ActionContext.getContext().getSession().get("loginUser");
		return (Emp) SecurityUtils.getSubject().getPrincipal();
	}

4、授权(指定电脑的那些资源可以被访问)

(1)需求分析

授权就是通过设置规则,指定URL需要哪些授权才可以访问
在这里插入图片描述

(2)授权的实现

在这里插入图片描述

1)授权方法与配置(重点)
a、修改ErpRealm的doGetAuthorizationInfo方法

在这里插入图片描述

System.out.println("执行了授权的方法...");
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		// uuid=?怎么来
		Emp emp = (Emp) principals.getPrimaryPrincipal();

		// 获取当前登陆用户的菜单权限
		List<Menu> menuList = empBiz.getMenusByEmpuuid(emp.getUuid());
b、配置权限名称:修改applicationContext_shiro.xml

在这里插入图片描述

<value>
				/error.html = anon
				/login_*.action = anon
				/login_*=anon
				
				/*_list=perms[]
				
				/dep_*=perms["部门"]
				/supplier.html=perms["供应商管理","客户管理"]
				/supplier_*=perms["供应商管理","客户管理"]
				/role.html=perms["角色设置"]
				/role_*=perms["角色设置","角色菜单设置"]
				/emp.html=perms["员工"]
				/emp_*=perms["用户角色设置","重置密码"]
				/goodstype.html=perms["商品类型"]
				/goodstype_*=perms["商品类型"]
				/goods.html=perms["商品"]
				/goods_*=perms["商品"]
				/orders.html=perms["我的采购订单","采购订单申请","采购订单查询","采购订单审核","采购订单确认","采购订单入库","销售订单查询","销售订单录入","销售订单出库"]
				/orders_*=perms["我的采购订单","采购订单申请","采购订单查询","采购订单审核","采购订单确认","采购订单入库","销售订单查询","销售订单录入","销售订单出库"]
				/report_order.html=perms["销售统计报表"]
				/report_*=perms["销售统计报表","销售趋势报表"]
				/report_trend.html=perms["销售趋势报表"]
				
				
				/*.html = authc
				/*.action=authc
				/*=authc
			</value>
2)
3)小结:

授权方法的作用:告诉shiro当前用户有什么权限
配置信息的作用:告诉shiro什么资源有什么权限才可以被访问

5、自定义授权过滤器

(1)创建filter包以及ErpAuthorizationFilter类继承AuthorizationFilter

在这里插入图片描述

package com.itzheng.erp.filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
/**
 * 自定义授权过滤器
 *
 */
public class ErpAuthorizationFilter extends AuthorizationFilter {
    
    
	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
			throws Exception {
    
    
		// 获取主题
		Subject subject = getSubject(request, response);
		/// orders.html=perms["采购订单的查询","采购订单的审核","采购订单的确认","采购订单的入库"]
		// mappedValue="采购订单的查询","采购订单的审核","采购订单的确认","采购订单的入库"
		String[] perms = (String[]) mappedValue;
		boolean isPermitted = true;
		if (null == perms || perms.length == 0) {
    
    
			return isPermitted;
		}
		if (perms != null && perms.length > 0) {
    
    
			/*
			 * 原有的代码 if (perms.length == 1) { if (!subject.isPermitted(perms[0])) {
			 * isPermitted = false; } } else { if (!subject.isPermittedAll(perms)) {
			 * isPermitted = false; } }
			 */
			for (String perm : perms) {
    
    
				// 只要有一个权限,就返回true
				if (subject.isPermitted(perm)) {
    
    
					return true;
				}
			}
		}
		return false;
	}
}

(2)过滤器的配置和使用

在这里插入图片描述
在这里插入图片描述

<property name="securityManager" ref="securityManager" />
		<property name="filters">
			<map>
				<entry key="perms" value-ref="erpAuthorizationFilter"></entry>
			</map>
		</property>

猜你喜欢

转载自blog.csdn.net/qq_44757034/article/details/112362973