• shiro实战整合


      引入依赖(包括缓存等):

            <!-- SECURITY begin -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-cas</artifactId>
                <version>${shiro.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>commons-logging</groupId>
                        <artifactId>commons-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-web</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-ehcache</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <!-- SECURITY end -->

      由于shiro是通过过滤器拦截的,所以第一步在web.xml中进行过滤器的配置

      web.xml:

    <!-- shiro的filter -->
        <!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
        <filter>
            <filter-name>shiroFilter</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
            <!-- 设置true由servlet容器控制filter的生命周期 -->
            <init-param>
                <param-name>targetFilterLifecycle</param-name>
                <param-value>true</param-value>
            </init-param>
            <!-- 【可选】设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean-->
            <init-param>
                <param-name>targetBeanName</param-name>
                <param-value>shiroFilter</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>shiroFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>

      全部web.xml中应包含 spring监听 springMVC监听 shiro过滤器 POST乱码处理等,示例:

    <?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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 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>permission_shiro1110</display-name>
      
      <!-- 配置spring容器监听器 -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/classes/spring/applicationContext-*.xml</param-value>
        </context-param>
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
    
    
        <!-- 前端控制器 -->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- 加载springmvc配置 -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <!-- 配置文件的地址 如果不配置contextConfigLocation, 默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml"即:springmvc-serlvet.xml -->
                <param-value>classpath:spring/springmvc.xml</param-value>
            </init-param>
    
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <!-- 可以配置/ ,此工程 所有请求全部由springmvc解析,此种方式可以实现 RESTful方式,需要特殊处理对静态文件的解析不能由springmvc解析 
                可以配置*.do或*.action,所有请求的url扩展名为.do或.action由springmvc解析,此种方法常用 不可以/*,如果配置/*,返回jsp也由springmvc解析,这是不对的。 -->
            <url-pattern>*.action</url-pattern>
        </servlet-mapping>
        
        <!-- shiro的filter -->
        <!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
        <filter>
            <filter-name>shiroFilter</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
            <!-- 设置true由servlet容器控制filter的生命周期 -->
            <init-param>
                <param-name>targetFilterLifecycle</param-name>
                <param-value>true</param-value>
            </init-param>
            <!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean-->
            <init-param>
                <param-name>targetBeanName</param-name>
                <param-value>shiroFilter</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>shiroFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
        
        
        <!-- 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>
        </filter>
        <filter-mapping>
            <filter-name>CharacterEncodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
      <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    View Code

      shiro配置文件中配置对应的bean,并进行一些依赖关系的配置与注入:

        applicationContext-shiro.xml:

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-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 ">
    
    <!-- web.xml中shiro的filter对应的bean -->
    <!-- Shiro 的Web过滤器 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager" />
            <!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
            <property name="loginUrl" value="/login.action" />
            <!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
            <property name="successUrl" value="/first.action"/>
            <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
            <property name="unauthorizedUrl" value="/refuse.jsp" />
            <!-- 自定义filter配置 -->
            <property name="filters">
                <map>
                    <!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中-->
                    <entry key="authc" value-ref="formAuthenticationFilter" />
                </map>
            </property>
            
            <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
            <property name="filterChainDefinitions">
                <value>
                    <!-- 对静态资源设置匿名访问 -->
                    /images/** = anon
                    /js/** = anon
                    /styles/** = anon
                    <!-- 验证码,可匿名访问 -->
                    /validatecode.jsp = anon
                    
                    <!-- 请求 logout.action地址,shiro去清除session-->
                    /logout.action = logout
                    <!--商品查询需要商品查询权限 ,取消url拦截配置,使用注解授权方式 -->
                    <!-- /items/queryItems.action = perms[item:query]
                    /items/editItems.action = perms[item:edit] -->
                    <!-- 配置记住我或认证通过可以访问的地址 -->
                    /index.jsp  = user
                    /first.action = user
                    /welcome.jsp = user
                    <!-- /** = authc 所有url都必须认证通过才可以访问-->
                    /** = authc
                    <!-- /** = anon所有url都可以匿名访问 -->
                    
                </value>
            </property>
        </bean>
    
    <!-- securityManager安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realm" ref="customRealm" />
            <!-- 注入缓存管理器 -->
            <property name="cacheManager" ref="cacheManager"/>
            <!-- 注入session管理器 -->
            <property name="sessionManager" ref="sessionManager" />
            <!-- 记住我 -->
            <property name="rememberMeManager" ref="rememberMeManager"/>
        </bean>
    
    <!-- realm -->
    <bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
        <!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>
    
    <!-- 凭证匹配器 -->
    <bean id="credentialsMatcher"
        class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="md5" />
        <property name="hashIterations" value="1" />
    </bean>
    
    <!-- 缓存管理器 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
            <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
        </bean>
    
    <!-- 会话管理器 -->
        <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
            <!-- session的失效时长,单位毫秒 -->
            <property name="globalSessionTimeout" value="600000"/>
            <!-- 删除失效的session -->
            <property name="deleteInvalidSessions" value="true"/>
        </bean>
    
    <!-- 自定义form认证过虑器 -->
    <!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
        <bean id="formAuthenticationFilter" 
        class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter ">
            <!-- 表单中账号的input名称 -->
            <property name="usernameParam" value="username" />
            <!-- 表单中密码的input名称 -->
            <property name="passwordParam" value="password" />
            <!-- 记住我input的名称 -->
            <property name="rememberMeParam" value="rememberMe"/>
     </bean>
    
    <!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
        <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
            <property name="cookie" ref="rememberMeCookie" />
        </bean>
        <!-- 记住我cookie -->
        <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <!-- rememberMe是cookie的名字 -->
            <constructor-arg value="rememberMe" />
            <!-- 记住我cookie生效时间30天 -->
            <property name="maxAge" value="2592000" />
        </bean>
    </beans>

      <!--注意静态资源的匿名访问-->

      其中,自定义的realm——CustomRealm(通过继承实现)如下:

    package cn.itcast.ssm.shiro;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.shiro.SecurityUtils;
    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.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import cn.itcast.ssm.po.ActiveUser;
    import cn.itcast.ssm.po.SysPermission;
    import cn.itcast.ssm.po.SysUser;
    import cn.itcast.ssm.service.SysService;
    
    /**
     * 
     * <p>
     * Title: CustomRealm
     * </p>
     * <p>
     * Description:自定义realm
     * </p>
     * <p>
     * Company: www.itcast.com
     * </p>
     * 
     * @author 传智.燕青
     * @date 2015-3-23下午4:54:47
     * @version 1.0
     */
    public class CustomRealm extends AuthorizingRealm {
        
        //注入service
        @Autowired
        private SysService sysService;
    
        // 设置realm的名称
        @Override
        public void setName(String name) {
            super.setName("customRealm");
        }
    
        // 用于认证
        //没有连接数据库的方法
        /*@Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
            
            // token是用户输入的用户名和密码 
            // 第一步从token中取出用户名
            String userCode = (String) token.getPrincipal();
    
            // 第二步:根据用户输入的userCode从数据库查询
            // ....
        
    
            // 如果查询不到返回null
            //数据库中用户账号是zhangsansan
            if(!userCode.equals("zhangsansan")){//
                return null;
            }
            
            
            // 模拟从数据库查询到密码
            String password = "111111";
    
            // 如果查询到返回认证信息AuthenticationInfo
            
            //activeUser就是用户身份信息
            ActiveUser activeUser = new ActiveUser();
            
            activeUser.setUserid("zhangsan");
            activeUser.setUsercode("zhangsan");
            activeUser.setUsername("张三");
            //..
            
            //根据用户id取出菜单
            //通过service取出菜单 
            List<SysPermission> menus  = null;
            try {
                menus = sysService.findMenuListByUserId("zhangsan");
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //将用户菜单 设置到activeUser
            activeUser.setMenus(menus);
            
            
            //将activeUser设置simpleAuthenticationInfo
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                    activeUser, password, this.getName());
    
            return simpleAuthenticationInfo;
        }*/
        
        //realm的认证方法,从数据库查询用户信息
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
            
            // token是用户输入的用户名和密码 
            // 第一步从token中取出用户名
            String userCode = (String) token.getPrincipal();
    
            // 第二步:根据用户输入的userCode从数据库查询
            SysUser sysUser = null;
            try {
                sysUser = sysService.findSysUserByUserCode(userCode);
            } catch (Exception e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
    
            // 如果查询不到返回null
            if(sysUser==null){//
                return null;
            }
            // 从数据库查询到密码
            String password = sysUser.getPassword();
            
            //
            String salt = sysUser.getSalt();
    
            // 如果查询到返回认证信息AuthenticationInfo
            
            //activeUser就是用户身份信息
            ActiveUser activeUser = new ActiveUser();
            
            activeUser.setUserid(sysUser.getId());
            activeUser.setUsercode(sysUser.getUsercode());
            activeUser.setUsername(sysUser.getUsername());
            //..
            
            //根据用户id取出菜单
            List<SysPermission> menus  = null;
            try {
                //通过service取出菜单 
                menus = sysService.findMenuListByUserId(sysUser.getId());
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //将用户菜单 设置到activeUser
            activeUser.setMenus(menus);
    
            //将activeUser设置simpleAuthenticationInfo
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                    activeUser, password,ByteSource.Util.bytes(salt), this.getName());
    
            return simpleAuthenticationInfo;
        }
        
        
    
        // 用于授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(
                PrincipalCollection principals) {
            
            //从 principals获取主身份信息
            //将getPrimaryPrincipal方法返回值转为真实身份类型(在上边的doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中身份类型),
            ActiveUser activeUser =  (ActiveUser) principals.getPrimaryPrincipal();
            
            //根据身份信息获取权限信息
            //从数据库获取到权限数据
            List<SysPermission> permissionList = null;
            try {
                permissionList = sysService.findPermissionListByUserId(activeUser.getUserid());
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //单独定一个集合对象 
            List<String> permissions = new ArrayList<String>();
            if(permissionList!=null){
                for(SysPermission sysPermission:permissionList){
                    //将数据库中的权限标签 符放入集合
                    permissions.add(sysPermission.getPercode());
                }
            }
            
            
        /*    List<String> permissions = new ArrayList<String>();
            permissions.add("user:create");//用户的创建
            permissions.add("item:query");//商品查询权限
            permissions.add("item:add");//商品添加权限
            permissions.add("item:edit");//商品修改权限
    */        //....
            
            //查到权限数据,返回授权信息(要包括 上边的permissions)
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            //将上边查询到授权信息填充到simpleAuthorizationInfo对象中
            simpleAuthorizationInfo.addStringPermissions(permissions);
    
            return simpleAuthorizationInfo;
        }
        
        //清除缓存
        public void clearCached() {
            PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
            super.clearCache(principals);
        }
    
    
    }
    View Code

      自定义的filter—— formAuthenticationFilter 如下(继承此过滤器然后添加相应的自定义代码,当然前文有提过会自动注册,但此处建议配置,可以拓展诸如验证码等功能)

    package cn.itcast.ssm.shiro;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    
    import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    
    /**
     * 
     * <p>Title: CustomFormAuthenticationFilter</p>
     * <p>Description:自定义FormAuthenticationFilter,认证之前实现 验证码校验 </p>
     * <p>Company: www.itcast.com</p> 
     * @author    传智.燕青
     * @date    2015-3-25下午4:53:15
     * @version 1.0
     */
    public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
    
        //原FormAuthenticationFilter的认证方法
        @Override
        protected boolean onAccessDenied(ServletRequest request,
                ServletResponse response) throws Exception {
            //在这里进行验证码的校验
            
            //从session获取正确验证码
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            HttpSession session =httpServletRequest.getSession();
            //取出session的验证码(正确的验证码)
            String validateCode = (String) session.getAttribute("validateCode");
            
            //取出页面的验证码
            //输入的验证和session中的验证进行对比 
            String randomcode = httpServletRequest.getParameter("randomcode");
            if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
                //如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中
                httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
                //拒绝访问,不再校验账号和密码 
                return true; 
            }
            return super.onAccessDenied(request, response);
        }
    
            
    }
    View Code

      更多整合配置可参见http://blog.csdn.net/clj198606061111/article/details/24185023

      注解式标签式开发与配置参见http://blog.csdn.net/oppoppoppo/article/details/55215731

       一、登陆

        1.原理:

        使用FormAuthenticationFilter过虑器实现 ,原理如下:

          将用户没有认证时,请求loginurl进行认证,用户身份和用户密码提交数据到loginurl

          FormAuthenticationFilter拦截住取出request中的username和password(两个参数名称是可以配置的)

          FormAuthenticationFilter调用realm传入一个token(username和password)

          realm认证时根据username查询用户信息(在Activeuser中存储,包括 userid、usercode、username、menus)。

          如果查询不到,realm返回null,FormAuthenticationFilter向request域中填充一个参数(记录了异常信息)

        2.登陆代码:

    //登陆提交地址,和applicationContext-shiro.xml中配置的loginurl一致
        @RequestMapping("login")
        public String login(HttpServletRequest request)throws Exception{
            
            //如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
            String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
            //根据shiro返回的异常类路径判断,抛出指定异常信息
            if(exceptionClassName!=null){
                if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
                    //最终会抛给异常处理器
                    throw new CustomException("账号不存在");
                } else if (IncorrectCredentialsException.class.getName().equals(
                        exceptionClassName)) {
                    throw new CustomException("用户名/密码错误");
                } else if("randomCodeError".equals(exceptionClassName)){
                    throw new CustomException("验证码错误 ");
                }else {
                    throw new Exception();//最终在异常处理器生成未知错误
                }
            }
            //此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径
            //登陆失败还到login页面
            return "login";
        }
    View Code

    //注意页面中字段需要为username和password(当然可以自定义)

        其中异常处理器已在springmvc中配置,全部代码另附

      二、退出

        不用我们去实现退出,只要去访问一个退出的url(该 url是可以不存在),由LogoutFilter拦截住,清除session

        在applicationContext-shiro.xml配置LogoutFilter

    <!-- 请求 logout.action地址,shiro去清除session-->
                    /logout.action = logout

      三、首页显示当前用户

    @Controller
    public class FirstAction {
        //系统首页
        @RequestMapping("/first")
        public String first(Model model)throws Exception{
            
            //从shiro的session中取activeUser
            Subject subject = SecurityUtils.getSubject();
            //取身份信息
            ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
            //通过model传到页面
            model.addAttribute("activeUser", activeUser);
            
            return "/first";
        }
    View Code

      四、授权过滤器

        1.流程

        1、在applicationContext-shiro.xml中配置filter规则

          <!--商品查询需要商品查询权限  -->

          /items/queryItems.action = perms[item:query](后续修改为注解与标签替代)

        2、用户在认证通过后,请求/items/queryItems.action

        3、被PermissionsAuthorizationFilter拦截,发现需要“item:query”权限

        4、PermissionsAuthorizationFilter调用realm中的doGetAuthorizationInfo获取数据库中正确的权限

        5、PermissionsAuthorizationFilter对item:query 和从realm中获取权限进行对比,如果“item:query”在realm返回的权限列表中,授权通过。

      其中,拒绝页面是在shiro中进行的配置:

    <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
            <property name="unauthorizedUrl" value="/refuse.jsp" />

         2.分析

      1、在applicationContext-shiro.xml中配置过虑器链接,需要将全部的url和权限对应起来进行配置,比较发麻不方便使用。

      2、每次授权都需要调用realm查询数据库,对于系统性能有很大影响,可以通过shiro缓存来解决。

         shiro的过滤器详细见day01

       五、认证

        1.需求

      修改realm的doGetAuthenticationInfo,从数据库查询用户信息,realm返回的用户信息中包括 (md5加密后的串和salt),实现让shiro进行散列串的校验。

        2.1 修改doGetAuthenticationInfo为数据库查询

    public class CustomRealm extends AuthorizingRealm {
        
        //注入service
        @Autowired
        private SysService sysService;
    
                //realm的认证方法,从数据库查询用户信息
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
            
            // token是用户输入的用户名和密码 
            // 第一步从token中取出用户名
            String userCode = (String) token.getPrincipal();
    
            // 第二步:根据用户输入的userCode从数据库查询
            SysUser sysUser = null;
            try {
                sysUser = sysService.findSysUserByUserCode(userCode);
            } catch (Exception e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
    
            // 如果查询不到返回null
            if(sysUser==null){//
                return null;
            }
            // 从数据库查询到密码
            String password = sysUser.getPassword();
            
            //
            String salt = sysUser.getSalt();
    
            // 如果查询到返回认证信息AuthenticationInfo
            
            //activeUser就是用户身份信息
            ActiveUser activeUser = new ActiveUser();
            
            activeUser.setUserid(sysUser.getId());
            activeUser.setUsercode(sysUser.getUsercode());
            activeUser.setUsername(sysUser.getUsername());
            //..
            
            //根据用户id取出菜单
            List<SysPermission> menus  = null;
            try {
                //通过service取出菜单 
                menus = sysService.findMenuListByUserId(sysUser.getId());
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //将用户菜单 设置到activeUser
            activeUser.setMenus(menus);
    
            //将activeUser设置simpleAuthenticationInfo
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                    activeUser, password,ByteSource.Util.bytes(salt), this.getName());
    
            return simpleAuthenticationInfo;
        }
    View Code

      对应的实体类:

    package cn.itcast.ssm.po;
    
    import java.util.Iterator;
    import java.util.List;
    
    import com.sun.org.apache.bcel.internal.generic.NEW;
    
    /**
     * 用户身份信息,存入session 由于tomcat将session会序列化在本地硬盘上,所以使用Serializable接口
     * 
     * @author Thinkpad
     * 
     */
    public class ActiveUser implements java.io.Serializable {
        private String userid;//用户id(主键)
        private String usercode;// 用户账号
        private String username;// 用户名称
    
        private List<SysPermission> menus;// 菜单
        private List<SysPermission> permissions;// 权限
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
    
        public String getUsercode() {
            return usercode;
        }
    
        public void setUsercode(String usercode) {
            this.usercode = usercode;
        }
    
        public String getUserid() {
            return userid;
        }
    
        public void setUserid(String userid) {
            this.userid = userid;
        }
    
        public List<SysPermission> getMenus() {
            return menus;
        }
    
        public void setMenus(List<SysPermission> menus) {
            this.menus = menus;
        }
    
        public List<SysPermission> getPermissions() {
            return permissions;
        }
    
        public void setPermissions(List<SysPermission> permissions) {
            this.permissions = permissions;
        }
    
        
    }
    View Code

        2.2  设置凭证匹配器

        数据库中存储到的md5的散列值,在realm中需要设置数据库中的散列值它使用散列算法 及散列次数,让shiro进行散列对比时和原始数据库中的散列值使用的算法 一致。:

    <!-- realm -->
    <bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
        <!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>
    
    <!-- 凭证匹配器 -->
    <bean id="credentialsMatcher"
        class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="md5" />
        <property name="hashIterations" value="1" />
    </bean>
    View Code

        六、授权

        1 需求

      修改realm的doGetAuthorizationInfo,从数据库查询权限信息。

      使用注解式授权方法。参见day02相关介绍:http://www.cnblogs.com/jiangbei/p/7138197.html

      使用jsp标签授权方法——标签的介绍参见http://jinnianshilongnian.iteye.com/blog/2026398

           注解与标签建议参考http://www.cnblogs.com/leechenxiang/p/5914661.html

      常用标签:

    标签名称

    标签条件(均是显示标签内容)

    <shiro:authenticated>

    登录之后

    <shiro:notAuthenticated>

    不在登录状态时

    <shiro:guest>

    用户在没有RememberMe时

    <shiro:user>

    用户在RememberMe时

    <shiro:hasAnyRoles name="abc,123" >

    在有abc或者123角色时

    <shiro:hasRole name="abc">

    拥有角色abc

    <shiro:lacksRole name="abc">

    没有角色abc

    <shiro:hasPermission name="abc">

    拥有权限资源abc

    <shiro:lacksPermission name="abc">

    没有abc权限资源

    <shiro:principal>

    显示用户身份名称

                   <shiro:principal property="username"/>     显示用户身份中的属性值

        页面使用示例:

    <!-- 有item:update权限才显示修改链接,没有该 权限不显示,相当 于if(hasPermission(item:update)) -->
        <shiro:hasPermission name="item:update">
        <a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a>
        </shiro:hasPermission>

      2.测试流程

        当调用controller的一个方法,由于该 方法加了@RequiresPermissions("item:query") shiro调用realm获取数据库中的权限信息,看"item:query"是否在权限数据中存在,如果不存在就拒绝访问,如果存在就授权通过。

        当展示一个jsp页面时,页面中如果遇到<shiro:hasPermission name="item:update">shiro调用realm获取数据库中的权限信息,看item:update是否在权限数据中存在,如果不存在就拒绝访问,如果存在就授权通过。

        问题:只要遇到注解或jsp标签的授权,都会调用realm方法查询数据库,需要使用缓存解决此问题。

       七、缓存

      针对上边授权频繁查询数据库,需要使用shiro缓存。

      shiro中提供了对认证信息和授权信息的缓存。shiro默认是关闭认证信息缓存的,对于授权信息的缓存shiro默认开启的。主要研究授权信息缓存,因为授权的数据量大。

      用户认证通过。

        该用户第一次授权:调用realm查询数据库

        该用户第二次授权:不调用realm查询数据库,直接从缓存中取出授权信息(权限标识符:就是一堆字符串)。

      缓存的配置步骤:

        导包:

          

        配置缓存管理器(cacheManager):由于缓存管理器也是归安全管理器securityManager这个大管家管理的,所以在其属性处进行配置即可:

       ehcache.xml配置文件:

    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
        <!--diskStore:缓存数据持久化的目录 地址  -->
        <diskStore path="F:developehcache" />
        <defaultCache 
            maxElementsInMemory="1000" 
            maxElementsOnDisk="10000000"
            eternal="false" 
            overflowToDisk="false" 
            diskPersistent="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120" 
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        </defaultCache>
    </ehcache>
    View Code

      清空缓存

      如果用户正常退出,缓存自动清空。

      如果用户非正常退出,缓存自动清空。

      如果修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。

        需要手动进行编程实现:

          在权限修改后调用realmclearCache方法清除缓存。

          下边的代码正常开发时要放在service中调用。

          在service中,权限修改后调用realm的方法。

        Realm中的清除缓存

    //清除缓存
        public void clearCached() {
            PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
            super.clearCache(principals);
        }
    View Code

        清除缓存的controller

    @Controller
    public class ClearShiroCache {
        
        //注入realm
        @Autowired
        private CustomRealm customRealm;
        
        @RequestMapping("/clearShiroCache")
        public String clearShiroCache(){
            
            //清除缓存,将来正常开发要在service调用customRealm.clearCached()
            customRealm.clearCached();
            
            return "success";
        }
    
    }
    View Code

      八、会话管理  

      和shiro整合后,使用shirosession管理,shiro提供sessionDao操作 会话数据。

    <!-- securityManager安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realm" ref="customRealm" />
            <!-- 注入缓存管理器 -->
            <property name="cacheManager" ref="cacheManager"/>
            <!-- 注入session管理器 -->
            <property name="sessionManager" ref="sessionManager" />
            <!-- 记住我 -->
            <property name="rememberMeManager" ref="rememberMeManager"/>
            
        </bean>
    ...
    
    <!-- 会话管理器 -->
        <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
            <!-- session的失效时长,单位毫秒 -->
            <property name="globalSessionTimeout" value="600000"/>
            <!-- 删除失效的session -->
            <property name="deleteInvalidSessions" value="true"/>
        </bean>
    View Code

      九、验证码的引入

      思路

      shiro使用FormAuthenticationFilter进行表单认证,验证校验的功能应该加在FormAuthenticationFilter中,在认证之前进行验证码校验。

      需要写FormAuthenticationFilter的子类,继承FormAuthenticationFilter,改写它的认证方法,在认证之前进行验证码校验。

    public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
    
        //原FormAuthenticationFilter的认证方法
        @Override
        protected boolean onAccessDenied(ServletRequest request,
                ServletResponse response) throws Exception {
            //在这里进行验证码的校验
            
            //从session获取正确验证码
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            HttpSession session =httpServletRequest.getSession();
            //取出session的验证码(正确的验证码)
            String validateCode = (String) session.getAttribute("validateCode");
            
            //取出页面的验证码
            //输入的验证和session中的验证进行对比 
            String randomcode = httpServletRequest.getParameter("randomcode");
            if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
                //如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中
                httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
                //拒绝访问,不再校验账号和密码 
                return true; 
            }
            return super.onAccessDenied(request, response);
        }
    View Code
    <!-- 自定义filter配置 -->
            <property name="filters">
                <map>
                    <!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中-->
                    <entry key="authc" value-ref="formAuthenticationFilter" />
                </map>
            </property>
    <!-- 自定义form认证过虑器 -->
    <!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
        <bean id="formAuthenticationFilter" 
        class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter ">
            <!-- 表单中账号的input名称 -->
            <property name="usernameParam" value="username" />
            <!-- 表单中密码的input名称 -->
            <property name="passwordParam" value="password" />
            <!-- 记住我input的名称 -->
            <property name="rememberMeParam" value="rememberMe"/>
     </bean>

      十、认证与记住我

       • Shiro 提供了记住我(RememberMe)的功能,比如访问如淘宝 等一些网站时,关闭了浏览器,下次再打开时还是能记住你是谁, 下次访问时无需再登录即可访问,基本流程如下:

      • 1、首先在登录页面选中 RememberMe 然后登录成功;如果是 浏览器登录,一般会把 RememberMe 的Cookie 写到客户端并 保存下来;——用户信息实现序列化借口,方便进行保存与反序列化。

      • 2、关闭浏览器再重新打开;会发现浏览器还是记住你的;

      • 3、访问一般的网页服务器端还是知道你是谁,且能正常访问;

      • 4、但是比如我们访问淘宝时,如果要查看我的订单或进行支付 时,此时还是需要再进行身份认证的,以确保当前用户还是你。

       如何配置:

      前台页面:

                   <tr>
                                <TD></TD>
                                <td><input type="checkbox" name="rememberMe" />自动登陆</td>
                            </tr>

    //当然,name是可以配置的,详见配置文件applicationContext-shiro.xml:

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-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 ">
    
    <!-- web.xml中shiro的filter对应的bean -->
    <!-- Shiro 的Web过滤器 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager" />
            <!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
            <property name="loginUrl" value="/login.action" />
            <!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
            <property name="successUrl" value="/first.action"/>
            <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
            <property name="unauthorizedUrl" value="/refuse.jsp" />
            <!-- 自定义filter配置 -->
            <property name="filters">
                <map>
                    <!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中-->
                    <entry key="authc" value-ref="formAuthenticationFilter" />
                </map>
            </property>
            
            <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
            <property name="filterChainDefinitions">
                <value>
                    <!-- 对静态资源设置匿名访问 -->
                    /images/** = anon
                    /js/** = anon
                    /styles/** = anon
                    <!-- 验证码,可匿名访问 -->
                    /validatecode.jsp = anon
                    
                    <!-- 请求 logout.action地址,shiro去清除session-->
                    /logout.action = logout
                    <!--商品查询需要商品查询权限 ,取消url拦截配置,使用注解授权方式 -->
                    <!-- /items/queryItems.action = perms[item:query]
                    /items/editItems.action = perms[item:edit] -->
                    <!-- 配置记住我或认证通过可以访问的地址 -->
                    /index.jsp  = user
                    /first.action = user
                    /welcome.jsp = user
                    <!-- /** = authc 所有url都必须认证通过才可以访问-->
                    /** = authc
                    <!-- /** = anon所有url都可以匿名访问 -->
                    
                </value>
            </property>
        </bean>
    
    <!-- securityManager安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realm" ref="customRealm" />
            <!-- 注入缓存管理器 -->
            <property name="cacheManager" ref="cacheManager"/>
            <!-- 注入session管理器 -->
            <property name="sessionManager" ref="sessionManager" />
            <!-- 记住我 -->
            <property name="rememberMeManager" ref="rememberMeManager"/>
            
        </bean>
    
    <!-- realm -->
    <bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
        <!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>
    
    <!-- 凭证匹配器 -->
    <bean id="credentialsMatcher"
        class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="md5" />
        <property name="hashIterations" value="1" />
    </bean>
    
    <!-- 缓存管理器 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
            <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
        </bean>
    
    <!-- 会话管理器 -->
        <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
            <!-- session的失效时长,单位毫秒 -->
            <property name="globalSessionTimeout" value="600000"/>
            <!-- 删除失效的session -->
            <property name="deleteInvalidSessions" value="true"/>
        </bean>
    
    <!-- 自定义form认证过虑器 -->
    <!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
        <bean id="formAuthenticationFilter" 
        class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter ">
            <!-- 表单中账号的input名称 -->
            <property name="usernameParam" value="username" />
            <!-- 表单中密码的input名称 -->
            <property name="passwordParam" value="password" />
            <!-- 记住我input的名称 -->
            <property name="rememberMeParam" value="rememberMe"/>
     </bean>
    
    <!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
        <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
            <property name="cookie" ref="rememberMeCookie" />
        </bean>
        <!-- 记住我cookie -->
        <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <!-- rememberMe是cookie的名字 -->
            <constructor-arg value="rememberMe" />
            <!-- 记住我cookie生效时间30天 -->
            <property name="maxAge" value="2592000" />
        </bean>
    
    
    </beans>
    View Code

      配置某些页面可以使用记住我访问:

       认证与记住我的区别:

      subject.isAuthenticated() 表示用户进行了身份验证登录的, 即使有 Subject.login 进行了登录;

      • subject.isRemembered():表示用户是通过记住我登录的, 此时可能并不是真正的你(如你的朋友使用你的电脑,或者 你的cookie 被窃取)在访问的

      • 两者二选一,即 subject.isAuthenticated()==true,则 subject.isRemembered()==false;反之一样。

            // rememberme
                token.setRememberMe(true);

      //一般为若单选框的进行了勾选则设置记住我,还可以进一步设置记住我的时间。

     记住我请参阅:http://jinnianshilongnian.iteye.com/blog/2031823

      【补充1】:jeesite中对shiro的配置:(2017.7.22)

        web.xml:

        <!-- Apache Shiro -->
        <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>

        applicationContext-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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
            http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.1.xsd"
        default-lazy-init="true">
    
        <description>Shiro Configuration</description>
    
        <!-- 加载配置属性文件 -->
        <context:property-placeholder ignore-unresolvable="true" location="classpath:jeesite.properties" />
        
        <!-- Shiro权限过滤过滤器定义 -->
        <bean name="shiroFilterChainDefinitions" class="java.lang.String">
            <constructor-arg>
                <value>
                    /static/** = anon
                    /userfiles/** = anon
                    ${adminPath}/cas = cas
                    ${adminPath}/login = authc
                    ${adminPath}/logout = logout
                    ${adminPath}/** = user
                    /act/editor/** = user
                    /ReportServer/** = user
                </value>
            </constructor-arg>
        </bean>
        
        <!-- 安全认证过滤器 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager" /><!-- 
            <property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" /> -->
            <property name="loginUrl" value="${adminPath}/login" />
            <property name="successUrl" value="${adminPath}?login" />
            <property name="filters">
                <map>
                    <entry key="cas" value-ref="casFilter"/>
                    <entry key="authc" value-ref="formAuthenticationFilter"/>
                </map>
            </property>
            <property name="filterChainDefinitions">
                <ref bean="shiroFilterChainDefinitions"/>
            </property>
        </bean>
        
        <!-- CAS认证过滤器 -->  
        <bean id="casFilter" class="org.apache.shiro.cas.CasFilter">  
            <property name="failureUrl" value="${adminPath}/login"/>
        </bean>
        
        <!-- 定义Shiro安全管理配置 -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realm" ref="systemAuthorizingRealm" />
            <property name="sessionManager" ref="sessionManager" />
            <property name="cacheManager" ref="shiroCacheManager" />
        </bean>
        
        <!-- 自定义会话管理配置 -->
        <bean id="sessionManager" class="com.thinkgem.jeesite.common.security.shiro.session.SessionManager"> 
            <property name="sessionDAO" ref="sessionDAO"/>
            
            <!-- 会话超时时间,单位:毫秒  -->
            <property name="globalSessionTimeout" value="${session.sessionTimeout}"/>
            
            <!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话   -->
            <property name="sessionValidationInterval" value="${session.sessionTimeoutClean}"/>
    <!--          <property name="sessionValidationSchedulerEnabled" value="false"/> -->
             <property name="sessionValidationSchedulerEnabled" value="true"/>
             
            <property name="sessionIdCookie" ref="sessionIdCookie"/>
            <property name="sessionIdCookieEnabled" value="true"/>
        </bean>
        
        <!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,
            当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! -->
        <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <constructor-arg name="name" value="jeesite.session.id"/>
        </bean>
    
        <!-- 自定义Session存储容器 -->
    <!--     <bean id="sessionDAO" class="com.thinkgem.jeesite.common.security.shiro.session.JedisSessionDAO"> -->
    <!--         <property name="sessionIdGenerator" ref="idGen" /> -->
    <!--         <property name="sessionKeyPrefix" value="${redis.keyPrefix}_session_" /> -->
    <!--     </bean> -->
        <bean id="sessionDAO" class="com.thinkgem.jeesite.common.security.shiro.session.CacheSessionDAO">
            <property name="sessionIdGenerator" ref="idGen" />
            <property name="activeSessionsCacheName" value="activeSessionsCache" />
            <property name="cacheManager" ref="shiroCacheManager" />
        </bean>
        
        <!-- 自定义系统缓存管理器-->
    <!--     <bean id="shiroCacheManager" class="com.thinkgem.jeesite.common.security.shiro.cache.JedisCacheManager"> -->
    <!--         <property name="cacheKeyPrefix" value="${redis.keyPrefix}_cache_" /> -->
    <!--     </bean> -->
        <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
            <property name="cacheManager" ref="cacheManager"/>
        </bean>
        
        <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
        
        <!-- AOP式方法级权限检查  -->
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
            <property name="proxyTargetClass" value="true" />
        </bean>
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
            <property name="securityManager" ref="securityManager"/>
        </bean>
        
    </beans>
    View Code

        spring-mvc.xml:

        
        <!-- 支持Shiro对Controller的方法级AOP安全控制 begin-->
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
            <property name="proxyTargetClass" value="true" />
        </bean>
        <!-- 异常处理的bean -->
        <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
            <property name="exceptionMappings">
                <props>
                    <prop key="org.apache.shiro.authz.UnauthorizedException">error/403</prop>
                    <prop key="java.lang.Throwable">error/500</prop>
                </props>
            </property>
        </bean>
        <!-- 支持Shiro对Controller的方法级AOP安全控制 end -->
  • 相关阅读:
    mysql的悲观锁与乐观锁的实现
    java中int和Integer的区别
    node 爬取图片并下载 到本地
    vue-cli3.0使用及配置(部分)
    vue模块组件
    新版公共弹出层
    四面八方拖拽
    js常用功能技巧函数
    localStorage和cookie的跨域解决方案
    前端面试要点
  • 原文地址:https://www.cnblogs.com/jiangbei/p/7221565.html
Copyright © 2020-2023  润新知