导航

  首先,我们要说明一下,本技术点的开发背景是shiro与springMvc结合环境下的开发方式。

  由于shiro把用户登录后的信息都存在了自己封装的session中,所以要实现单一地址登录,我们需要关注到shiro的 session操作。技术实现步骤如下:

一、在shiro的xml配置文件中

1、加入sessionManager配置

    <!-- shiro结合Session会话管理器 -->
      <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
          <!-- session的失效时长,单位毫秒 1小时: 3600000, 站点设置以 6小时 为主:21600000 -->
          <!-- 设置全局会话超时时间,默认30分钟,即如果30分钟内没有访问会话将过期 1800000 -->
          <property name="globalSessionTimeout" value="1800000"/>
          <!-- 删除失效的session -->
          <property name="deleteInvalidSessions" value="true"/>
          <!-- 是否开启会话验证器,默认是开启的 -->
          <property name="sessionValidationSchedulerEnabled" value="true"/>
     </bean>

2、在安全管理器中注入session管理器

<!-- 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" />
</bean>

Ps:以上操作针对没有将session管理器注入到安全管理器的同学。因为我们之后需要用到sessionManager,如果没有注入,在运行时会报‘’ServletContainerSessionManager cannot be cast to org.apache.shiro.web.session.mgt.DefaultWebSessionManager‘’错误;

二、在登录的controller中判断重复session,并把他们关闭

1、在controller中写入如下的方法,用于根据当前登录用户,筛选出已登录session中重复登录的信息集合

  /**
     * 
     * @param currentUser 当前登录用户的shiro认证信息
     * @return 重复登录的用户Session(shiro格式)
     * Description: 遍历同一个账户的现有的session用户信息并将重复的登录缓存信息输出
     * @author mylydg 
     * @date 
     */
    private List<Session> getLoginedSession(Subject currentUser) {
        //获得当前登录用户的全部session
        Collection<Session> list = ((DefaultSessionManager) ((DefaultSecurityManager) SecurityUtils
                .getSecurityManager()).getSessionManager()).getSessionDAO()
                .getActiveSessions();
        List<Session> loginedList = new ArrayList<Session>();
        Sys_User loginUser = (Sys_User) currentUser.getPrincipal();//获得当前用户信息
        for (Session session : list) {

            Subject s = new Subject.Builder().session(session).buildSubject();

            if (s.isAuthenticated()) {
                Sys_User user = (Sys_User) s.getPrincipal();

                if (user.getLogin_Name().equalsIgnoreCase(loginUser.getLogin_Name())) {
                    if (!session.getId().equals(
                            currentUser.getSession().getId())) {
                        loginedList.add(session);//把除当前登录用户的其他的同名用户session信息加入集合
                    }
                }
            }
        }
        return loginedList;
}    

2、在认证登录方法中关闭重复认证的session

@RequestMapping("/login")
    public String login(String username,String password,Model model) {
        //获取shiro的控制类Subject
         Subject subject = SecurityUtils.getSubject();
         //获取身份信息
         Sys_User cacheUser =(Sys_User)subject.getPrincipal();
         if(cacheUser!=null)
         {
             model.addAttribute("user", cacheUser);
             return "sys/user/main";
         }
        if(username == null) {
            return "sys/user/login";
        }
         //创建一个用于验证用户名和密码的token
         UsernamePasswordToken token = new UsernamePasswordToken(username, password);
         Sys_User user;
         try {
             //验证用户名密码
             subject.login(token);
//----------------------------关键代码   star--------------------------------------------
             //检查当前登录用户信息,将存在重复登录用户信息筛选出来
             List<Session> loginedList = getLoginedSession(subject);
             for (Session session : loginedList) {
                 session.stop();//关闭重复用户登录信息缓存
             }
//----------------------------关键代码   end--------------------------------------------
             //这个方法是获取shiro里面的登录信息,如果reaml里面传入的是对象,这里就是
             //对象,如果是字符串,这里就是字符串,按照相应的格式转换使用即可
             Object o = subject.getPrincipal();
             user = (Sys_User)o;
             System.out.println(user.getLogin_Name()+"   "+user.getPassword());
             model.addAttribute("user", user);
         } catch (UnknownAccountException e) {
             model.addAttribute("errormessage", "用户名错误!");
             System.out.println("userName 用户名错误!");
             return "sys/user/login";
         } catch (IncorrectCredentialsException e) {
             model.addAttribute("errormessage", "密码错误!");
             System.out.println("passwd 密码错误");
             return "sys/user/login";
         }

再次运行系统后,功能实现~