JAVAWEB开发之权限管理(三)——shiro与企业项目整合开发(基于Spring)

本文详细介绍了如何在企业项目中整合Shiro进行权限管理。内容涵盖Shiro与Spring Web项目的集成,包括web工程的创建、取消SpringMVC拦截器、Shiro过滤器配置、自定义 Realm 实现认证和授权,以及缓存、session管理和验证码功能的集成。同时,还讲解了RememberMe功能的实现。最后,提供了项目代码供读者参考。
摘要由CSDN通过智能技术生成

原理回顾

什么是权限管理?
权限管理是系统的安全范畴,要求必须是合法的用户才可以访问系统(用户认证),且必须具有该 资源的访问权限才可以访问该 资源(授权)。
认证:对用户合法身份的校验,要求必须是合法的用户才可以访问系统。
授权:访问控制,必须具有该 资源的访问权限才可以访问该 资源。
权限模型:标准权限数据模型包括 :用户、角色、权限(包括资源和权限)、用户角色关系、角色权限关系。
权限分配:通过UI界面方便给用户分配权限,对上边权限模型进行增、删、改、查操作。
权限控制:
  • 基于角色的权限控制:根据角色判断是否有操作权限,因为角色的变化 性较高,如果角色修改需要修改控制代码,系统可扩展性不强。
  • 基于资源的权限控制:根据资源权限判断是否有操作权限,因为资源较为固定,如果角色修改或角色中权限修改不需要修改控制代码,使用此方法系统可维护性很强。建议使用。
权限管理的解决方案:
  • 对于粗颗粒权限管理,建议在系统架构层面去解决,写系统架构级别统一代码(基础代码)。所谓粗颗粒权限,比如对系统的url、菜单、jsp页面、页面上按钮、类方法进行权限管理,即对资源类型进行权限管理。
  • 对于细颗粒权限管理:所谓细颗粒权限, 比如用户id为001的用户信息(资源实例)、类型为t01的商品信息(资源实例),对资源实例进行权限管理,理解对数据级别的权限管理。细颗粒权限管理是系统的业务逻辑,业务逻辑代码不方便抽取统一代码,建议在系统业务层进行处理。
基于url的权限管理(掌握):
  • 企业开发常用的方法,使用web应用中filter来实现,用户请求url,通过filter拦截,判断用户身份是否合法(用户认证),判断请求的地址是否是用户权限范围内的url(授权)。
shiro:shiro是一个权限管理框架,是apache下的开源项目。相比spring security框架更简单灵活,spring security对spring依赖较强。shiro可以实现web系统、c/s、分布式等系统 权限管理。
shiro认证流程:(掌握)
  • 1.subject(主体)请求认证,调用subject.login(token)
  • 2.SecurityManager (安全管理器)执行认证
  • 3.SecurityManager通过ModularRealmAuthenticator进行认证。
  • 4.ModularRealmAuthenticator将token传给realm,realm根据token中用户信息从数据库查询用户信息(包括身份和凭证)
  • 5.realm如果查询不到用户给ModularRealmAuthenticator返回null,ModularRealmAuthenticator抛出异常(用户不存在)
  • 6.realm如果查询到用户给ModularRealmAuthenticator返回AuthenticationInfo(认证信息)
  • 7.ModularRealmAuthenticator拿着AuthenticationInfo(认证信息)去进行凭证(密码 )比对。如果一致则认证通过,如果不致抛出异常(凭证错误)。
subject:主体
Authenticator:认证器( shiro提供)
realm(一般需要自定义):相当于数据源,认证器需要realm从数据源查询用户身份信息及权限信息。

shiro与项目集成开发

shiro与spring  web项目整合

shiro与spring web项目整合在"基于url拦截实现的工程" 基础上整合,基于url拦截实现的工程的技术架构师SpringMVC+Mybatis,整合时注意两点:
1.shiro与Spring整合
2.加入shiro对web应用的支持

创建web工程

创建新工程permission_web_shiro

取消原springmvc认证和授权拦截器

去除springmvc.xml中配置的LoginInterceptor和PermissionInterceptor拦截器

导入shiro相应的jar包

主要包括:
shiro-web:shiro整合web项目必须的包
shiro-spring:shiro整合Spring项目需要的整合包,依赖于shiro-web包
shiro-core:shiro核心包。必须导入的包。



在web.xml中配置shiro的filter

在web系统中,shiro也通过filter进行拦截,filter拦截器后将操作权交给Spring中配置的filterChain(过滤器链),shiro提供很多filter。要使用代理filter类DelegatingFilterProxy

<!-- shiro的filter -->
	<!-- shiro过滤器,DelegatingFilterProxy通过代理模式将Spring容器中的bean和filter关联起来 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<!-- 设置targetFilterLifecycle为true 由servlet控制filter生命周期 -->
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
		<!-- 设置Spring容器filter的bean id,如果不设置则在Spring注册的bean中查找与filter-name一致的bean -->
		<init-param>
			<param-name>targetBeanName</param-name>
			<param-value>shiroFilter</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

applicationContext-shiro.xml

<!-- web.xml中shiro的filter对应的bean -->
	<!-- Shiro的web过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager"/>
		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
		<property name="loginUrl" value="/login.action"/>
		<!-- 认证成功统一跳转到first.action,建议不配置,默认情况下,shiro认证成功后自动跳转上一个请求路径 -->
		<property name="successUrl" value="/first.action"/>
		<!-- 通过unauthorizedUrl 指定没有权限操作时的跳转页面 -->
		<property name="unauthorizedUrl" value="/refuse.jsp"/>
		<!-- 过滤器链定义,从上向下执行,一般将/**放在最下边 -->
		<property name="filterChainDefinitions">
		    <value>
				<!-- 退出拦截,请求logout.action执行退出操作 shiro自动清除Session-->
				/logout.action = logout
				<!-- 无权访问页面 anon表示可以匿名访问 -->
			    /refuse.jsp = anon
			    <!-- 验证码可以匿名访问 -->
			    /validatecode.jsp = anon
			    <!-- perms[xx] 表示有xx权限才可以访问 -->
			    /item/queryItem.action = perms[item:query]
			    /item/editItem.action = perms[item:edit]
			    <!-- 对静态资源设置匿名访问 -->
			    /js/** = anon
			    /images/** = anon
			    /styles/** = anon
			    
			    <!-- /** = authc 表示所有的URL都必须认真通过才可以进行访问-->
			    /** = authc	
		    </value>		
		</property>
	</bean>
	
	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="customRealm" />
	</bean>
	
	<!-- 自定义Realm -->
	<bean id="customRealm" class="liuxun.ssm.shiro.CustomRealm"/>
securityManager:这个属性是必须的
loginUrl:没有登录认证的用户请求将跳转到此地址进行认证,不是必须的属性,不输入地址的话会自动寻找web项目的根目录下的"login.jsp" 页面

自定义Realm模拟测试

此Realm先不从数据库查询权限数据,当前需要先将shiro整合完成。
package liuxun.ssm.shiro;

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

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.authc.UsernamePasswordToken;
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 liuxun.ssm.po.ActiveUser;
import liuxun.ssm.po.SysPermission;

public class CustomRealm extends AuthorizingRealm {

	// 设置Realm名称
	@Override
	public void setName(String name) {
		super.setName("CustomRealm");
	}

	// 支持UsernamePasswordToken
	@Override
	public boolean supports(AuthenticationToken token) {
		return token instanceof UsernamePasswordToken;
	}

	// 用于认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// 从token中获取用户身份信息
		String username = (String) token.getPrincipal();
		// 拿着username从数据库中进行查询
		// ....
		// 如果查询不到返回null
		if (!username.equals("zhangsan")) {
			return null;
		}

		// 获取从数据库查询出来的用户密码
		String password = "123"; // 这里使用静态数据进行测试

		// 根据用户id从数据库中取出菜单
		// ...先使用静态数据
		List<SysPermission> menus = new ArrayList<SysPermission>();
		SysPermission sysPermission_1 = new SysPermission();
		sysPermission_1.setName("商品管理");
		sysPermission_1.setUrl("/item/queryItem.action");
		SysPermission sysPermission_2 = new SysPermission();
		sysPermission_2.setName("用户管理");
		sysPermission_2.setUrl("/user/query.action");

		menus.add(sysPermission_1);
		menus.add(sysPermission_2);

		// 构建用户身份信息
		ActiveUser activeUser = new ActiveUser();
		activeUser.setUserid(username);
		activeUser.setUsername(username);
		activeUser.setUsercode(username);
		activeUser.setMenus(menus);

		// 返回认证信息由父类AuthenticationRealm进行认证
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(activeUser, password,
				this.getName());

		return simpleAuthenticationInfo;
	}

	// 用于授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//获取身份信息
		ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
		//用户id
		String userid = activeUser.getUserid();
		// 根据用户id从数据库中查询权限数据
		// ...这里使用静态数据模拟
		List<String> permissions = new ArrayList<String>();
		permissions.add("item:query");
		permissions.add("item:update");
		
		//将权限信息封装为AuthorizationInfo
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		//基于资源权限的访问控制
		for (String permission : permissions) {
			simpleAuthorizationInfo.addStringPermission(permission);
		}
		// 如果基于角色进行访问控制
		// for (String role : roles) {
		// simpleAuthorizationInfo.addRole(role);
		// }
		
		return simpleAuthorizationInfo;
	}

}

登录

(1) 登录原理
使用FormAuthenticationFilter过滤器实现,原理如下:
当用户没有认证时,请求loginUrl进行认证,用户身份和用户密码提交数据到loginUrl,FormAuthorizationFilter拦截去除request中的username和password(两个参数名称是可以配置的),FormAuthorizationFilter调用Realm传入一个token(username和password),realm认证时根据username查询用户信息(在ActiveUser中存储,包括userid、usercode、username、menus) 如果查询不到,Realm返回null,FormAuthorizationFilter向request域中填充了一个参数"shiroLoginFailure" 记录了异常信息。
(2) 登录页面
由于FormAuthorizationFilter的用户身份和密码的input的默认值(username和password),修改页面中账号和密码的input的name属性为username和password。
(3)控制器登录代码的实现,如下:
//用户登录提交方法
	@RequestMapping("/login")
	public String login(HttpServletRequest request) throws Exception{
	   
		//shiro在认证通过后出现错误后将异常类路径通过request返回
		//如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
		String exceptionClassName = (String)request.getAttribute("shiroLoginFailure");
		if (exceptionClassName!=null) {
			if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
				throw new CustomException("账号不存在");
			} else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)){
				throw new CustomException("用户名/密码错误");
			}else {
				throw new Exception(); //最终在设置的异常处理器中生成未知错误
			}
		}
		//此方法不处理登录成功(认证成功)的情况
		//如果登录失败还到login页面
		return "login";
	}

首页

1.认证成功后用户菜单在首页显示(从activeUser获取)
2.认证后用户的信息在页头显示(从activeUse
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值