shiro权限架作战
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权限架作战的更多相关文章
- 十、 Spring Boot Shiro 权限管理
使用Shiro之前用在spring MVC中,是通过XML文件进行配置. 将Shiro应用到Spring Boot中,本地已经完成了SpringBoot使用Shiro的实例,将配置方法共享一下. 先简 ...
- Spring Boot Shiro 权限管理 【转】
http://blog.csdn.net/catoop/article/details/50520958 主要用于备忘 本来是打算接着写关于数据库方面,集成MyBatis的,刚好赶上朋友问到Shiro ...
- shiro权限管理的框架-入门
shiro权限管理的框架 1.权限管理的概念 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能 ...
- (39.1) Spring Boot Shiro权限管理【从零开始学Spring Boot】
(本节提供源代码,在最下面可以下载)距上一个章节过了二个星期了,最近时间也是比较紧,一直没有时间可以写博客,今天难得有点时间,就说说Spring Boot如何集成Shiro吧.这个章节会比较复杂,牵涉 ...
- Spring Boot Shiro 权限管理
Spring Boot Shiro 权限管理 标签: springshiro 2016-01-14 23:44 94587人阅读 评论(60) 收藏 举报 .embody{ padding:10px ...
- SpringMVC下的Shiro权限框架的使用
SpringMVC+Shiro权限管理 博文目录 权限的简单描述 实例表结构及内容及POJO Shiro-pom.xml Shiro-web.xml Shiro-MyShiro-权限认证,登录认证层 ...
- SpringMVC整合Shiro权限框架
尊重原创:http://blog.csdn.net/donggua3694857/article/details/52157313 最近在学习Shiro,首先非常感谢开涛大神的<跟我学Shiro ...
- shiro权限框架(一)
不知不觉接触shiro安全框架都快三个月了,这中间配合项目开发踩过无数的坑.现在回想总结下,也算是一种积累,一种分享.中间有不够完美的地方或者不好的地方,希望大家指出来能一起交流.在这里谢谢开涛老师的 ...
- Shiro入门之一 -------- Shiro权限认证与授权
一 将Shirojar包导入web项目 二 在web.xml中配置shiro代理过滤器 注意: 该过滤器需要配置在struts2过滤器之前 <!-- 配置Shiro的代理过滤器 --> ...
随机推荐
- poj 1018 Communication System 枚举 VS 贪心
Communication System Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 21631 Accepted: ...
- 【ASP.NET Web API教程】2.3.2 创建域模型
原文:[ASP.NET Web API教程]2.3.2 创建域模型 Part 2: Creating the Domain Models 第2部分:创建域模型 本文引自:http://www.asp. ...
- 中文分词算法之最大正向匹配算法(Python版)
最大匹配算法是自然语言处理中的中文匹配算法中最基础的算法,分为正向和逆向,原理都是一样的. 正向最大匹配算法,故名思意,从左向右扫描寻找词的最大匹配. 首先我们可以规定一个词的最大长度,每次扫描的时候 ...
- JSTL解析——001
JSTL 全称jsp standard tag library ,即jsp标准标签库. 是不是想问标签是什么东西? 标签就是jsp专门用于显示数据的,可重复利用的类库: 是不是想问标签由那些部分组成的 ...
- HDU4712-----Hamming Distance------超级大水题
本文出自:http://blog.csdn.net/dr5459 题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4712 题目意思: 海明距离:任意两个 ...
- swift_将UIDatePicker到达的传播之间的时间差在数小时出现页面的事
今天,写swift demo当它来到了一个非常精彩的问题,我再次 present 使用页面出来 UIDatePicker 选择时间,然后再回到原来的主界面的时间,结果出现的问题:B页面的正常时间,传回 ...
- 【翻译】十大要避免的Ext JS开发方法
原文地址:http://www.sencha.com/blog/top-10-ext-js-development-practices-to-avoid/ 作者:Sean Lanktree Sean ...
- mojo 关闭utf8
[root@wx03 ~]# cat test.pl use Mojolicious::Lite; use JSON qw/encode_json decode_json/; use Encode; ...
- Android改变系统自带环形ProgressBar的大小
MainActivity如下: package cc.testprogressbar; import android.os.Bundle; import android.app.Activity; / ...
- poj 3623 Best Cow Line, Gold
题目不算难,但是不认真想的话很容易wa,我就是wa了多次才意识到自己想法存在的缺陷. 相同的时候往后找知道出现不相同时,只能判断出当前字符的优先顺序. 这个题目如果朴素的按照这种方法做的话复杂度其实是 ...