用shiro+springboot+mybatis实现禁止账号重复登录

springboot和mybatis整合在这我就不说了有兴趣的自己去找一下,网上有很多

下面介绍一下和shiro整合的一些配置以及怎么防止用户重复登录,什么叫防止重复登录?

嗯........ 好吧,我解释一下就是,一个账号在同一时间只能在一处登录,比如张三在北京登录了,还没有下线呢,李四也用同一账号在上海登录了,那就会把北京的张三挤下来,张三如果不服,也可以把李四挤下来。就像qq在电脑端一样,后面的会把前面的挤下线。

好,明白了。

下面进行技术讲解,哈哈哈哈


stringboot提倡习惯大于约定,所以基本没有xml文件配置,那些通过xml配置产生的交给Spring管理的bean都变成了这样

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

@Bean注解写在函数上面即表示该函数产生的bean交给了Spring管理,默认为函数的名字为bean的id


可以用@Qualifier("bean的id")来写在参数前


或者用@Resource("")写在变量上面



跑偏了,跑偏了


下面直接上shiro和Spring整合的配置文件吧


import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

@Configuration
public class ShiorConfig {


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

    @Bean(name = "sessionIdCookie")
    public SimpleCookie getSimpleCookie()
    {
        SimpleCookie simpleCookie = new SimpleCookie();
        simpleCookie.setName("SHRIOSESSIONID");
        return simpleCookie;
    }

    //配置shiro session 的一个管理器
    @Bean(name = "sessionManager")
    public DefaultWebSessionManager getDefaultWebSessionManager(@Qualifier("sessionDAO") MemorySessionDAO sessionDAO,
                                                                @Qualifier("sessionIdCookie") SimpleCookie simpleCookie)
    {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(sessionDAO);
        sessionManager.setSessionIdCookie(simpleCookie);
        return sessionManager;
    }

    //配置session的缓存管理器
    @Bean(name= "shiroCacheManager")
    public MemoryConstrainedCacheManager getMemoryConstrainedCacheManager()
    {
        return new MemoryConstrainedCacheManager();
    }

    @Bean(name="shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {

        //安全事务管理器工厂类
        ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
        bean.setSecurityManager(manager);
        //配置未登录时拦截到的路径
        bean.setLoginUrl("/user/notlogin");

        //配置访问权限
        LinkedHashMap<String, String> filterChainDefinitionMap=new LinkedHashMap<>();
        filterChainDefinitionMap.put("/user/validateCode","anon");//匿名访问验证码
        filterChainDefinitionMap.put("/user/insert", "anon");//匿名注册
        filterChainDefinitionMap.put("/user/login", "anon"); //匿名登录
        filterChainDefinitionMap.put("/AidocPlatform/index.html", "anon"); //匿名访问
        filterChainDefinitionMap.put("/u/**", "anon"); //匿名登录
        filterChainDefinitionMap.put("/*", "authc");//表示需要认证才可以访问
        filterChainDefinitionMap.put("/**", "authc");//表示需要认证才可以访问
        filterChainDefinitionMap.put("/*.*", "authc");
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return bean;
    }
    //配置核心安全事务管理器
    @Bean(name="securityManager")
    public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm,
                                           @Qualifier("shiroCacheManager") MemoryConstrainedCacheManager shiroCacheManager,
                                           @Qualifier("sessionManager") DefaultWebSessionManager sessionManager) {
        System.err.println("--------------shiro已经加载----------------");
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        manager.setRealm(authRealm);
        manager.setCacheManager(shiroCacheManager);
        manager.setSessionManager(sessionManager);
        return manager;
    }
    //配置自定义的权限登录器
    @Bean(name="authRealm")
    public AuthRealm authRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) {
        AuthRealm authRealm=new AuthRealm();
        authRealm.setCredentialsMatcher(matcher);
        return authRealm;
    }
    //配置自定义的密码比较器
    @Bean(name="credentialsMatcher")
    public CredentialsMatcher credentialsMatcher() {
        return new CredentialsMatcher();
    }
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager) {
        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(manager);
        return advisor;
    }

自定义密码比较器


import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;

public class CredentialsMatcher extends SimpleCredentialsMatcher {

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        UsernamePasswordToken utoken=(UsernamePasswordToken) token;
        //获得用户输入的密码:(可以采用加盐(salt)的方式去检验)
        String inPassword = new String(utoken.getPassword());
        //获得数据库中的密码
        String dbPassword=(String) info.getCredentials();
        //进行密码的比对
        return this.equals(inPassword, dbPassword);
    }

}


自定义Realm文件


import ai.medp.aidoc.dto.user.RoleDto;
import ai.medp.aidoc.dto.user.UserDto;
import ai.medp.aidoc.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class AuthRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Autowired
    private SessionDAO sessionDAO;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {

        UserDto user = (UserDto) principal.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户
        List<String> permissions = new ArrayList<>();
        Set<RoleDto> roles = user.getRoleDtos();
        if (roles.size() > 0) {
            for (RoleDto role : roles) {
                permissions.add(role.getRole());
            }
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(permissions);//将权限放入shiro中.
        return info;
    }


    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //获取用户输入的token
        UsernamePasswordToken utoken = (UsernamePasswordToken) token;
        String username = utoken.getUsername();

        //以下为只允许同一账户单个登录
       Collection<Session> sessions = sessionDAO.getActiveSessions();
        if(sessions.size()>0) {
            for (Session session : sessions) {
                System.out.println("::::::::::::::::" + session);
                //获得session中已经登录用户的名字
                if(null!=session.getAttribute("user")) {
                    String loginUsername = ((UserDto)session.getAttribute("user")).getEmail();
                    System.out.println("::::::::::::::::" + loginUsername);
                    if (username.equals(loginUsername)) {  //这里的username也就是当前登录的username
                        session.setTimeout(0);  //这里就把session清除,
                        System.out.println("清除了");
                    }
                }
            }
        }


        UserDto user = userService.findUserByUserName(username);
        Set<RoleDto> set = userService.getUserPermissionsName(user.getId());
        user.setRoleDtos(set);
        //放入shiro.调用CredentialsMatcher检验密码
        return new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());
    }
}

咳咳......   敲黑板  关键代码在这,登陆成功时我将用户保存到了shiro提供的session的Attribute中key值时user,所以现在拿到了session之后先判断能不能通过“user”取到值,如果取得到再进行匹配用户的名字(每个用户的名字必须不同)如果匹配上了,就将该session移除,系统会为新登录的用户新建一个session,之前的session失效之后之前的账户就无法继续操作就会被迫下线,当然,这是shiro中过滤器的功劳,大体上的逻辑就是这样,但是这种方法有个效率问题,如果一千万用户在线,那就要遍历一千万次,太慢了,以后再想到有其他什么方法再说吧

Collection<Session> sessions = sessionDAO.getActiveSessions();
        if(sessions.size()>0) {
            for (Session session : sessions) {
                System.out.println("::::::::::::::::" + session);
                //获得session中已经登录用户的名字
                if(null!=session.getAttribute("user")) {
                    String loginUsername = ((UserDto)session.getAttribute("user")).getEmail();
                    System.out.println("::::::::::::::::" + loginUsername);
                    if (username.equals(loginUsername)) {  //这里的username也就是当前登录的username
                        session.setTimeout(0);  //这里就把session清除,
                        System.out.println("清除了");
                    }
                }
            }
        }




评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值