zoukankan      html  css  js  c++  java
  • spring整合apache-shiro的简单使用

    这里不对shiro进行介绍,介绍什么的没有意义

    初学初用,不求甚解,简单使用

    一.导入jar包(引入坐标)

    <!--shiro和spring整合-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.3.2</version>
    </dependency>
    <!--shiro核心包-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.3.2</version>
    </dependency>

    二.在web.xml中配置shiro的过滤器

    <?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"
             version="2.5">
    
      <!-- 监听器监听其他的spring配置文件 -->
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:spring/applicationContext-*.xml</param-value>
      </context-param>
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
     
      <!-- 解决post乱码 -->
      <filter>
        <filter-name>CharacterEncodingFilter</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>
        <init-param>
          <param-name>forceEncoding</param-name>
          <param-value>true</param-value>
        </init-param>
      </filter>
      <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>
    
      <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载-->
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
      </servlet-mapping>
    
      <!--配置shiro的filter-->
      <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
          <!--将shiro交给spring管理,不写默认是由tomcat进行管理-->
          <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>
    </web-app>
    web.xml

    注意:web.xml文件中的加载顺序为: context-param(ServletContext) -> listener-> filter -> servlet

    ,并且shiro的过滤器需要使用spring中配置的关于shiro的bean,所以shiro的配置不能写在springmvc的配置文件中,需要写在spring的配置文件中

    三.编写shiro的配置(可以写在spring配置文件中,也可以单独一个文件)

    本案例中spring配置文件名称为:applicationContext-service.xml

                       shiro的配置文件名称为:applicationContext-shiro.xml

                       dao层配置从spring配置中分离出来:applicationContext-dao.xml

    在web.xml文件中引用方式为:

    三.applicationContext-shiro.xml文件(shiro配置文件)

    <!--主要三个东西:shiroFilter,SaasRealm,SecrityManager-->

    注意:  shiroFilter:这个名字必须和web.xml文件中配置的过滤器的名称一致

                    SaasRealm:自定义数据源(领域),只要继承抽象类AuthorizingRealm,实现抽象方法即可,其中一个方法是关于登录认证的(doGetAuthenticationInfo),一个方法是权限封装的(doGetAuthorizationInfo).
    将从数据库中获取到的数据进行封装(权限)或者和客户端传入的数据进行比对(登录),返回相应的封装数据和登录结果

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--由spring整合shiro文件-->
        <!--主要三个东西:shiroFilter,SaasRealm,SecrityManager-->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <!--shiro核心类-->
            <property name="securityManager" ref="securityManager"/>
            <!--登录页面,如果没有登录,会跳转到登录界面,-->
            <property name="loginUrl" value="/login.jsp"/>
            <!--登录后,在访问没有经过授权的方法或界面时,直接跳转到这个界面-->
            <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
            <!--配置过滤器链-->
            <property name="filterChainDefinitions">
                <value>
                    /login.jsp = anon
                    /css/** = anon
                    /img/** = anon
                    /plugins/** = anon
                    /make/** = anon
                    /login.do = anon
                    /company/list.do = perms["企业管理"]
                     /system/log/list.do=perms["日志管理"]
                     /system/module/list.do=perms["模块管理"]
                     /company/list.do=perms["企业管理"]
                     /=perms["SaaS管理"]
                     /cargo=perms["货运管理"]
                     /stat=perms["统计分析"]
                     /baseinfo=perms["基础信息"]
                     /sysadmin=perms["系统管理"]
                     /cargo/contract/list.do=perms["购销合同"]
                     /cargo/contract/print.do=perms["出货表"]
                     /cargo/export/contractList.do=perms["合同管理"]
                     /cargo/export/list.do=perms["出口报运"]
                     /stat/toCharts.do=perms["生产厂家销售情况"]
                     /stat/toCharts.do=perms["产品销售排行"]
                     /stat/toCharts.do=perms["系统访问压力图"]
                     /system/dept/list.do=perms["部门管理"]
                     /system/user/list.do=perms["用户管理"]
                     /system/role/list.do=perms["角色管理"]
                     /cargo/packing/list.do=perms["装箱管理"]
                     /cargo/shipping/list.do=perms["委托管理"]
                     /cargo/invoice/list.do=perms["发票管理"]
                     /cargo/finance/list.do=perms["财务管理"]
                     /sysadmin/deptAction_toview=perms["查看部门"]
                     /sysadmin/deptAction_tocreate=perms["新增部门"]
                     /sysadmin/deptAction_toupdate=perms["修改部门"]
                     /sysadmin/deptAction_delete=perms["删除部门"]
                    /** = authc
                </value>
            </property>
        </bean>
    
    
        <!--注入自定义的SaasRealm-->
        <bean id="saamRealm" class="com.ahd.realm.SaasRealm"/>
    
        <!--配置shiro核心类-->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realm" ref="saamRealm"/>
        </bean>
    
        <!--后来编写的-->
        <!-- 安全管理器 -->
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
            <property name="securityManager" ref="securityManager"/>
        </bean>
    
        <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
        <!-- 生成代理,通过代理进行控制 -->
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
              depends-on="lifecycleBeanPostProcessor">
            <property name="proxyTargetClass" value="true"/>
        </bean>
    
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    
    </beans>

    三.1路径匹配权限太多,不好写,不想使用注解来搞乱代码,自己编写了一个小工具类,实现拼接

    package com.ahd.util;
    import com.ahd.domain.system.Module;
    import com.ahd.service.system.ModuleService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import java.util.List;
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath*:spring/applicationContext-*.xml")
    public class realmUtil {
        @Autowired
        private ModuleService ms;
        @Test
        public void test() {
            String authorization = "perms";
            List<Module> all = ms.findAll();
            StringBuffer sb = new StringBuffer();
            for (Module module : all) {
                String curl = module.getCurl();
                if(curl!=null){
                    if (!curl.contains("?")) {
                        sb.append("/" + curl + "=" + authorization + "["" + module.getName() + ""]
    ");
                    }else{
                        String[] split = curl.split("\?");
                        sb.append("/" + split[0] + "=" + authorization + "["" + module.getName() + ""]
    ");
                    }
                }else{
                    sb.append("/" + curl + "=" + authorization + "["" + module.getName() + ""]
    ");
                }
            }
            System.out.println(sb.toString());
        }
    }
    自己编写的小工具

    三.2想法

    shiro对于路径匹配权限

     

    没有好的解决方案,只能一条一条编写,

             想法:编写一个工具类,动态查询数据库,拼接字符串,在spring容器加载之前写入文件(在web.xml中配置监听器)

    没有好的解决方案,只能一条一条编写,

             想法:编写一个工具类,动态查询数据库,拼接字符串,在spring容器加载之前写入文件(在web.xml中配置监听器)

    四.自定义Realm

    public class SaasRealm extends AuthorizingRealm {
        @Autowired
        private UserService us;
        @Autowired
        private ModuleService ms;
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            //将用户所有权限存入对象中
            //获取登录的用户对象
            User user = (User) principalCollection.getPrimaryPrincipal();
            //通过查询数据库,查找该用户所具有的的权限
            List<Module> moduleByUser = ms.findModuleByUser(user);
    //        Set<String> perms=new HashSet<>();
            //创建SimpleAuthorizationInfo权限对象
            SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
    
            for (Module module : moduleByUser) {
                info.addStringPermission(module.getName());
            }
    
            return info;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
            //获取页面传递过来的账号密码
            String username_page = token.getUsername();//email
            String password_page = new String(token.getPassword());
    
            //从数据库中查找该账号的信息
            User user_db = us.findByEmail(username_page);
            String password_db = user_db.getPassword();
    
            //进行信息比较,如果密码不匹配,返回null,调用者会抛出异常
            if(!password_page.equals(password_db)){
                return null;
            }
            return new SimpleAuthenticationInfo(user_db,user_db.getPassword(),getName());
        }
    }
    部分代码图片解释:

    五.登录代码示例

    @RequestMapping(value="login",name = "用户登录")
    public String login(String email,String password){
        //登录分析
        //1.首先判断用户邮箱和密码是否为空
        if(StringUtils.isEmpty(email)||StringUtils.isEmpty(password)){
            request.setAttribute("error","邮箱或密码不能为空");
            return "forward:/login.jsp";
        }
        password=new Md5Hash(password,email,1).toString();
        UsernamePasswordToken token=new UsernamePasswordToken(email,password);
        Subject subject = SecurityUtils.getSubject();
    
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            request.setAttribute("error","邮箱或密码有误");
            return "forward:/login.jsp";
        }
        //执行到这里,证明登录成功,所以继续封装数据
        //通过shiro的Session域获取user对象
        User user = (User) subject.getPrincipal();
    
        List<Module> moduleList =ms.findModuleByUser(user);//存入session
        session.setAttribute("modules",moduleList);
    
        //将登录数据存入session域
        session.setAttribute("loginUser",user);
        return "home/main";
    }

    登录之后经过以上配置对于权限不需要再进行其他操作

    六.注解配置

     

    在路径对应的方法上添加注解@RequiresPermissions (“权限名称”)

    七.页面标签展示

    1.引入标签库:

    <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

    2.使用标签

    <shiro:hasPermission name="删除部门">
        <button type="button" class="btn btn-default" title="删除" onclick='deleteById()'><i class="fa fa-trash-o"></i> 删除</button>
    </shiro:hasPermission>

    八.优化

     

    使用缓存,避免一项功能查询多次

    增加了jvm负担,建议使用redis缓存

    九.自定义过滤器

    说明:模仿shiro现有的10个过滤器 现在模仿的是perms过滤器

    目的:用此配置/system/module/list.do = perms["模块管理","删除模块"]

     但凡符合其中的一个权限就应该放行,但是默认的perms过滤器必须两个都有才能放行

    第一步:定义一个过滤器,继承一个父类

       重写方法

    public class MyPermsFilter extends AuthorizationFilter {
        public MyPermsFilter() {
        }
    
        public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
            Subject subject = this.getSubject(request, response);
            String[] perms = (String[])((String[])mappedValue);
    
    //       perms =  ["部门管理","删除部门"]
            if (perms != null && perms.length > 0) {
                for (String perm : perms) {
                    if (subject.isPermitted(perm)) {
                        return true;
                    }
                }
                return false;
                      }
    
            return true;
        }
    }

    第二步:交给spring容器

    第三步:把过滤器交个shiro框架

    第四步:使用自定义的过滤器

     关于缓存的问题,使用redis比较好推荐网页:

    SpringDataRedis的操作

    https://www.jianshu.com/p/4a9c2fec1079

  • 相关阅读:
    HDU 2011 (水)
    HDU 2010 (水)
    HDU 2009 (水)
    HDU 2007 (水)
    mcsscm
    Meow~
    沉沦魔是战5渣
    谈谈和81的过往
    量子力学就像一座破房子 Quantum power is like a broken house.
    能源危机的应对方案(基于Energy不守恒定律)Response to the Energy Crisis (Based on the Law of Mana Non-Conservation)
  • 原文地址:https://www.cnblogs.com/aihuadung/p/11154813.html
Copyright © 2011-2022 走看看