• Shiro教程之五 记住我的功能(rememberMe)


     这个也和 cookie和session相关,

    记住我实现步骤

    1.applicationContext.xml文件要修改,(在securityManager的bean中除了本来的Realm,再注入rememberMeManager相关bean)

    2.用户认证时token设置参数记住我为true

    3.测试是:登录后关闭浏览器再打开浏览器看是否登录状态存在(这里有个问题) 重新打开浏览器session无法获取问题

    每次打开浏览器访问网址,session_id值回变,因此关掉浏览器再次打开用session_id获取用户信息-->(行不通)
    解决办法,自定义一个过滤器,集成FormAuthenticationFilter ,在这里面,把rememberMe的用户信息,再次加入session中返回给客户端.
    (这个自定义的过滤器可以模仿applicationContext.xml中Shiro中配置默认一些过滤器链一样,加入进去)

    4.测试

    【代码】记住我的功能

    1.jsp或html页面的form表单中添加一个checkbox元素,选中为记住我,并传值到控制器

      <div class="form-row">
          <div class="form-group col-md-4">
              <label for="rememberme">七天免登录</label>
              <input type="checkbox" class="form-control" id="rememberme" name="rememberme" value="1" >
          </div>
      </div>

     2.applicationContext.xml里面设置给securityManager加入rememberMe的Bean

    <!-- 10. Shiro认证权限配置-->
        <!-- ================ Shiro start ================ -->
    
        <!-- (1). cookie的配置 -->
        <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <!-- 应该是表单传过来的值吧 -->
            <constructor-arg value="rememberme"></constructor-arg>
            <!-- 只有http时才能使用cookie-->
            <property name="httpOnly" value="true"></property>
            <!-- 设置cookie的存活时间 7天 单位秒 -->
            <property name="maxAge" value="604800"></property>
        </bean>
        <!-- (2). 声明记住我的管理对象 -->
        <bean name="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
            <property name="cookie" ref="rememberMeCookie"></property>
        </bean>
    
        <!-- (3). 声明凭证匹配器(密码加密用)-->
        <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <!--注入算法-->
            <property name="hashAlgorithmName" value="md5"></property>
            <!--注入散列次数-->
            <property name="hashIterations" value="1"></property>
        </bean>
    
        <!-- (4).配置Realm-->
        <bean id="shiroReaml" class="com.cc8w.shiro.ShiroRealm">
            <!--注入凭证匹配器-->
            <property name="credentialsMatcher" ref="credentialsMatcher"></property>
        </bean>
    
        <!-- (5). 创建安全管理器-->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <!-- 注入reaml -->
            <property name="realm" ref="shiroReaml"></property>
            <!-- 注入rememberManager -->
            <property name="rememberMeManager" ref="rememberMeManager"></property>
        </bean>
    
        <!-- (6). 配置过滤器链-->
        <!-- Shiro 的Web过滤器 id必须和web.xml里面的shiroFilter的 targetBeanName的值一样 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <!--Shiro的核心安全接口,这个属性是必须的-->
            <property name="securityManager" ref="securityManager"></property>
            <!-- 要求登录时的链接(登录页面地址),非必须属性,属性会自动寻找web工程根目录下的"/login.jsp"页面-->
            <property name="loginUrl" value="/login.jsp"></property>
            <!-- Shiro 的Web过滤器 id必须和web.xml里面的shiroFilter的 targetBeanName的值一样 -->
            <!--property name="successUrl" value="success.do"></property-->
            <!-- 用户访问未对其授权的资源时,所显示的连接 -->
            <property name="unauthorizedUrl" value="unauthorized.jsp"></property>
            <!-- 过滤器链的定义,从上往下顺序执行,一般将/**放在最后 -->
            <property name="filterChainDefinitions">
                <!-- 等于后面是一个filter,可以用自带的也可以继承相应接口自己写 -->
                <value>
                    <!-- /**=authc 所有url都必须认证通过才可以访问 -->
                    /index.jsp*=anon
                    /login/toLogin*=anon
                    /login/login*=anon
                    <!-- 如果访问/login/logout就是用Shiro注销session-->
                    /login/logout=logout
                    <!-- /** = anon所有url都可以匿名访问 -->
                    <!-- /** = authc -->
                    <!-- /*/* = authc -->
                    <!-- /** = authc所有url都不可以匿名访问 必须放到最后面 -->
                    /** = authc
                </value>
            </property>
        </bean>
    <!-- 总结:
            Shiro总体配置:
            1.先配置第6条,用到了securityManager
                  2.所以配置 第5条:securityManager, 第5条用到了:realm(用户相关)和rememberMeManager(记住我)
    
                          3.所以配置 第4条:realm,第4条用到了credentialsMatcher(加密相关)
                                    4.所以配置 第3条credentialsMatcher(加密相关)
    
                          5.在配置rememberMeManager(记住我), 用到了第2条rememberMeManager(cookie的声明)
                                    6.设置第2条cookie的时候,用到了第1条rememberMeCookie(设置cookid)
    -->
    
        <!-- ================ Shiro end ================ -->

    3.控制器接收值,登录时token设置相应参数

        //三,登录测试(自定义Realm)
        @Test
        public void testRealmLogin(){
            //1.创建一个安全管理器的工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory();
            //2.在工厂中获取安全管理器
            DefaultSecurityManager securityManager = (DefaultSecurityManager) factory.getInstance();
    
            //---添加认证凭证配置start  --如果Spring环境下(这些都是在xml配置的)
            //2.1 创建自定义Realm注入到安全管理器
            ShiroRealm shiroRealm = new ShiroRealm();//(SpringM在bean控制,并可以配置散列加密相关)
            //2.1.1设置密码学相关加密方式
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            //2.1.2设置加密方式
            credentialsMatcher.setHashAlgorithmName("md5");
            //2.1.3设置散列次数
            credentialsMatcher.setHashIterations(1);
            //2.1.4将密码学凭证注入到自定义的Realm类中
            shiroRealm.setCredentialsMatcher(credentialsMatcher);
            //---添加认证凭证配置end  --如果Spring环境下(这些都是在xml配置的)
    
            securityManager.setRealm(shiroRealm);
    
            //3.将securityManager绑定到运行环境
            SecurityUtils.setSecurityManager(securityManager);
            //4.获取Subject对象(将要登录的用户)
            Subject subject = SecurityUtils.getSubject();
            //5.获取要登录用户的token,客户端传递过来的用户名和密码
            String username = "zhangsan",password="123456";
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    
            //6.如果有记住我功能(设置参数)
            Integer rememberme= 表单传来的值;
            if(rememberme!=null && rememberme==1){
                token.setRememberMe(true);//说明如果认证成功要记住我
            }
    
            try{
                //7.登陆(认证)
                subject.login(token);
                logger.info("登录了");
    
            }catch (IncorrectCredentialsException  e ){
                logger.info("密码不正确");
                logger.info(e);
            }catch (UnknownAccountException e) {
                System.out.println("没有这个帐号");
            }catch (AuthenticationException e) {
                e.printStackTrace();
            }
    
            //如果登录成功了,可以获取subject中各种状态了
            Boolean isAuth = subject.isAuthenticated();
            System.out.println("认证状态:" + isAuth);
    
            // 8.授权 分为:基于角色授权 基于资源的授权
            //8.1 基于角色授权
            boolean permited  = subject.hasRole("role1");
            System.out.println("这是授权单个:"+permited);
            boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1","role2","role3"));
            System.out.println("这个授权多个"+hasAllRoles);
    
            // 使用check方法进行授权,如果授权不通过会抛出异常
            // subject.checkRole("role13");
            try {
                subject.checkRole("roles1");
            }catch (UnauthenticatedException e){
                logger.info("没有这个角色");
                //e.printStackTrace();
            }catch (UnauthorizedException e){
                logger.info("没有这个权限");
                //e.printStackTrace();
            }
    
    
            //8.2 基于资源的授权
            //isPermitted传入权限标识符
            boolean isPermitted = subject.isPermitted("user:query");
            System.out.println("单个权限判断:"+isPermitted);
    
            boolean isPermittedAll = subject.isPermittedAll("user:query","user:adb","user:add");
            System.out.println("多个权限判断"+isPermittedAll);
    
            // 使用check方法进行授权,如果授权不通过会抛出异常
            try {
                subject.checkPermission("user:adb");
            }catch (UnauthenticatedException e){
                logger.info("没有这个角色");
                //e.printStackTrace();
            }catch (UnauthorizedException e){
                logger.info("没有这个权限");
                //e.printStackTrace();
            }
    
    
    
        }

    4.重新打开浏览器,session无法获取问题

    这个要创建一个自定义创建RememberMeFilter来集成FormAuthenticationFilter ,在applicationContext.xml配置这个过滤器链
    目的:
    让访问页面认证的时候,判断subject主体是否认证过了,如果认证过了,并且当前session中用户信息为空,再在subject.getPrincipal()获取一下用户信息给session.
    
    1.因为securityManager设置了记住我时间,token登录的时候subject知道记住我时长.所以subject一直存有用户信息.
    2.只是判断一下服务器session没有值了,且subject认证还没有过期,表示重新打开的浏览器,
    这时要把subject.getPrincipal()就是自定义Reaml中传的ActiveUser.getUser();复制给session即可.
    package com.cc8w.filter;
    
    import com.cc8w.entity.UserActivePojo;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    public class ShiroRememberMeFilter extends FormAuthenticationFilter {
    
        //过滤器访问,必须经过的方法,返回true表示通过
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    
            //1.获得主体
            Subject subject = SecurityUtils.getSubject();
            //2.通过主体获得session信息
            Session session = subject.getSession();
            //记住我的功能isAuthenticated肯定为false 而isRemembered肯定为true (因为不用短时间不用认证)
            if(!subject.isAuthenticated() && subject.isRemembered() && session.getAttribute("user")==null){
                //说明是记住我的功能
                UserActivePojo activeUser= (UserActivePojo)subject.getPrincipal();
                if(null!=activeUser){
                    session.setAttribute("user",activeUser.getUserPojo());
                }
            }
    
    
            return true;
        }
    
    
    }

    5. 配置这个过滤器链

    修改后的 xml 文件

        <!-- 10. Shiro认证权限配置-->
        <!-- ================ Shiro start ================ -->
    
        <!-- (1). cookie的配置 -->
        <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <!-- 应该是表单传过来的值吧 -->
            <constructor-arg value="rememberme"></constructor-arg>
            <!-- 只有http时才能使用cookie-->
            <property name="httpOnly" value="true"></property>
            <!-- 设置cookie的存活时间 7天 单位秒 -->
            <property name="maxAge" value="604800"></property>
        </bean>
        <!-- (2). 声明记住我的管理对象 -->
        <bean name="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
            <property name="cookie" ref="rememberMeCookie"></property>
        </bean>
    
        <!-- (3). 声明凭证匹配器(密码加密用)-->
        <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <!--注入算法-->
            <property name="hashAlgorithmName" value="md5"></property>
            <!--注入散列次数-->
            <property name="hashIterations" value="1"></property>
        </bean>
    
        <!-- (4).配置Realm-->
        <bean id="shiroReaml" class="com.cc8w.shiro.ShiroRealm">
            <!--注入凭证匹配器-->
            <property name="credentialsMatcher" ref="credentialsMatcher"></property>
        </bean>
    
        <!-- (5). 创建安全管理器-->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <!-- 注入reaml -->
            <property name="realm" ref="shiroReaml"></property>
            <!-- 注入rememberManager -->
            <property name="rememberMeManager" ref="rememberMeManager"></property>
        </bean>
    
        <!-- (6). 配置过滤器链-->
        <!-- 声明自定义记住我过滤器链-->
        <bean id="shirorememberMeFilter" class="com.cc8w.filter.ShiroRememberMeFilter" ></bean>
    
        <!-- Shiro 的Web过滤器 id必须和web.xml里面的shiroFilter的 targetBeanName的值一样 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <!--Shiro的核心安全接口,这个属性是必须的-->
            <property name="securityManager" ref="securityManager"></property>
            <!-- 要求登录时的链接(登录页面地址),非必须属性,属性会自动寻找web工程根目录下的"/login.jsp"页面-->
            <property name="loginUrl" value="/login.jsp"></property>
            <!-- Shiro 的Web过滤器 id必须和web.xml里面的shiroFilter的 targetBeanName的值一样 -->
            <!--property name="successUrl" value="success.do"></property-->
            <!-- 用户访问未对其授权的资源时,所显示的连接 -->
            <property name="unauthorizedUrl" value="unauthorized.jsp"></property>
            <!-- 注入自定义过滤器 -->
            <property name="filters">
                <map>
                    <entry key="shirorememberMeFilter"  value-ref="shirorememberMeFilter"></entry>
                </map>
            </property>
            <!-- 过滤器链的定义,从上往下顺序执行,一般将/**放在最后 -->
            <property name="filterChainDefinitions">
                <!-- 等于后面是一个filter,可以用自带的也可以继承相应接口自己写 -->
                <value>
                    <!-- /**=authc 所有url都必须认证通过才可以访问 -->
                    /index.jsp*=anon
                    /login/toLogin*=anon
                    /login/login*=anon
                    <!-- 如果访问/login/logout就是用Shiro注销session-->
                    /login/logout=logout
                    <!-- 其他页面都要认证,并且过自定义过滤器-->
                    /** = shirorememberMeFilter,authc
                    /* = authc
                    /*/* = authc
                    <!-- /** = authc所有url都不可以匿名访问 必须放到最后面 -->
    
                </value>
            </property>
        </bean>
    <!-- 总结:
            Shiro总体配置:
            1.先配置第6条,用到了securityManager
                  2.所以配置 第5条:securityManager, 第5条用到了:realm(用户相关)和rememberMeManager(记住我)
    
                          3.所以配置 第4条:realm,第4条用到了credentialsMatcher(加密相关)
                                    4.所以配置 第3条credentialsMatcher(加密相关)
    
                          5.在配置rememberMeManager(记住我), 用到了第2条rememberMeManager(cookie的声明)
                                    6.设置第2条cookie的时候,用到了第1条rememberMeCookie(设置cookid)
    -->
    
        <!-- ================ Shiro end ================ -->

    测试:  登录后,再关闭浏览器,在打开.

    最后贴一个登录认证,(这时一些类已经交由Spring管理)

    /**
         * 完成登陆的方法
         */
        @RequestMapping("login")
        public String login(String username, String password, HttpSession session) {
            // 1,得到主体
            Subject subject = SecurityUtils.getSubject();
            // 2,封装用户名和密码
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            try {
                subject.login(token);
                ActiveUser activerUser = (ActiveUser) subject.getPrincipal();
                session.setAttribute("user", activerUser.getUser());
                return "redirect:/user/loadAllUser.action";
            } catch (AuthenticationException e) {
                System.out.println("用户名或密码不正确");
            }    
            return "redirect:index.jsp";
        }
  • 相关阅读:
    [转] zigbee的低功耗
    手机显示电量原理
    TinyOS总结(一)
    KingPaper初探redist 之redis设置分析
    KingPaper初探redis之redis在window xp下的安装
    Web开发必知的八种隔离级别
    目前国内著名的维客(wiki)网站
    SSH Secure Shell Client
    Java线程:volatile关键字
    JDK1.6(JDK6.0) 新特性
  • 原文地址:https://www.cnblogs.com/fps2tao/p/13572518.html
Copyright © 2020-2023  润新知