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("清除了"); } } } }