项目场景:
项目有二个要求:
1)同一个浏览器中只能登录一次用户
2)不同的浏览器中有登录,则以最后一次登录为准
问题描述
1)同一个浏览器中同用户只能登录一次,那么shiro自带的方法就可以实现。
2)如果是不同的浏览器中,则要检测是否已经登录,并且要让之前登录的账号自动失效。
解决方案:
只要进行Shiro的配制,让系统自动保存已登录的用户
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import net.sf.ehcache.CacheManager;
@Configuration
public class ShiroConfig {
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setGlobalSessionTimeout(1800000*1000);
sessionManager.setSessionDAO(sessionDAO());
return sessionManager;
}
@Bean
public SessionDAO sessionDAO(){
return new MemorySessionDAO();
}
@Bean
DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
defaultSecurityManager.setRealm(getUserRealm());
defaultSecurityManager.setSessionManager(sessionManager());
defaultSecurityManager.setCacheManager(ehCacheManager());
return defaultSecurityManager;
}
@Bean
EhCacheManager ehCacheManager() {
EhCacheManager ehCacheManager = new EhCacheManager();
CacheManager cacheManager = CacheManager.getCacheManager("sm");
if (cacheManager == null) {
InputStream is = null;
try {
is = ResourceUtils.getInputStreamForPath("classpath:ehcache-shiro.xml");
} catch (IOException e) {
e.printStackTrace();
}
cacheManager = CacheManager.create(is);
}
ehCacheManager.setCacheManager(cacheManager);
return ehCacheManager;
}
// Filter工厂,设置对应的过滤条件和跳转条件
@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new LinkedHashMap<>();
// authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
// 登出
map.put("logout", "logout");
map.put("/login", "anon");
// 对所有用户认证
map.put("/**", "authc");
// 登录
shiroFilterFactoryBean.setLoginUrl("/login");
// 首页
// shiroFilterFactoryBean.setSuccessUrl("/index");
// 错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean
UserRealm getUserRealm() {
UserRealm userRealm = new UserRealm();
return userRealm;
}
UserRealm代码:
public class UserRealm extends AuthorizingRealm {
/**
* 控制角色
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
/**
* 控制登录
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//.....
String usrname = (String) token.getPrincipal();
// 登录实现
Collection<Session> sessions = sessionDAO.getActiveSessions();
Object attribute;
for (Session session : sessions) {
attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (attribute == null) {
System.err.println(
SecurityUtils.getSubject().getSession().getId() + "-----" + session.getId() + ",未登录。");
} else {
User user = (User) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
if (user != null && user.getusername() != null && user.getusername().equalsIgnoreCase(usrname )) {
session.setTimeout(0);// 设置session立即失效,即将其踢出系统
break;
}
}
}
}
}
同一个浏览器中检测 :
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
// 同一个浏览器中会有提示
System.err.println("******************************************已经登录啦******************************");
}
不同浏览器中会进入doGetAuthorizationInfo验证判断,如果存在则会让其失效。
session.setTimeout(0);// 设置session立即失效,即将其踢出系统
调用登录方法:
AuthenticationToken authenticationToken = new UsernamePasswordToken(
user.getusername(), user.getPassword(), false);
subject.login(authenticationToken);
Object principal = SecurityUtils.getSubject().getPrincipal();
User entity = (User) principal;
//...
总结:
主要是Shiro的配制,这里我们全是用的Shiro自带的实现机制,当然这里只是实现的本地默认的session机制。