SpringBoot整合shiro之禁止重复登陆(附源码)

禁止重复登陆,就是同一个账号在不同的浏览器,不同的设备登陆的时候只会有一个登陆成功,其他的都会被挤下去。

就像,我登的我的账号,这个时候你再来登我的账号,我就被挤下去了。

本文将介绍如何使用shiro禁止重复登陆。

每一个登陆用户登陆后都会存在于 shiro 的一个 session里面。当然,这里我们需要设置,登陆成功后

Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("USER_SESSION", user);

所以,解决重复登陆的思路就是,当前用户登陆的时候去session里面查询,所有的session,看有没有跟当前登陆用户一样的用户已经登陆,比如说,比较用户名。如果有,则将其剔除, session.setTimeout(0);

不知道springboot如何集成shiro的可以看我的上篇文章

代码示例:

1.shiro配置。

/**
 * shiro 配置
 */
@Configuration
public class ShiroConfig {

    private final static Logger logger = LoggerFactory.getLogger(ShiroConfig.class);

    // 下面两个方法对 注解权限起作用有很大的关系,请把这两个方法,放在配置的最上面
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        autoProxyCreator.setProxyTargetClass(true);
        return autoProxyCreator;
    }

    //将自己的验证方式加入容器
    @Bean
    public MyRealm myRealm() {
        System.out.println( "注入 realm" );
        MyRealm myRealm = new MyRealm();
        return myRealm;
    }

    // 配置sessionDAO
    @Bean(name="sessionDAO")
    public MemorySessionDAO getMemorySessionDAO(){
        MemorySessionDAO sessionDAO = new MemorySessionDAO();
        return sessionDAO;
    }

    //配置shiro session 的一个管理器
    @Bean(name = "sessionManager")
    public DefaultWebSessionManager getDefaultWebSessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 设置session过期时间
        sessionManager.setGlobalSessionTimeout(60*60*1000);
        // 请注意看代码
        sessionManager.setSessionDAO(getMemorySessionDAO());
        return sessionManager;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm( myRealm() );
        // 将sessionDAO放进来
        defaultWebSecurityManager.setSessionManager( getDefaultWebSessionManager() );
        return defaultWebSecurityManager;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
            DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        System.out.println( "shiro 过滤器" );
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //拦截器.
        Map filterChainDefinitionMap = new LinkedHashMap();

        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        //:这是一个坑呢,一不小心代码就不好使了;
        //
        filterChainDefinitionMap.put("/**", "authc");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login.html");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index.html");

        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403.html");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

}

2.realm 配置

在认证那里加上,登陆成功后,从session里面判断当前用户是否有重复登陆

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //加这一步的目的是在Post请求的时候会先进认证,然后在到请求
        if (authenticationToken.getPrincipal() == null) {
            return null;
        }
        String username = (String)authenticationToken.getPrincipal();
        String password = new String((char[])authenticationToken.getCredentials());
        System.out.println( "username="+username+",password="+password );

        // 获取用户名。通过 username 找到该用户
        SysUser user = sysUserService.getUserByUserNameWithPermission(username);
        if( !"1".equals(user.getStatus()) ){
            throw new LockedAccountException();
        }

        if( username.equals( user.getUsername() ) && password.equals( user.getPassword() ) ){
            // 获取所有session
            Collection sessions = sessionDAO.getActiveSessions();
            for (Session session: sessions) {
                SysUser sysUser = (SysUser)session.getAttribute("USER_SESSION");
                // 如果session里面有当前登陆的,则证明是重复登陆的,则将其剔除
                if( sysUser!=null ){
                    if( username.equals( sysUser.getUsername() ) ){
                        session.setTimeout(0);
                    }
                }
            }
        }

        // 从数据库查询出来的用户名密码,进行验证
        // 用户名,密码,密码盐值,realm 名称
        // 登陆的时候直接调用 subject.login() 即可自动调用该方法
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
                authenticationToken.getPrincipal() , user.getPassword() , getName()
        );

        Session session = SecurityUtils.getSubject().getSession();
        session.setAttribute("USER_SESSION", user);
        return info;
    }

3.效果

就是我在 google浏览器上登陆了,再去 ie上登陆了,然后谷歌的就需要再次登陆了,成功的被挤下线了。

4.代码地址:

https://github.com/winterme/pkusoft/

你可能感兴趣的:(springboot,shiro,SpringBoot菜鸟教程)