加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Spring 整合Shiro 并扩展使用EL表达式的实例详解

发布时间:2020-05-23 21:22:14 所属栏目:Java 来源:互联网
导读:Shiro是一个轻量级的权限控制框架,应用非常广泛。本文的重点是介绍Spring整合Shiro,并通过扩展使用Spring的EL表达式,使@RequiresRoles等支持动态的参数。对Shiro的介绍则不在本文的讨论范围之内,读者如果有对shi

Shiro是一个轻量级的权限控制框架,应用非常广泛。本文的重点是介绍Spring整合Shiro,并通过扩展使用Spring的EL表达式,使@RequiresRoles等支持动态的参数。对Shiro的介绍则不在本文的讨论范围之内,读者如果有对shiro不是很了解的,可以通过其官方网站了解相应的信息。infoq上也有一篇文章对shiro介绍比较全面的,也是官方推荐的,其地址是https://www.oudahe.com/p/84/。

Shiro整合Spring

首先需要在你的工程中加入shiro-spring-xxx.jar,如果是使用Maven管理你的工程,则可以在你的依赖中加入以下依赖,笔者这里是选择的当前最新的1.4.0版本。

<dependency>
 <groupId>org.apache.shiro</groupId>
 <artifactId>shiro-spring</artifactId>
 <version>1.4.0</version>
</dependency>

接下来需要在你的web.xml中定义一个shiroFilter,应用它来拦截所有的需要权限控制的请求,通常是配置为/*。另外该Filter需要加入最前面,以确保请求进来后最先通过shiro的权限控制。这里的Filter对应的class配置的是DelegatingFilterProxy,这是Spring提供的一个Filter的代理,可以使用Spring bean容器中的一个bean来作为当前的Filter实例,对应的bean就会取filter-name对应的那个bean。所以下面的配置会到bean容器中寻找一个名为shiroFilter的bean。

<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时通常会定义一个org.apache.shiro.web.servlet.ShiroFilter来做类似的事。

接下来就是在bean容器中定义我们的shiroFilter了。如下我们定义了一个ShiroFilterFactoryBean,其会产生一个AbstractShiroFilter类型的bean。通过ShiroFilterFactoryBean我们可以指定一个SecurityManager,这里使用的DefaultWebSecurityManager需要指定一个Realm,如果需要指定多个Realm则通过realms指定。这里简单起见就直接使用基于文本定义的TextConfigurationRealm。通过loginUrl指定登录地址、successUrl指定登录成功后需要跳转的地址,unauthorizedUrl指定权限不足时的提示页面。filterChainDefinitions则定义URL与需要使用的Filter之间的关系,等号右边的是filter的别名,默认的别名都定义在org.apache.shiro.web.filter.mgt.DefaultFilter这个枚举类中。

<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="/home.jsp"/>
 <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
 <property name="filterChainDefinitions">
  <value>
   /admin/** = authc,roles[admin]
   /logout = logout
   # 其它地址都要求用户已经登录了
   /** = authc,logger
  </value>
 </property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
 <property name="realm" ref="realm"/>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 简单起见,这里就使用基于文本的Realm实现 -->
<bean id="realm" class="org.apache.shiro.realm.text.TextConfigurationRealm">
 <property name="userDefinitions">
  <value>
   user1=pass1,role1,role2
   user2=pass2,role2,role3
   admin=admin,admin
  </value>
 </property>
</bean>

如果需要在filterChainDefinitions定义中使用自定义的Filter,则可以通过ShiroFilterFactoryBean的filters指定自定义的Filter及其别名映射关系。比如下面这样我们新增了一个别名为logger的Filter,并在filterChainDefinitions中指定了/**需要应用别名为logger的Filter。

<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="/home.jsp"/>
 <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
 <property name="filters">
  <util:map>
   <entry key="logger">
    <bean class="com.elim.chat.shiro.filter.LoggerFilter"/>
   </entry>
  </util:map>
 </property>
 <property name="filterChainDefinitions">
  <value>
   /admin/** = authc,logger
  </value>
 </property>
</bean>

其实我们需要应用的Filter别名定义也可以不直接通过ShiroFilterFactoryBean的setFilters()来指定,而是直接在对应的bean容器中定义对应的Filter对应的bean。因为默认情况下,ShiroFilterFactoryBean会把bean容器中的所有的Filter类型的bean以其id为别名注册到filters中。所以上面的定义等价于下面这样。

<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="/home.jsp"/>
 <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
 <property name="filterChainDefinitions">
  <value>
   /admin/** = authc,logger
  </value>
 </property>
</bean>
<bean id="logger" class="com.elim.chat.shiro.filter.LoggerFilter"/>

经过以上几步,Shiro和Spring的整合就完成了,这个时候我们请求工程的任意路径都会要求我们登录,且会自动跳转到loginUrl指定的路径让我们输入用户名/密码登录。这个时候我们应该提供一个表单,通过username获得用户名,通过password获得密码,然后提交登录请求的时候请求需要提交到loginUrl指定的地址,但是请求方式需要变为POST。登录时使用的用户名/密码是我们在TextConfigurationRealm中定义的用户名/密码,基于我们上面的配置则可以使用user1/pass1、admin/admin等。登录成功后就会跳转到successUrl参数指定的地址了。如果我们是使用user1/pass1登录的,则我们还可以试着访问一下/admin/index,这个时候会因为权限不足跳转到unauthorized.jsp。

启用基于注解的支持

基本的整合需要我们把URL需要应用的权限控制都定义在ShiroFilterFactoryBean的filterChainDefinitions中。这有时候会没那么灵活。Shiro为我们提供了整合Spring后可以使用的注解,它允许我们在需要进行权限控制的Class或Method上加上对应的注解以定义访问Class或Method需要的权限,如果是定义中Class上的,则表示调用该Class中所有的方法都需要对应的权限(注意需要是外部调用,这是动态代理的局限)。要使用这些注解我们需要在Spring的bean容器中添加下面两个bean定义,这样才能在运行时根据注解定义来判断用户是否拥有对应的权限。这是通过Spring的AOP机制来实现的,关于Spring Aop如果有不是特别了解的,可以参考笔者写在iteye的《Spring Aop介绍专栏》。下面的两个bean定义,AuthorizationAttributeSourceAdvisor是定义了一个Advisor,其会基于Shiro提供的注解配置的方法进行拦截,校验权限。DefaultAdvisorAutoProxyCreator则是提供了为标注有Shiro提供的权限控制注解的Class创建代理对象,并在拦截到目标方法调用时应用AuthorizationAttributeSourceAdvisor的功能。当拦截到了用户的一个请求,而该用户没有对应方法或类上标注的权限时,将抛出org.apache.shiro.authz.AuthorizationException异常。

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" 
 depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
 <property name="securityManager" ref="securityManager"/>
</bean>

如果我们的bean容器中已经定义了<aop:config/>或<aop:aspectj-autoproxy/>,则可以不再定义DefaultAdvisorAutoProxyCreator。因为前面两种情况都会自动添加与DefaultAdvisorAutoProxyCreator类似的bean。关于DefaultAdvisorAutoProxyCreator的更多介绍也可以参考笔者的Spring Aop自动创建代理对象的原理这篇博客。

Shiro提供的权限控制注解如下:

RequiresAuthentication:需要用户在当前会话中是被认证过的,即需要通过用户名/密码登录过,不包括RememberMe自动登录。

RequiresUser:需要用户是被认证过的,可以是在本次会话中通过用户名/密码登录认证,也可以是通过RememberMe自动登录。

RequiresGuest:需要用户是未登录的。
RequiresRoles:需要用户拥有指定的角色。
RequiresPermissions:需要用户拥有指定的权限。

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读