Apache shiro配置与使用(Spring整合)

网络上大部分的博文是关于Apache shiro与Spring MVC的整合,以及使用教程,最近在做一个物流项目的时候使用的是Apache shiro与Spring进行整合,期间遇到了一些问题,花费了一些时间才得到解决,所以本文首先会从介绍shiro框架到理解shiro以及使用shiro框架几个角度进行描述如何正确的使用与理解该框架:

  • 1、权限概述(正确理解认证、授权的基本概念)
  • 2、常见的权限控制的方式(URL拦截的方式、方法注解的方式)
  • 3、权限涉及到的数据表以及模型关系
  • 4、Apache shiro框架
  • 5、整合到项目中

1、权限概述(正确理解认证、授权的基本概念)


  • 有关概念介绍

    • 在我们开发的一个大中小型系统中提供的功能有许多,并不是所有的用户都有权限可以进行访问以及操作,总是需要对于有些功能进行分组,并且进行访问限制;
    • 认证:系统提供的用于识别用户身份的功能,通常是在登录功能(当前系统登录之后,是谁?如何识别到你是谁登录了系统);
    • 授权:系统提供的赋予用户可以操作系统某些功能能力(当前登录系统的用户具有哪些权限功能);
    • 单点登录(SSO):一处登录处处使用(可以联想我们在使用淘宝的时候,如果实在同一个终端,比如浏览器中登录了一次,之后我们在使用淘宝旗下的大部分产品的时候不需要进行第二次登录),利用Cookie实现,并且至少需要独立出一台服务器存储用户的登录信息,也就是说用户不是直接登录到系统,需要进行一台服务器的单独认证,如果在服务器中存在有相关用户的登录信息,就可以不用第二次登录;
    • Remember me :记住我功能(利用的是Cookie实现);

2、常见的权限控制的方式(URL拦截的方式、方法注解的方式)

两种方式实际上都会遵从的后台流程图如下所示:

而所谓的URL拦截无非就是在中间的一层使用配置文件的配置URL拦截;

而方法注解也就是在对应的业务方法上就像注解标示标示,例如:

  @RequiresPermissions(value="XXX.xxx")

3、权限涉及到的数据表以及模型关系


4、Apache shiro框架

官网地址:http://shiro.apache.org

提供的进行权限控制的方式:

  • 1、URL拦截进行权限控制
  • 2、方法注解权限控制
  • 3、页面标签权限控制
  • 4、代码方式权限控制(了解)
  • 下图来在shiro框架的官方手册:

  • 下图主要是shiro框架的运行流程,来自shiro框架的官方手册:

  • Application Code:应用程序代码,由开发人员负责(也就是当前的项目代码部分)
  • Subject:主体,当前用户,由框架提供的
  • SecurityManager:安全管理器,由框架提供的,整个shiro框架最核心的组件。
  • Realm:安全数据桥,类似于项目中的DAO,访问安全数据的,框架提供,开发人员也可自己编写

5、整合到项目中

  1. 导入jar包(shiro的jar有很多,针对不同的项目导入不同的jar包,但是为了防止第一次学习的时候出错,所有使用的是shiro-all-1.2.jar版本的jar包);
  2. 配置以及代码详细

    步骤一: 在web.xml中配置一个过滤器,是由spring提供的,用于整合shiro:

    • web.xml文件(一定要注意配置shiro框架以及Spring,Struts之间的顺序问题,否则报错!)
<?xml version="1.0" encoding="UTF-8"?>
<web-app
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID"
    version="2.5">
  <display-name>XXX系统</display-name>

  <!-- 中文乱码问题解决过滤器 -->
  <filter>
    <filter-name>characterFilter</filter-name>
    <filter-class>
        org.springframework.web.filter.CharacterEncodingFilter
    </filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>characterFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!-- spring提供的解决hibernate延迟加载问题的过滤器 -->
  <filter>
    <filter-name>openSessionInView</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>openSessionInView</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!-- spring配置文件位置 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <!-- spring核心监听器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- Spring配置文件结束 -->

  <!-- 配置由Spring提供的过滤器,用于整合shiro框架 -->
  <!-- 在项目启动的过程中,当前过滤器会从Spring工厂中提取同名对象 -->
  <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>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>

  <!-- struts核心控制器 -->
  <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>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
  </filter-mapping>

  <!-- 欢迎页面 -->
  <welcome-file-list>
    <welcome-file>login.jsp</welcome-file>
  </welcome-file-list>

</web-app>

步骤二: 在applicationContext.xml中配置bean,ID必须为shiroFilter:

  • applicationContext.xml文件配置
  • shiro 框架由于大量的使用了代理模式,所以在使用的过程中如果配置不当,可能会出现问题,另外在使用注解开发时候尽量的使用Spring的注解,不要使用JDK自带的原生注解,减少出错的几率
<!-- 配置shiro的过滤器 -->
    <!-- 当前对象用于创建shiro框架需要的过滤器对象 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 注入安全管理器 -->
        <property name="securityManager" ref="securityManager"></property>
        <!-- 注入系统的登录访问路径 -->
        <!-- 跳转到登录页面 -->
        <property name="loginUrl" value="/login.jsp"></property>
        <!-- 成功页面 -->
        <property name="successUrl" value="/index.jsp"></property>
        <!-- 权限不足的错误提示页面 -->
        <property name="unauthorizedUrl" value="/500.html"></property>
        <!-- 基于URL拦截权限控制 -->
        <property name="filters">
            <map>
                <entry key="authc">
                    <bean class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter"/>
                </entry>
            </map>
        </property>
        <!--
            URL路径自上而下进行匹配
         -->
        <!--
            anon过滤器处理原则 :随便访问
            authc需要进行权限认证
         -->
        <property name="filterChainDefinitions">
            <value>
                /css/** = anon
                /easyuidemo/** = anon
                /images/** = anon
                /js/** = anon
                /json/** = anon
                /user/** = anon
                /validatecode.jsp* = anon
                /login.jsp* = anon
                /user/userAction_login.action = anon
                /* = authc
            </value>
        </property>
    </bean>
    <bean id="sessionManager" class="org.apache.shiro.session.mgt.DefaultSessionManager">
    </bean>

    <!-- 定义安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 注入realm -->
        <property name="realm" ref="XXXRealm"></property>
        <!-- 注入缓存管理器 -->
        <property name="cacheManager" ref="cacheManager"></property>
    </bean>

    <!-- 注册缓存管理器 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
    </bean>

    <bean id="lifecycleBeanPostProcessor"
            class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- 自定义Realm -->
    <bean id="XXXRealm" class="com.online.XXX.shiro.XXXRealm">
    </bean>

    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <!-- 使用shiro的注解需要的配置代码 -->
    <!-- 开启shiro自动代理 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
            depends-on="lifecycleBeanPostProcessor">
        <!-- 指定强制使用cglib为action创建代理对象 -->
        <property name="proxyTargetClass" value="true"></property>
    </bean>

    <aop:config proxy-target-class="true"></aop:config>

    <!-- 配置切面类 -->
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"></property>
    </bean>

步骤三: 登录系统使用shrio框架管理,修改Action中login方法:

    /**
     * 登录系统,使用shiro框架操作
     * @return
     */
    public String login() {
        //从session中获取自动生成的验证码
        String key = (String) ActionContext.getContext().getSession().get("key");
        //shiro的基本认证方法
        if (StringUtils.isNotBlank(key) && checkCode.equals(key)) {
            //使用shiro方式进行认证
            String username = model.getUsername();
            String password = model.getPassword();
            password = MD5Utils.md5(password);
            //主体,当前状态为没有认证的状态“未认证”
            Subject subject = SecurityUtils.getSubject();
            //登录认证令牌(用户名密码令牌)
            AuthenticationToken token = new UsernamePasswordToken(username,password);
            //登录方法(认证是否通过)
            //使用subject调用securityManager,安全管理器调用Realm
            try {
                //利用异常操作
                //需要开始调用到Realm中
                System.out.println("========================================");
                System.out.println("1、进入认证方法");
                subject.login(token);
                user = (User) subject.getPrincipal();
            } catch (UnknownAccountException e) {
                this.addActionError(this.getText("loginError"));
                return LOGIN;
            }

            ActionContext.getContext().getSession().put("loginUser", user);
            return "home";
        } else {
            //验证码错误
            this.addActionError(this.getText("checkCodeError"));
            return LOGIN;
        }
    }

步骤四: 开发属于自己的realm类:

import java.util.List;

import javax.annotation.Resource;

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.authc.UsernamePasswordToken;
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.springframework.stereotype.Component;

import com.online.XXX.dao.IFunctionDao;
import com.online.XXX.dao.IUserDao;
import com.online.XXX.domain.Function;
import com.online.XXX.domain.User;

/**
 * 自定义Realm
 *
 */
@Component("XXXRealm")
public class XXXRealm extends AuthorizingRealm {

    // 注入DAO
    @Resource(name="userDao")
    private IUserDao userDao;
    @Resource(name="functionDao")
    private IFunctionDao functionDao;

    // 认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authenticationToken) throws AuthenticationException {
        //返回空当前账号不存在

        //toke强转
        UsernamePasswordToken usernamePasswordToken =
                (UsernamePasswordToken) authenticationToken;
        String username = usernamePasswordToken.getUsername();
        //根据用户名查询密码,有安全管理器负责对比查询出的数据库中的密码和页面输入的密码是否一致
        User user = userDao.findUserByUsername(username);
        if (user == null) {
            return null;
        }
        String passwordFromDB = user.getPassword();

        //最后的比对需要交给安全管理器
        //三个参数进行初步的简单认证信息对象的包装
        AuthenticationInfo info =
                new SimpleAuthenticationInfo(user,
                        passwordFromDB, //由安全管理器进行包装运行
                        this.getClass().getSimpleName());

        return info;
    }

    // 授权方法
    // 执行的时期
    /**
     * 在访问需要控制的时候需要权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principalCollection) {

        //根据当前登录用户,查询用户的角色,根据角色对应获得的权限添加到信息对象中

        //程序任何位置都可以拿到user对象

        //方法一:
        User user = (User) principalCollection.getPrimaryPrincipal();

        //方法二:
//      Subject subject = SecurityUtils.getSubject();
//      User _user = (User) subject.getPrincipal();
//      System.out.println("subject"+_user.getUsername());

        //方法三:从context中获取XXXContext中获取
        //所有的过程都是动态从数据库中取出来
        //为所有的用户授予staff权限(模拟)
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        List<Function> list = null;
        //根据当前登录用户,查询用户角色,返回用户权限,将权限添加到当前的权限信息中

        //如果是超级管理员应该赋予全部权限的操作
        if (user.getUsername().equals("admin")) {
            //如果是超级管理员,授予所有权限
            list = functionDao.findAll();
        } else {
            //普通用户,根据用户查询对应的权限
            list = functionDao.findFunctionByUserId(user.getId());
        }
        for (Function function : list) {
            //权限关键字
            String code = function.getCode();
            info.addStringPermission(code);
        }
        return info;
    }

}

步骤五: shiro框架基于注解的开发:

在上面的applicationContext.xml的配置文件中已经代开了shiro的注解开发代理

<!-- 使用shiro的注解需要的配置代码 -->
    <!-- 开启shiro自动代理 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
            depends-on="lifecycleBeanPostProcessor">
        <!-- 指定强制使用cglib为action创建代理对象 -->
        <property name="proxyTargetClass" value="true"></property>
    </bean>
    <aop:config proxy-target-class="true"></aop:config>

以上的配置以及代码已经实现了权限管理以及控制功能,并且实现了不同的角色登>录系统之后不同的功能列表的显示

补充:缓存机制:使用的ehcache缓存机制,需要使用的是ehcache的jar包,以及配置一个ehcache.xml的缓存文件,如下:

<ehcache
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 缓存存储的硬盘位置 -->
    <diskStore path="/home/ubuntu/..."/>

    <!--
        1、maxElementsInMemory:最大缓冲量
        2、eternal:物理内存有Java虚拟机进行定时清理
        3、timeToIdleSeconds:最大空闲时间
        4、timeToLiveSeconds:最大存货时间
        5、maxElementsOnDisk:最大溢出大磁盘上
        6、diskPersistent:服务器重启之后是否有效
        7、memoryStoreEvictionPolicy:换出策略
     -->

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            />
</ehcache>

一下部分单独最为单纯的demo研究使用,一般情况下这样使用不太常见

步骤六:在Action中对应的需要权限管理的方法上@RequestMapping(“XXX”)的注解进行注解标示;

@RequiresPermissions(value="region")
    public String pageQuery() throws IOException {
        //业务代码略
        return NONE;
    }

步骤七:在XXXRealm中对于该方法进行授权与否

info.addStringPermission(code);

时间: 2024-10-07 04:07:31

Apache shiro配置与使用(Spring整合)的相关文章

SpringMVC+Apache Shiro+JPA(hibernate)整合配置

序: 关于标题: 说是教学,实在愧不敢当,但苦与本人文笔有限,实在找不到更合理,谦逊的词语表达,只能先这样定义了. 其实最真实的想法,只是希望这个关键词能让更多的人浏览到这篇文章,也算是对于自己写文章的一个肯定吧.^_^! 关于内容: 再写这系列文章之前,本人和许多人一样都是伸手党,并深深的了解咱伸手党且英文较差的朋友对于新知识的学习及获取中文资料少的痛苦.所以本着"取之于民,共享与民"的原则,记录下实际工作中对SpringMVC+Shiro整合应用的部分心得.本人技术水平有限,仅希望

将 Shiro 作为应用的权限基础 五:SpringMVC+Apache Shiro+JPA(hibernate)整合配置

配置web.xml,applicationContext.xml, spring-mvc.xml,applicationContext-shiro.xml,而且都有详细的说明. Web.xml是web项目最基本的配置文件,看这个配置,可以快速知道web项目使用什么框架,它就像一个面板,切入我们想用的插件. applicationContext.xml是spring的基本配置,主要配置数据源.JPA实体管理器工厂.事务 spring-mvc.xml是SpringMVC的配置, applicatio

Java EE 7基于数据库的Apache Shiro配置

上一篇文章我介绍了在Java EE环境中配置Shiro的基本方法, 但是在真正的开发过程中我们基本上不 会使用基于配置文件的用户角色配置, 大多数情况下我们会将用户, 角色和权限存储在数据库中, 然后我们告诉Shiro去数据库中取数据, 这样的配置更灵活且功能更强大. 这样使Shiro能读数据库(或LDAP, 文件系统等)的组件叫做Realm, 可以把Realm看作是一个安全专用的DAO, 下面我详细介绍一下如何配置Realm: (所用到的技术和上一篇文章中的类似, 此外, 我们用到了JPA,

Spring整合Shiro做权限控制模块详细案例分析

1.引入Shiro的Maven依赖 <!-- Spring 整合Shiro需要的依赖 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId

Spring整合ActiveMQ及多个Queue消息监听的配置

消息队列(MQ)越来越火,在java开发的项目也属于比较常见的技术,MQ的相关使用也成java开发人员必备的技能.笔者公司采用的MQ是ActiveMQ,且消息都是用的点对点的模式.本文记录了实现Spring整合ActivateMQ的全过程及如何使用MQ,便于后续查阅. 一.项目的搭建 采用maven构建项目,免去了copy jar包的麻烦.因此,我们创建了一个java类型的Maven Project (1)项目结构图 先把项目结构图看一下,便于对项目的理解. (2)pom.xml 我们需要加入以

将 Shiro 作为应用的权限基础 五:SpringMVC+Apache Shiro+JPA(hib

点击链接加入群[JavaEE(SSH+IntelliJIDE+Maven)]:http://jq.qq.com/?_wv=1027&k=L2rbHv 将 Shiro 作为应用的权限基础 五:SpringMVC+Apache Shiro+JPA(hibernate)整合配置 配置web.xml,applicationContext.xml, spring-mvc.xml,applicationContext-shiro.xml,而且都有详细的说明. web.xml是web项目最基本的配置文件,看这

Spring 整合CXF 实现WebService(JAX-WS)

服务端创建项目 添加依赖 web.xml 配置CXFServlet <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="htt

Apache Shiro 学习笔记

一.为什么要学习Shiro Shiro是简单易用的权限控制框架.应用范围广,受到许多开发人员的欢迎.他可以运用于javaSE项目还可以运用于javaEE项目.在项目中Shiro可以帮助我们完成:认证.授权.加密.会话管理.与Web集成.缓存等. 二.与spring security的笔记 Shiro简单易学,尽管功能没有spring security强大,但其功能已足够日常开发使用.spring官网使用的便是Shiro.可见其的方便.Shiro还可与spring整合.更方便了开发者. 三.Shi

Apache Shiro Web应用整合-配置

博客分类: Shiro Shiro 将Shiro  集成到任何 Web  应用程序的最简单的方法是在 web.xml  中配置 ContextListener  和 Filter ,来使 Shiro 知道如何读取 Shiro 的 INI  配置文件. 注意:Spring  框架用户将不执行此设置.如果你使用 Spring ,你将要阅读关于 Spring  特定的 Web  配置. Web.xml Shiro 1.2 and later 在Shiro 1.2  及以后版本,标准的 Web  应用程