在线办公系统之二 整合Shiro框架

为什么要用Shiro?

什么是shiro

Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。

为什么要学shiro

既然shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。
shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。
java领域中spring security(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro。

其实上面两点都比较官方,但是的确,做完这个项目过后,我感觉到了SHIRO的强大,在还没接触类似安全认证框架之前,写个登陆,每次都得做大量的业务操作,假设没有安全性框架,我们就不能做项目了?并不是,为了保证用户的登陆认证的安全性,我们就必须花费大量时间在用户名的唯一性,密码加密以及密码认证逻辑上,虽然这些对熟手的程序员造成不了什么麻烦,但是其实也是时间消耗,效率会降低,所以加入Shiro之后,我们可以撇除这部分的操作,特别是权限授予的时候。所以SHIRO为什么能够在如今的框架内加入,主要是为了给程序员提速,让我们更专注解决业务上的逻辑。当然,他还有如下的优点:
1、作为安全验证框架,使用起来比spring-security方便
2、提供web支持,可以在jsp中通过shiro标签方便的做到细粒度的权限管控
3、可以直接使用annotation对所使用的方法做权限管控,节省代码。
4、可扩展,可插拔。

配置Shiro

那么知道为什么要用Shiro框架之后,我们就要知道怎么用了。
配置文件如下:

  • applicationContext-shiro.xml
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
		http://www.springframework.org/schema/mvc 
		http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-3.2.xsd 
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
	
	<!-- 开启aop,对类代理 -->
	<aop:config proxy-target-class="true"></aop:config>
	<!-- 开启shiro注解支持 -->
	<bean  class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager" />
	</bean>

	<!-- 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" />
		<!-- 自定义filter配置 -->
		<property name="filters">
			<map>
				<!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中 -->
				<entry key="authc" value-ref="formAuthenticationFilter" />
			</map>
		</property> 
		<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
		<property name="filterChainDefinitions">
			<value>
			    <!-- 所有的静态资源要匿名访问 -->
				/js/**=anon
			    /bootstrap/**=anon
			    /static/**=anon
			    /css/**=anon
				<!--授权过滤器  , 建议使用注解授权或在jsp页面使用标签 ,USER字段的,permission都能过,因为权限已经是包含了已经被认证的用户信息-->
			 	/toapply_expense.action=perms[expence:apply]
				/myExpenseBill.action=perms[expence:billquery]
				/myExpenseTaskList.action=perms[expence:tasklist]
						
				/toapply_process.action=perms[expence:publish]
				/processDefinitionList.action=perms[expence:processlist]
				
				/findUserList.action=perms[user:query]
				/toAddRole.action=perms[user:create]
				/delDeployment.action=perms[expence:remove]
				/viewImage.action=perms[expence:viewimage]
				/findRoles.action=perms[user:rolelist]
				/delDeployment.action=perms[expence:delprocess]
			    <!-- 验证码jsp文件,匿名访问 -->
				/validatecode.jsp=anon
				/first.action=user
			    /index.jsp=user
			    /welcome.jsp=user
			    <!-- 退出系统 -->
			    /logout.action=logout
				/**=authc
				
			</value>
		</property>
	</bean>

	<!-- securityManager安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="customRealm" />
		<property name="cacheManager" ref="cacheManager"/>
		<property name="rememberMeManager" ref="rememberMeManager"/>
		
	</bean>

	<!-- realm -->
	<bean id="customRealm" class="com.activiti.ssm.shiro.CustomRealm">
		<!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
		<property name="credentialsMatcher" ref="credentialsMatcher"></property>
	</bean>
	
	<!-- 凭证匹配器 -->
	<bean id="credentialsMatcher"
		class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
		<property name="hashAlgorithmName" value="md5" />
		<property name="hashIterations" value="2" />
	</bean>
	
	   <!-- 自定义form认证过虑器 -->
	<!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
	<bean id="formAuthenticationFilter" class="com.activiti.ssm.shiro.CustomFormAuthenticationFilter">
		<!-- 表单中账号的input名称 -->
		<property name="usernameParam" value="username" />
		<!-- 表单中密码的input名称 -->
		<property name="passwordParam" value="password" />
   </bean>	
   
      	<!-- 缓存管理器 -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    	<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
    </bean>
   
   	<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
		<property name="cookie" ref="rememberMeCookie" />
	</bean>
   
	<!-- 记住我cookie -->
	<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
		<!-- rememberMe是cookie的名字 -->
		<constructor-arg value="rememberMe" />
		<!-- 记住我cookie生效时间30天 -->
		<property name="maxAge" value="2592000" />
	</bean>
	
</beans>

当然,一般配置文件都有核心部分,如果用类似Spring的框架理解的话,Shiro也不难理解,里面最核心的就是shiroFilter以及开启securityManager,从命名就可以知道,其实Shiro也是一个很大的拦截器

		<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" />
		<!-- 自定义filter配置 -->
		<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
		<property name="filterChainDefinitions">

这几个属性可以说是整个filter中的重中之重,分别对应登陆、成功登陆之后怎么做以及未授权跳转的页面。

当然,securityManager中最重要的就是数据域,数据域负责管理与数据库对接的用户名与密文,用户输入用户名与密码之后,通过用户名进行口令匹配,找到对应的用户,我们也就能从securityUtils中获取主体,进一步获取pricipal,在后台中随时获取对应的用户,这点类似Session的获取。

为什么要加缓存?

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<!--diskStore:缓存数据持久化的目录 地址  -->
	<diskStore path="d:\develop\ehcache" />
	<defaultCache 
		maxElementsInMemory="1000" 
		maxElementsOnDisk="10000000"
		eternal="false" 
		overflowToDisk="false" 
		diskPersistent="false"
		timeToIdleSeconds="120"
		timeToLiveSeconds="120" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
</ehcache>

对于这里的理解,我们可以类比一下AJAX的异步,其实当我们页面的数据量很大,但是访问频率也很高同时内容变化比较小的时候,我们一般可以采取异步刷新的方法去进行分页输出,而缓存其实他的存在,很大程度上也是为了解决对应的问题,因为Shiro能很好的配合JSP进行精细粒度的权限控制。

<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro"%>
<shiro:hasPermission name="item:view">
	<a href="${pageContext.request.contextPath }/item/queryItem/${item.id}">商品查看</a>
	</shiro:hasPermission>

上述代码就是通过页面的taglib标签库进行Shiro指令的引入,判断具有item:view的权限值的用户才能看到这个标签,否则这个a标签会在页面消失,对于这种方式的权限控制,如果我们在JSP的循环中,多项读出,则会造成卡顿,所以我们采取缓存,其实我上面说类比,并不是类比他们之间的技术,而是类比他们的目的,其实都是为了提高系统的整体性能。
可以参照:
https://blog.csdn.net/he90227/article/details/77976022

认证与授权

package com.activiti.ssm.shiro; 

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

import javax.servlet.http.HttpSession;

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 org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;

import com.activiti.ssm.pojo.ActiveUser;
import com.activiti.ssm.pojo.Employee;
import com.activiti.ssm.pojo.Permission;
import com.activiti.ssm.pojo.TreeMenu;
import com.activiti.ssm.service.SysService;

public class CustomRealm extends AuthorizingRealm{
	@Autowired
	private SysService sysService;
	// 认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String userName=(String) token.getPrincipal();
		//高仿数据库姓名密码
		Employee emp=null;
		String userpwd_db=null;
		
		List<TreeMenu> menuTree=null;
		try {
			emp = sysService.findSysUserByUserCode(userName);
			if (emp==null) {
				return null;
			}
		
			userpwd_db=emp.getPassword();
			
			menuTree=sysService.getMenuList();
	
		} catch (Exception e) {
			e.printStackTrace();
		}
		String salt_db=emp.getSalt();
		
		ActiveUser activeUser=new ActiveUser();
		
		activeUser.setUserid(emp.getId());
		activeUser.setUsername(emp.getName());
		activeUser.setRealname(emp.getRealname());
		activeUser.setManagerId(emp.getManagerId());
		activeUser.setMenuTree(menuTree);
		
		
//		第一个参数:表示认证主体的主体信息(一般是User 对象),第二个参数:数据库需要比对的密码
		SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(activeUser,userpwd_db,ByteSource.Util.bytes(salt_db), "CustomRealm");
		return info;
		
		
//		自定义Realm里面的内容,其实就是将自定义域通过依赖的方式注入到安全管理器里面
//		方法1:通过本地的INI文件当做一个数据库文件,然后通过主体读入TOKEN的比对,访问---->ini---->subject.login---->认证结果
//		方法2:通过自定义类,里面进行数据库访问或者自定义一个用户TOKEN,自定义域通过ini写明关系依赖注入到对应的subject中。
//		#自定义Realm  
//	    customRealm=com.gec.shiro.realm.CustomRealm
//		securityManager.realms=$customRealm
	}
	
	// 授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
		//必须是认证的用户才进行授权
		ActiveUser activeUser=(ActiveUser) principal.getPrimaryPrincipal();
		List<Permission> permissionList=null;
		try {
		
			permissionList=sysService.findRoleandPermissionListByUserId(activeUser.getUsername()).getPermissionList();
		
		} catch (Exception e) {
			e.printStackTrace();
		}
		List<String> list=new ArrayList<String>();
		for (Permission sysPermission : permissionList) {
			list.add(sysPermission.getPercode());
		}
	
		
		SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
		//将获取的权限字段放入对应的授权信息中
		info.addStringPermissions(list);
		
		
		return info;
	}


}

上述代码就是叙述Shiro的核心,认证以及授权。当然,其实每一个用户Bean里面的信息都未必是必要的,这时候为了保证我们获取到的用户信息Bean是一个我们需要的模板的时候,我们可以自定义一个ActiveUser,不做赘述。
在登陆进来的那一刻,我们就必须在这两个方法做文章,而授权的前提是,这个传过来的principal是必须通过了认证的,然后才能授权,最终传出一个SimpleAuthorizationInfo 标准的授权信息,通过ShiroFilter中的权限字段,控制对应的用户的权限,在ShiroFilter中有的字段我们才能进行访问,否则一并回到

<property name="unauthorizedUrl" value="/refuse.jsp" />

这里面去。

最后,当然还是要到web.xml那里给门卫打声招呼

	
	<!-- shiro的filter -->
	<!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<!-- 设置true由servlet容器控制filter的生命周期 -->
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
		<!-- 设置spring容器filter的bean id,如果不设置则找与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>

Tips:
1.其实建议在整合整个在线办公系统的时候,建议是先将Activiti整合进去,毕竟在调试的时候,可以免除一些不必要的麻烦,例如每次都要登陆,部分权限控制等,当然,你也可以屏蔽了功能再去做~!
2./viewImage.action=perms[expence:viewimage]例如这样的权限字段,是不能进行等号换行的,会报错,这部分要小心翼翼处理,分毫不差的进行导入
3.当然,我们在嵌入Shiro的过程中,一定要在开启服务器的时候,不能报错,这个是前提,否则,就证明你的Shiro+SSM中没有配置正确

总结

Shiro其实在权限以及认证方面的确做出了很好的全局控制,整体项目下来,Shiro在控制用户权限、密文认证以及用户授权等提供了明确的规定,让我们对应的用户信息更饱满,控制力度更精细,其次Shiro对于权限控制需要高度的与数据库进行紧密配合,这个是在项目设计阶段的时候就需要根据用户需求进行设计,是否需要用Shiro,对于数据库有比较大的影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值