shiro框架作为一种特权的开源框架,通过身份验证和授权从具体的业务逻辑分离极大地提高了我们的发展速度,它的易用性使得它越来越受到人们的青睐。上一页ACL架相比,shiro能更easy的实现权限控制,并且作为基于RBAC的权限管理框架通过与shiro标签结合使用。可以让开发者在更加细粒度的层面上进行控制。

举个样例来讲,之前我们使用基于ACL的权限控制大多是控制到连接(这里的连接大家可以简单的觉得是页面。下同)层面,也就是通过给用户授权让这个用户对某些连接拥有权限。这样的情况显然不太适合详细的项目开发,由于在某些情况下。某个用户可能仅仅对某个连接的某个部分有权限。比方这个连接的页面上有增删改查四个button。而当前登录用户对这个页面有查看的权限。可是没有增删改的权限,假设用之前的基于ACL的权限管理。我们手动控制某个button的显示。某些button的不显示是十分麻烦的,shiro通过标签就非常好的攻克了这个问题。shiro不但能细化控制粒度。并且通过加密算法可以更加安全的保证用户password的安全性。以下结合实例介绍一下shiro的详细使用。

1.spring集成shiro

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"version="2.5"><welcome-file-list><welcome-file>login.jsp</welcome-file></welcome-file-list><!-- 载入spring的配置****begin --><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param><param-name>contextConfigLocation</param-name><param-value>classpath*:config/spring/appCtx-*.xml</param-value></context-param>
<!-- 载入spring的配置****end --><!-- 载入Log4j的配置****begin --><context-param>  <param-name>log4jConfigLocation</param-name>  <param-value>/WEB-INF/classes/log4j.properties</param-value>  </context-param> <listener><listener-class>org.springframework.web.util.Log4jConfigListener</listener-class></listener>
<!-- 载入Log4j的配置****end -->    <!-- 解决Hibernate的Session的关闭与开启问题功能是用来把一个Hibernate Session和一次完整的请求过程相应的线程相绑定。目的是为了实现"Open Session in View"的模式。

比如: 它同意在事务提交之后延迟载入显示所须要的对象 --> <filter> <filter-name>openSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>openSessionInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 载入shiro的配置*********begin***** --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 载入shiro的配置*********end***** --> <!-- 载入struts2的配置******begin****** --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 载入struts2的配置*******end********* --> </web-app>

2.shiro的主要配置文件shiro.xml文件:

<?xml version="1.0" encoding="UTF-8"?

> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd "> <!-- 自己主动扫描载入spring的bean*****begin********* --> <context:annotation-config /> <context:component-scan base-package="com" /> <!-- 自己主动扫描载入spring的bean*****end********* --> <!-- 载入spring的properties文件*****begin********* --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="fileEncoding" value="utf-8" /> <property name="locations"> <list> <value>classpath*:/config/properties/deploy.properties</value> </list> </property> </bean> <!-- 载入spring的properties文件*****end******** --> <!-- 载入数据库的相关连接****************begin********** --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="url" value="${datasource.url}" /> <property name="username" value="${datasource.username}" /> <property name="password" value="${datasource.password}" /> <property name="driverClassName" value="${datasource.driverClassName}"></property> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="${druid.initialPoolSize}" /> <property name="minIdle" value="${druid.minPoolSize}" /> <property name="maxActive" value="${druid.maxPoolSize}" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="${druid.maxWait}" /> <!-- 配置间隔多久才进行一次检測。检測须要关闭的空暇连接。单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" /> <!-- 配置一个连接在池中最小生存的时间。单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" /> <property name="validationQuery" value="${druid.validationQuery}" /> <property name="testWhileIdle" value="${druid.testWhileIdle}" /> <property name="testOnBorrow" value="${druid.testOnBorrow}" /> <property name="testOnReturn" value="${druid.testOnReturn}" /> <!-- 打开PSCache,而且指定每一个连接上PSCache的大小 --> <property name="poolPreparedStatements" value="${druid.poolPreparedStatements}" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="${druid.maxPoolPreparedStatementPerConnectionSize}" /> <!-- 配置监控统计拦截的filters,如需防御SQL注入则增加wall --> <property name="filters" value="${druid.filters}" /> <property name="connectionProperties" value="${druid.connectionProperties}" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- <property name="packagesToScan">--> <!-- <list>--> <!-- <value>com.wenc.*.po</value>--> <!-- </list>--> <!-- </property>--> <property name="packagesToScan" value="com.wenc.core.po" /> <!-- <property name="mappingLocations"> 此处加入Java类和数据库表的映射关系|mappingLocations取代mappingResources --> <!-- <list>--> <!-- <value>classpath:/com/wec/po/**/*.hbm.xml</value> --> <!-- </list>--> <!-- </property>--> <property name="hibernateProperties"> <props> <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.query.substitutions">${hibernate.query.substitutions}</prop> <prop key="hibernate.default_batch_fetch_size">${hibernate.default_batch_fetch_size}</prop> <prop key="hibernate.max_fetch_depth">${hibernate.max_fetch_depth}</prop> <prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop> <prop key="hibernate.bytecode.use_reflection_optimizer">${hibernate.bytecode.use_reflection_optimizer}</prop> <prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}</prop> <prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop> <prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</prop> <prop key="net.sf.ehcache.configurationResourceName">${net.sf.ehcache.configurationResourceName}</prop> <prop key="hibernate.cache.use_structured_entries">${hibernate.cache.use_structured_entries}</prop> </props> </property> </bean> <!-- 载入数据库的相关连接****************end********** --> <!-- spring的事务控制****************begin********** --> <!-- 开启AOP监听 仅仅对当前配置文件有效 --> <aop:aspectj-autoproxy expose-proxy="true"/> <!-- 开启注解事务 仅仅对当前配置文件有效 --> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> <property name="globalRollbackOnParticipationFailure" value="true" /> </bean> <tx:advice id="transactionAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="do*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="up*" propagation="REQUIRED" /> <tx:method name="del*" propagation="REQUIRED" /> <tx:method name="sear*" propagation="REQUIRED" read-only="true" /> <tx:method name="search*" propagation="REQUIRED" read-only="true" /> <tx:method name="find*" propagation="REQUIRED" read-only="true" /> <tx:method name="get*" propagation="REQUIRED" read-only="true" /> </tx:attributes> </tx:advice> <aop:config expose-proxy="true" proxy-target-class="true"> <aop:pointcut id="txPointcut" expression="execution(* com.wenc.*.service.*.*(..))" /> <aop:advisor advice-ref="transactionAdvice" pointcut-ref="txPointcut" order="1"/> </aop:config> <!-- spring的事务控制****************end********** --> <!-- shiro的配置*************************begin********** --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- 自己定义的realm --> <property name="realm" ref="sampleRealmService"/> </bean> <!-- 保证实现了Shiro内部lifecycle函数的bean运行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <!-- 登陆页面的连接 --> <property name="loginUrl" value="/login.jsp"/> <!-- 身份验证后跳转的连接 --> <property name="successUrl" value="/loginAction.action"/> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <property name="filters"> <util:map> <entry key="authc"> <bean class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter"/> </entry> </util:map> </property> <!-- 指定过滤器 Anon:不指定过滤器,不错是这个过滤器是空的。什么都没做,跟没有一样。 Authc:验证,这些页面必须验证后才干訪问,也就是我们说的登录后才干訪问。

这里还有其它的过滤器,我没用。比方说授权 --> <property name="filterChainDefinitions"> <value> /loginAction.action=anon /** = authc </value> </property> </bean> <!-- shiro的配置*************************end********** --> </beans>

3.基本的实现类有三个各自是PersonAction,UserPermissionInterceptor,SampleRealmService,这三个之间的相互协作完毕了shiro的整个认证和授权过程。以下我们来看各个类的作用:

package com.wenc.test.service.web;@Controller
public class PersonAction extends BaseAction implements ModelDriven<User> {private static Logger logger =Logger.getLogger(SampleRealmService.class);@Autowiredprivate PersonService personService;public String login()throws Exception{//对用户输入的password进行MD5加密String newPassword = CipherUtil.MD5Encode(info.getPassword());logger.info(info.getUsername()+"="+info.getPassword());Subject currentUser = SecurityUtils.getSubject();  UsernamePasswordToken token = new UsernamePasswordToken(  info.getUsername(), newPassword);//token.setRememberMe(true); //是否记住我 try {  /**currentUser.login(token) 提交申请,验证能不能通过,也就是交给shiro。

这里会回调reaml(或自己定义的realm)里的一个方法 protected AuthenticationInfo doGetAuthenticationInfo() */ currentUser.login(token); } catch (AuthenticationException e) { //验证身份失败 logger.info("验证登陆客户身份失败!"); this.addActionError("username或password错误,请又一次输入!"); return "fail"; } /**Shiro验证后,跳转到此处,这里推断验证是否通过 */ if(currentUser.isAuthenticated()){ //验证身份通过 return SUCCESS; }else{ this.addActionError("username或password错误。请又一次输入!

"); return "fail"; } } }

这个类的login方法是当我们输入username和password之后,点击登录button所运行的方法,因为在数据库中用户的password是密文形式。所以在进行用户身份验证,我们必须以相同的加密方式来加密用户在页面上输入的password,然后将username和加密后的password放入令牌(也就是token中),之后shiro会通过比对token中的username和password是否与数据库中存放的真正的username和password来确定用户是否为合法用户,而这个验证过程是shiro为我们完毕的,当运行currentUser.login(token)方法的时候会触发验证过程,可是通常情况下这个验证过程是通过我们来自己定义完毕的,为此我们必须自己写一个realm类来继承shiro的AuthorizingRealm类并覆盖其AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)方法,来看SampleRealmService类。这个就是继承AuthorizingRealm并覆盖其方法后的类:

package com.wenc.core.service;@Component
public class SampleRealmService extends AuthorizingRealm {private static Logger logger =Logger.getLogger(SampleRealmService.class);@Autowiredprivate PersonDAO personDAO;public SampleRealmService() {logger.info("-------AAA1------------------");setName("sampleRealmService");// setCredentialsMatcher(new Sha256CredentialsMatcher());}/*** 身份验证* @param authcToken 登陆Action封装的令牌*/protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {UsernamePasswordToken token = (UsernamePasswordToken) authcToken;/**查询相应的用户是否存在*/User user =personDAO.getUser(token.getUsername(), token.getPassword().toString());logger.info(user);if( user != null ) {return new SimpleAuthenticationInfo(user.getId(), user.getPassword(), getName());} else {return null;}}/*** 授权* 注意:统一在struts的拦截器中处理,见UserPermissionInterceptor.java*/protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {Integer userId = (Integer) principals.fromRealm(getName()).iterator().next();logger.info("用户ID:"+userId);User user = personDAO.getUser(userId);if( user != null ) {SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();for( Role role : user.getRoles() ) {info.addRole(role.getName());Set<Perms> set= role.getPermissions();logger.info(set);for(Perms perm:set){info.addStringPermission(perm.getActionName());}}return info;} else {return null;}}}

如同上面介绍的那样运行验证的过程就进入了身份认证方法体中。也就是在这里讲数据库中查询出来的真实的用户信息和token中的用户信息进行比对,当验证成功后跳转至strut.xml中配置的index.jsp页面,截图例如以下:

至此我们完毕了用户身份验证过程,接下来我们介绍授权过程和通过shiro标签来介绍细粒度的权限控制。当我们点击“主页2”这个超链接的时候会被struts.xml文件里定义的拦截器拦截。拦截器UserPermissionInterceptor代码例如以下:

package com.wenc.core.web.interceptor;public class UserPermissionInterceptor extends AbstractInterceptor {private static final long serialVersionUID = -2185920708747626659L;private static final Log logger = LogFactory.getLog(UserPermissionInterceptor.class);@Overridepublic String intercept(ActionInvocation invocation) throws Exception {ActionContext ac = invocation.getInvocationContext();Map map = ac.getParameters();String actionName = ac.getName();String methodName = "";String[] _methodName = (String[]) map.get("method");if (_methodName != null) {methodName = _methodName[0];}logger.info("actionName:"+actionName+",方法名:"+methodName);Subject currentUser = SecurityUtils.getSubject(); /**推断是否已经授权*/if(!currentUser.isPermitted(actionName)){ logger.info("没有有权限");}return invocation.invoke();}
}

当点击“主页2”之后会首先被该拦截器拦截。拦截的过程中会将当前请求(即点击“主页2”相应的action)的action名称取出。我们要验证的就是该用户是否享有对该action的权限,运行到currentUser.isPermitted(actionName)方法的时候就触发了shiro的授权认证功能,相同我们也对这种方法进行了重写,进入的是SampleRealmService类中的授权方法AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)。在这个函数中我们取出了数据库中配置的该用户的权限,并将用户的全部权限增加到info中,然后返回请求页面,当载入请求页面的时候运行到shiro标签的时候会再次触发授权(注意这次将不被拦截),相当于再次从数据库中将该用户的权限载入了一遍,而且放入到info中,然后shiro标签会依据shiro:hasPermission或者是shiro:hasRole进行比对,假设存在则显示,否则不显示。当然shiro标签除了这两种方式外还有非常多种其它的方式,大家能够自行探索。

至此整个shiro身份认证和授权介绍完成。谢谢阅读,请指正。

版权声明:本文博主原创文章,博客,未经同意不得转载。

转载于:https://www.cnblogs.com/blfshiye/p/4911466.html

shiro权限架作战相关推荐

  1. Spring Boot Shiro 权限管理

    Spring Boot Shiro 权限管理 标签: springshiro 2016-01-14 23:44 94587人阅读 评论(60) 收藏 举报 本来是打算接着写关于数据库方面,集成MyBa ...

  2. 一篇文章搞定Shiro权限管理框架

    前言:前几天学习了SpringSecurity安全框架,这几天又接着学习shiro框架,这两者框架都是同一类产品,解决同一类问题,但是在官方推荐使用Shiro框架,因为它简单易学,所以这里有时间学习了 ...

  3. 【权限管理框架】一文看懂Shiro权限管理框架!

    文章目录 1.JavaWeb中的权限控制 2.权限框架核心知识ACL和RBAC 2.1.ACL和RBAC简介 2.2主流权限框架介绍 3.Shiro架构和基本概念 3.1.Shiro的4大核心模块 3 ...

  4. activiti 工作流 springboot 后台框架平台 集成代码生成器 shiro 权限

    QQ 313596790 官网:www.fhadmin.org 下载地址 工作流模块---------------------------------------------------------- ...

  5. (39.3) Spring Boot Shiro权限管理【从零开始学Spring Boot】

    在学习此小节之前您可能还需要学习: (39.1) Spring Boot Shiro权限管理[从零开始学Spring Boot] http://412887952-qq-com.iteye.com/b ...

  6. Apache Shiro权限控制框架简介

    Apache Shiro权限控制框架简介 要想实现权限控制,可以自己写代码实现,蓄力都的权限控制可以通过过滤器Filter实现,细粒度的权限控制是基于代理对象结合自定义的注解和反射技术来实现,反射技术 ...

  7. SpringBoot中关于Shiro权限管理的整合使用

    在整合Shiro的时候,我们先要确定一下我们的步骤: 1.加入Shiro的依赖包,实现自己的Realm类(通过继承AuthorizingRealm类): 2.实现Shiro的配置类 3.实现前端的登录 ...

  8. Shiro与Spring集成时,Shiro权限注解@RequiresRoles等不生效的解决方案

    2019独角兽企业重金招聘Python工程师标准>>> Shiro与Spring集成时,Shiro权限注解@RequiresRoles等不生效, 这个问题着实整了好久,网上各种解决方 ...

  9. SpringMVC+Shiro权限管理

    SpringMVC+Shiro权限管理 什么是权限呢?举个简单的例子: 我有一个论坛,注册的用户分为normal用户,manager用户. 对论坛的帖子的操作有这些: 添加,删除,更新,查看,回复 我 ...

  10. java权限框架_Java高级工程师必备技术栈-由浅入深掌握Shiro权限框架

    权限系统在任何一个系统中都存在,随着分布式系统的大行其道,权限系统也趋向服务化,对于一个高级工程师来说,权限系统的设计是必不可少需要掌握的技术栈 Apache Shiro™是一个功能强大且易于使用的J ...

最新文章

  1. OSPF详解-2 区域结构
  2. ASP.NET 2.0 - 如何把上传的文件保存到数据库字段 (转自章立民CnBlogs)
  3. linux中/etc/passwd文件与/etc/shadow文件解析
  4. h5 与原生 app 交互的原理
  5. 学习《css世界》笔记之content自动添加开启闭合符号
  6. Docker,使生信分析更简单、可重复
  7. 14.json文件读取
  8. Oracle RAC的五大优势及其劣势
  9. Ruijie(RGOS 11.X)忘记登录密码console口恢复密码
  10. 计算机重启快捷键是什么,重启计算机的快捷键是什么?
  11. 微信小程序生成带logo二维码
  12. bzoj1645 / P2061 [USACO07OPEN]城市的地平线City Horizon(扫描线)
  13. 数据:锁定在智能合约中的MKR供应占比已创下16个月新高
  14. Linux下QT平台Mysql数据库开发环境配置
  15. (贫民窟系列)Raspberry 树莓派+L298N+Python控制小车
  16. 基于stm32f103rct6(秉火mini板)的rc522RFID读卡例程
  17. 渗透测试实践指南:必知必会的工具与方法学习笔记(二)——扫描
  18. php获取本机的IP
  19. SuperMap iClient3D for WebGL教程-管线流动特效
  20. iOS音频编程之实时语音通信

热门文章

  1. Postman测试json格式的post请求
  2. 单片机c语言编程定时,单片机C语言编程定时器的几种表达方式
  3. NotifyIcon用法
  4. 非线性动力方程中的解析法和数值法(解析解和数值解)
  5. cadlisp框选打印_CAD中批量打印
  6. Xen虚拟化环境安装和常用命令
  7. 如何用word制作英语答题卡_英语考试答题卡(word 版)
  8. 电脑常识——更改鼠标光标(另附一套MC指针)
  9. WIFI sniffer log抓包教程
  10. python风格logo_十分钟利用Python制作属于你自己的个性logo