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); @Autowired
private 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);
@Autowired
private 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); @Override
public 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身份认证和授权介绍完成。谢谢阅读,请指正。

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

shiro权限架作战的更多相关文章

  1. 十、 Spring Boot Shiro 权限管理

    使用Shiro之前用在spring MVC中,是通过XML文件进行配置. 将Shiro应用到Spring Boot中,本地已经完成了SpringBoot使用Shiro的实例,将配置方法共享一下. 先简 ...

  2. Spring Boot Shiro 权限管理 【转】

    http://blog.csdn.net/catoop/article/details/50520958 主要用于备忘 本来是打算接着写关于数据库方面,集成MyBatis的,刚好赶上朋友问到Shiro ...

  3. shiro权限管理的框架-入门

    shiro权限管理的框架 1.权限管理的概念 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能 ...

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

    (本节提供源代码,在最下面可以下载)距上一个章节过了二个星期了,最近时间也是比较紧,一直没有时间可以写博客,今天难得有点时间,就说说Spring Boot如何集成Shiro吧.这个章节会比较复杂,牵涉 ...

  5. Spring Boot Shiro 权限管理

    Spring Boot Shiro 权限管理 标签: springshiro 2016-01-14 23:44 94587人阅读 评论(60) 收藏 举报 .embody{ padding:10px ...

  6. SpringMVC下的Shiro权限框架的使用

    SpringMVC+Shiro权限管理 博文目录 权限的简单描述 实例表结构及内容及POJO Shiro-pom.xml Shiro-web.xml Shiro-MyShiro-权限认证,登录认证层 ...

  7. SpringMVC整合Shiro权限框架

    尊重原创:http://blog.csdn.net/donggua3694857/article/details/52157313 最近在学习Shiro,首先非常感谢开涛大神的<跟我学Shiro ...

  8. shiro权限框架(一)

    不知不觉接触shiro安全框架都快三个月了,这中间配合项目开发踩过无数的坑.现在回想总结下,也算是一种积累,一种分享.中间有不够完美的地方或者不好的地方,希望大家指出来能一起交流.在这里谢谢开涛老师的 ...

  9. Shiro入门之一 -------- Shiro权限认证与授权

    一  将Shirojar包导入web项目 二 在web.xml中配置shiro代理过滤器 注意: 该过滤器需要配置在struts2过滤器之前 <!-- 配置Shiro的代理过滤器 -->  ...

随机推荐

  1. 小型Mp3播放器

    准备三张图片,名字分别为: play.pause.stop. 将一个名为Mp3的文件放入res/raw文件夹中. 在main.xml中: <LinearLayout xmlns:android= ...

  2. cocos2d-x 精灵移动

    在HelloWorldScene.h中声明 class  HelloWorld :  public  cocos2d::CCLayer { public :     ......     CCPoin ...

  3. Android Folding View(折叠视图、控件)

    版本号:1.0 日期:2014.4.21 版权:© 2014 kince 转载注明出处 非常早之前看过有人求助以下这个效果是怎样实现的,   也就是側滑菜单的一个折叠效果,事实上关于这个效果的实现,谷 ...

  4. MySQL filesort优化案例一则

    今天遇到一个filesort优化的案例,感觉不错,分享出来. MySQL中filesort是什么意思?官方手册定义: MySQL must do an extra pass to find out h ...

  5. ANDROID自定义视图——onMeasure流程,MeasureSpec详解

    简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量——onMeasure():决定View的大小 2.布局——onLayout():决定View在ViewGroup中的位置 3. ...

  6. JS - 焦点图

    下载地址:http://www.lanrentuku.com/js/jiaodiantu-1076.html 修改焦点图: CSS代码: /* 懒人图库 搜集整理 www.lanrentuku.com ...

  7. Decoding BASE64 in ABAP

    Code Gallery Decoding BASE64 in ABAP Skip to end of metadata Created by Frank Klausner, last modifie ...

  8. .Net 4.0特性 Tuple元组

    Tuple 字面意思:元组.是.net4.0增加的新特性,是干什么的呢?总结一句,个人觉得这个东西 就是用来在有返回很多种类型的值时可以用到.它提供了8种类型的Tuple,直接看最复杂的那种(其实不是 ...

  9. 学习VC MFC开发必须了解的常用宏和指令

    1.#include指令  包含指定的文件 2.#define指令   预定义,通常用它来定义常量(包括无参量与带参量),以及用来实现那些“表面似和善.背后一长串”的宏,它本身并不在编译过程中进行,而 ...

  10. 特殊的Windows消息

    WM_CREATE消息 该消息是Windows发送给视图的第一个消息.由于当应用程序框架调用Create函数时该消息就会被发送,而此时窗口创建还未完成,窗口还不可见,因此在控制函数OnCreate内部 ...