springboot整合shiro後。前端
當同一個用戶重複登陸時,默認會兩個都登陸成功,兩個session。java
目標是:當第二次登陸時,把第一個session剔除。不容許重複登陸web
小知識:同一個瀏覽器,用兩個標籤頁分別登陸,是同一個session。spring
兩個瀏覽器登陸,是兩個session。數據庫
ShiroConfiguration.java
package com.zfzn.hospital_backend.application;
import com.zfzn.hospital_backend.shiro.AuthRealm;
import com.zfzn.hospital_backend.shiro.CredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
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.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 org.springframework.context.annotation.DependsOn;
import java.util.LinkedHashMap;
/**
* shiro的配置類
* @author Administrator
*
*/
@Configuration
public class ShiroConfiguration {
private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
/**
* ShiroFilterFactoryBean,是個factorybean,爲了生成ShiroFilter。
* 它主要保持了三項數據,securityManager,filters,filterChainDefinitionManager。
*
* @return
*/
@Bean(name="shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") org.apache.shiro.mgt.SecurityManager manager) {
logger.info("ShiroConfiguration.shiroFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必須設置SecuritManager
shiroFilterFactoryBean.setSecurityManager(manager);
// Map<String, Filter> filtersMap = shiroFilterFactoryBean.getFilters();
// filtersMap.put("authc", myFormAuthenticationFilter(manager));// 自定義攔截器
// shiroFilterFactoryBean.setFilters(filtersMap);
// 攔截器
//配置登陸的url和登陸成功的url
shiroFilterFactoryBean.setLoginUrl("/user/login");
// bean.setSuccessUrl("/home");
//配置訪問權限
LinkedHashMap<String, String> filterChainDefinitionMap=new LinkedHashMap<>();
// filterChainDefinitionMap.put("/jsp/login.jsp*", "anon"); //表示能夠匿名訪問
// filterChainDefinitionMap.put("/loginUser", "anon");
// filterChainDefinitionMap.put("/logout*","anon");
// filterChainDefinitionMap.put("/jsp/error.jsp*","anon");
// filterChainDefinitionMap.put("/jsp/index.jsp*","authc");
// filterChainDefinitionMap.put("/*", "authc");//表示須要認證才能夠訪問
// filterChainDefinitionMap.put("/**", "authc");//表示須要認證才能夠訪問
// filterChainDefinitionMap.put("/*.*", "authc");
filterChainDefinitionMap.put("/upload/**","authc");
filterChainDefinitionMap.put("/KeyDevice/**", "authc");//表示須要認證才能夠訪問
filterChainDefinitionMap.put("/CoolingTower/**", "authc");
filterChainDefinitionMap.put("/CoolingTowerDetail/**", "authc");
filterChainDefinitionMap.put("/Refrigerating/**", "authc");
filterChainDefinitionMap.put("/RefrigeratingDetail/**", "authc");
filterChainDefinitionMap.put("/LKInterface/**", "authc");
filterChainDefinitionMap.put("/hospitalService/**", "authc");
filterChainDefinitionMap.put("/historyData/**", "authc");
filterChainDefinitionMap.put("/deviceManage/**", "authc");
filterChainDefinitionMap.put("/dictionary/**", "authc");
filterChainDefinitionMap.put("/WebSocketService/**", "authc");
filterChainDefinitionMap.put("/AccidentRecord/**", "authc");
filterChainDefinitionMap.put("/DataUpload/**", "authc");
filterChainDefinitionMap.put("/DataUploadLog/**", "authc");
filterChainDefinitionMap.put("/NewTrendDetail/getNewTrendDetail/**", "authc");
filterChainDefinitionMap.put("/NewTrend/**", "authc");
filterChainDefinitionMap.put("/ServiceProvider/**", "authc");
filterChainDefinitionMap.put("/buildService/**", "authc");
filterChainDefinitionMap.put("/Sewage/**", "authc");
filterChainDefinitionMap.put("/SewageDetail/**", "authc");
filterChainDefinitionMap.put("/AirCondtion/**", "authc");
filterChainDefinitionMap.put("/AirCondtionDetail/**", "authc");
filterChainDefinitionMap.put("/AccidentRecord/**", "authc");
filterChainDefinitionMap.put("/system/**", "authc");
filterChainDefinitionMap.put("/Repairman/**", "authc");
filterChainDefinitionMap.put("/maintainInspection/**", "authc");
filterChainDefinitionMap.put("/RepairmanTeam/**", "authc");
filterChainDefinitionMap.put("/EnergyConsumption/**", "authc");
filterChainDefinitionMap.put("/DeviceGroup/**", "authc");
filterChainDefinitionMap.put("/device/**", "authc");
filterChainDefinitionMap.put("/AnlysisRepair/**", "authc");
filterChainDefinitionMap.put("/AnlysisMaintain/**", "authc");
filterChainDefinitionMap.put("/propertyManage/**", "authc");
filterChainDefinitionMap.put("/Boiler/**", "authc");
filterChainDefinitionMap.put("/BoilerDetail/**", "authc");
filterChainDefinitionMap.put("/QueryConfigure/**", "authc");
filterChainDefinitionMap.put("/elevator/**", "authc");
// // 若是不設置默認會自動尋找工程根目錄下的"/login"頁面
// shiroFilterFactoryBean.setLoginUrl("/login");
// // 登陸成功後要跳轉的連接
// shiroFilterFactoryBean.setSuccessUrl("/index");
// // 未受權界面;
// shiroFilterFactoryBean.setUnauthorizedUrl("/403");
// 加載shiroFilter權限控制規則
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
//配置核心安全事務管理器
@Bean(name="securityManager")
public org.apache.shiro.mgt.SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
System.err.println("--------------shiro已經加載----------------");
DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
manager.setRealm(authRealm);
manager.setSessionManager(sessionManager());
// <!-- 用戶受權/認證信息Cache, 採用EhCache 緩存 -->
manager.setCacheManager(ehCacheManager());
//注入記住我管理器;
manager.setRememberMeManager(rememberMeManager());
return manager;
}
@Bean(name = "sessionManager")
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
return sessionManager;
}
//配置自定義的權限登陸器
@Bean(name="authRealm")
public AuthRealm authRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) {
System.out.println("authRealm");
AuthRealm authRealm=new AuthRealm();
authRealm.setCredentialsMatcher(matcher);
return authRealm;
}
//配置自定義的密碼比較器
@Bean(name="credentialsMatcher")
public CredentialsMatcher credentialsMatcher() {
System.out.println("credentialsMatcher");
return new CredentialsMatcher();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") org.apache.shiro.mgt.SecurityManager manager) {
System.out.println("authorizationAttributeSourceAdvisor");
AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(manager);
return advisor;
}
//-----------------------------------------------
/**
* LifecycleBeanPostProcessor,這是個DestructionAwareBeanPostProcessor的子類,
* 負責org.apache.shiro.util.Initializable類型bean的生命週期的,初始化和銷燬。
* 主要是AuthorizingRealm類的子類,以及EhCacheManager類。
*
* @return
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
logger.info("ShiroConfiguration.getLifecycleBeanPostProcessor()");
return new LifecycleBeanPostProcessor();
}
/**
* HashedCredentialsMatcher,這個類是爲了對密碼進行編碼的,防止密碼在數據庫裏明碼保存,
* 固然在登錄認證的生活,這個類也負責對form裏輸入的密碼進行編碼。
*
* @return
*/
// @Bean(name = "hashedCredentialsMatcher")
// public HashedCredentialsMatcher hashedCredentialsMatcher() {
// HashedCredentialsMatcher credentialsMatcher = new
// HashedCredentialsMatcher();
// credentialsMatcher.setHashAlgorithmName("MD5");
// credentialsMatcher.setHashIterations(2);
// credentialsMatcher.setStoredCredentialsHexEncoded(true);
// return credentialsMatcher;
// }
/**
* ShiroRealm,這是個自定義的認證類,繼承自AuthorizingRealm, 負責用戶的認證和權限的處理
*
* @return
*/
@Bean(name = "myShiroRealm")
@DependsOn("lifecycleBeanPostProcessor")
public AuthRealm myShiroRealm() {
logger.info("ShiroConfiguration.myShiroRealm()");
AuthRealm myShiroRealm = new AuthRealm();
return myShiroRealm;
}
/**
* EhCacheManager,緩存管理,用戶登錄成功後,把用戶信息和權限信息緩存起來,
* 而後每次用戶請求時,放入用戶的session中,若是不設置這個bean,每一個請求都會查詢一次數據庫。
*
* @return
*/
@Bean(name = "ehCacheManager")
@DependsOn("lifecycleBeanPostProcessor")
public EhCacheManager ehCacheManager() {
logger.info("ShiroConfiguration.ehCacheManager()");
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return cacheManager;
}
@Bean(name = "rememberMeCookie")
public SimpleCookie rememberMeCookie() {
logger.info("ShiroConfiguration.rememberMeCookie()");
// 這個參數是cookie的名稱,對應前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
// <!-- 記住我cookie生效時間30天 ,單位秒;-->
simpleCookie.setMaxAge(259200);
return simpleCookie;
}
/**
* cookie管理對象;
*
* @return
*/
@Bean(name = "rememberMeManager")
public CookieRememberMeManager rememberMeManager() {
logger.info("ShiroConfiguration.rememberMeManager()");
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
return cookieRememberMeManager;
}
// @Bean
// public MyFormAuthenticationFilter myFormAuthenticationFilter(@Qualifier("securityManager") org.apache.shiro.mgt.SecurityManager manager) {
// MyFormAuthenticationFilter myFormAuthenticationFilter = new MyFormAuthenticationFilter();
// myFormAuthenticationFilter.setSecurityManager(manager);
// return myFormAuthenticationFilter;
// }
/**
* DefaultAdvisorAutoProxyCreator,Spring的一個bean,由Advisor決定對哪些類的方法進行AOP代理。
*
* @return
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
logger.info("ShiroConfiguration.defaultAdvisorAutoProxyCreator()");
DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
daap.setProxyTargetClass(true);
return daap;
}
}
_____________________________________________________________
AuthRealm.java
package com.zfzn.hospital_backend.shiro;
import com.zfzn.hospital_backend.dao.IotdModuleDao;
import com.zfzn.hospital_backend.dao.IotdModuleRoleDao;
import com.zfzn.hospital_backend.dao.IotdRoleDao;
import com.zfzn.hospital_backend.dao.IotdUserDao;
import com.zfzn.hospital_backend.entity.IotdModuleEntity;
import com.zfzn.hospital_backend.entity.IotdModuleRoleEntity;
import com.zfzn.hospital_backend.entity.IotdRoleEntity;
import com.zfzn.hospital_backend.entity.IotdUserEntity;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.*;
public class AuthRealm extends AuthorizingRealm {
@Autowired
private IotdUserDao iotdUserDao;
@Autowired
IotdRoleDao iotdRoleDao;
@Autowired
IotdModuleRoleDao iotdModuleRoleDao;
@Autowired
IotdModuleDao iotdModuleDao;
//認證.登陸
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("------------------------------------doGetAuthenticationInfo(AuthenticationToken token)");
UsernamePasswordToken utoken = (UsernamePasswordToken) token;//獲取用戶輸入的token
String username = utoken.getUsername();
System.out.println("username=" + username);
System.out.println("password=" + utoken.getPassword());
//處理session
SessionsSecurityManager securityManager = (SessionsSecurityManager) SecurityUtils.getSecurityManager();
DefaultSessionManager sessionManager = (DefaultSessionManager) securityManager.getSessionManager();
Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();//獲取當前已登陸的用戶session列表
for (Session session : sessions) {
//清除該用戶之前登陸時保存的session
// IotdUserEntity en=(IotdUserEntity)(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY));
// String phone=en.getPhone();
//若是和當前session是同一個session,則不剔除
if (SecurityUtils.getSubject().getSession().getId().equals(session.getId()))
break;
IotdUserEntity user = (IotdUserEntity) (session.getAttribute("user"));
if (user != null) {
String phone = user.getPhone();
if (username.equals(phone)) {
System.out.println(username + "已登陸,剔除中...");
sessionManager.getSessionDAO().delete(session);
}
}
}
// User user = userService.findUserByUserName(username);
IotdUserEntity user = iotdUserDao.findDistinctByPhone(username);
SimpleAuthenticationInfo rst = new SimpleAuthenticationInfo(user, user.getPwd(), this.getClass().getName());//放入shiro.調用CredentialsMatcher檢驗密碼
return rst;
}
//受權
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
System.out.println("----------------------doGetAuthorizationInfo(PrincipalCollection principal)");
IotdUserEntity user = (IotdUserEntity) principal.fromRealm(this.getClass().getName()).iterator().next();//獲取session中的用戶
List<String> permissions = new ArrayList<>();
// Set<Role> roles = user.getRoleid();
Integer roleid = user.getRoleid();
System.out.println("roleid=" + roleid);
String roleName = "";
if (roleid > 0) {
IotdRoleEntity role = iotdRoleDao.findDistinctById(roleid);
roleName = role.getRole();
List<IotdModuleRoleEntity> moduleRoleList = iotdModuleRoleDao.findByRoleid(roleid);
for (IotdModuleRoleEntity moduleRole : moduleRoleList) {
Integer module_id = moduleRole.getModuleId();
IotdModuleEntity module = iotdModuleDao.findById(module_id);
permissions.add(module.getMname());
}
}
System.out.println("permissions=" + permissions);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roles = new HashSet<>();
roles.add(roleName);
info.setRoles(roles);
info.addStringPermissions(permissions);//將權限放入shiro中.
return info;
}
}
______________________________________________________________________________
CredentialsMatcher.java
package com.zfzn.hospital_backend.shiro;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) { System.out.println("doCredentialsMatch"); UsernamePasswordToken utoken=(UsernamePasswordToken) token; //得到用戶輸入的密碼:(能夠採用加鹽(salt)的方式去檢驗) String inPassword = new String(utoken.getPassword()); System.out.println("得到用戶輸入的密碼:"+inPassword); //得到數據庫中的密碼 String dbPassword=(String) info.getCredentials(); System.out.println("得到數據庫中的密碼:"+dbPassword); //進行密碼的比對 return this.equals(inPassword, dbPassword); }}