Commit aa8985b0 authored by xc's avatar xc

shiro限定并发登录人数

parent 3dacc61f
......@@ -67,7 +67,7 @@ public class AccountSerivceImpl implements AccountSerivce {
//返回数据
try {
subject.login(userToken);
kickoutSessionControlFilter.changeSession(1);
kickoutSessionControlFilter.changeSession(2);
AccountDto accountDto = new AccountDto();
AccountInfo info=(AccountInfo)SecurityUtils.getSubject().getPrincipal();
accountDto.setId(info.getId());
......
......@@ -161,7 +161,7 @@ public class UserLoginServiceImpl extends ServiceImpl<UsersMapper, User> impleme
UserToken userToken = new UserToken(userVo.getPhone(), userVo.getCode(), CODE_LOGIN_TYPE);
try {
subject.login(userToken);
kickoutSessionControlFilter.changeSession(2);
kickoutSessionControlFilter.changeSession(1);
//3、构造返回参数
UserInfoVo userInfoVo = new UserInfoVo();
userInfoVo.setUserId(user.getId());
......@@ -209,7 +209,7 @@ public class UserLoginServiceImpl extends ServiceImpl<UsersMapper, User> impleme
UserToken userToken = new UserToken(userVo.getPhone(), credentialsSalt, USER_LOGIN_TYPE);
try {
subject.login(userToken);
kickoutSessionControlFilter.changeSession(2);
kickoutSessionControlFilter.changeSession(1);
//3、构造返回参数
UserInfoVo userInfoVo = new UserInfoVo();
userInfoVo.setUserId(user.getId());
......
......@@ -227,7 +227,6 @@ public class ShiroConfig {
@Bean
public KickoutSessionControlFilter kickoutSessionControlFilter() {
KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
kickoutSessionControlFilter.setCacheManager(cacheManagers());
kickoutSessionControlFilter.setSessionManager(sessionManager());
kickoutSessionControlFilter.setKickoutAfter(false);
kickoutSessionControlFilter.setMaxSession(1);
......
......@@ -37,14 +37,16 @@ import java.util.concurrent.TimeUnit;
@Slf4j
public class KickoutSessionControlFilter extends AccessControlFilter{
private final Logger logger = LoggerFactory.getLogger(KickoutSessionControlFilter.class);
private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
private int maxSession = 1; //同一个帐号最大会话数 默认1
private MySessionManager sessionManager;
private Cache<String, Deque<Serializable>> cache;
private static final long EXPIRE_TIME = 30 * 60;
//设置session超时时长,30分钟(1800000毫秒)
//session.setTimeout(1800000);
//5分钟
private static final long EXPIRE_TIME = 5 * 60;
private static final String DEFAULT_KICKOUT_CACHE_KEY_PREFIX = "shiro:cache:kickout:";
......@@ -65,12 +67,6 @@ public class KickoutSessionControlFilter extends AccessControlFilter{
this.sessionManager = sessionManager;
}
//设置Cache的key的前缀
public void setCacheManager(CacheManager cacheManager) {
this.cache = cacheManager.getCache("shiro_redis_cache");
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
//如果是小程序,就放行
......@@ -83,18 +79,15 @@ public class KickoutSessionControlFilter extends AccessControlFilter{
log.info("KickoutSessionControlFilter Not Login begin.......");
Subject subject = getSubject(request, response);
Map<String, Object> map = new HashMap<>();
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// 没有登陆或没有设置“记住我”
if (!subject.isAuthenticated() && !subject.isRemembered()) {
log.info("KickoutSessionControlFilter isAuthenticated and isRemembered return true .......");
return true;
log.info("KickoutSessionControlFilter isAuthenticated and isRemembered Please login first .......");
// 没有登陆,抛出异常
thrLogoutException(httpServletResponse, "1001", "Please login first");
return false;
}
Session session = subject.getSession();
//设置session超时时长,30分钟(1800000毫秒)
//session.setTimeout(1800000);
//5分钟
session.setTimeout(300000);
Serializable sessionId = session.getId();
Integer userId = null;
try {
......@@ -112,7 +105,7 @@ public class KickoutSessionControlFilter extends AccessControlFilter{
userId = staff.getId();
}
}
log.info("KickoutSessionControlFilter 如果被踢出了,直接退出,重定向到踢出后的地址-----");
log.info("KickoutSessionControlFilter 如果被踢出了,直接退出,重定向到踢出后的地址-----返回1002");
//如果被踢出了,直接退出,重定向到踢出后的地址
if (session.getAttribute("kickout") != null) {
log.info("------" + "踢出用户" + userId + "登录sessionId=" + sessionId + "------");
......@@ -120,33 +113,23 @@ public class KickoutSessionControlFilter extends AccessControlFilter{
try {
//退出登录
subject.logout();
thrLogoutException(httpServletResponse, "1002", "您已经在其他地方登录,请重新登录。如有疑问请联系管理员!");
} catch (Exception e) {
e.printStackTrace();
}
saveRequest(request);
map.put("status", "1002");
map.put("message", "您已经在其他地方登录,请重新登录。如有疑问请联系管理员!");
out(response, map);
return false;
}
log.info("KickoutSessionControlFilter Not Login end.......");
return true;
}
private void out(ServletResponse response, Map<String, Object> map) throws IOException {
response.setContentType("text/json");
//设置字符集为'UTF-8'
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.write(JSON.toJSONString(map, SerializerFeature.WriteMapNullValue));
out.flush();
out.close();
}
public boolean isFilter(HttpServletRequest request) {
return null != request.getHeader("identity") && request.getHeader("identity").equals("miniprogram");
String url = request.getRequestURI();
boolean result = null != request.getHeader("identity") && request.getHeader("identity").equals("miniprogram");
boolean result1 = url.indexOf("/login")!=-1;
return result || result1;
}
public void changeSession(int type){
log.info("KickoutSessionControlFilter changeSession begin.......type : "+type);
Subject subject = SecurityUtils.getSubject();
......@@ -194,18 +177,18 @@ public class KickoutSessionControlFilter extends AccessControlFilter{
log.info("KickoutSessionControlFilter changeSession end.......");
}
String getRedisKickoutKey(Integer userId) {
private String getRedisKickoutKey(Integer userId) {
return keyprefix + userId;
}
// 抛出未登录异常
private void thrLogoutException(HttpServletResponse response){
private void thrLogoutException(HttpServletResponse response,String ErrorCode,String ErrorMsg){
PrintWriter writer = null;
try {
Result result = new Result();
result.setResult(Result.RESULT_FLG.FAIL.getValue());
// result.setErrorCode();
// result.setErrorMsg();
result.setErrorCode(ErrorCode);
result.setErrorMsg(ErrorMsg);
response.setContentType("application/json; charset=UTF-8");
writer = response.getWriter();
writer.write(JSON.toJSONString(result));
......
package cn.wisenergy.web.shir.cache;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
public class MySessionManager extends DefaultWebSessionManager {
//前端传递sessionid参数名称
private static final String AUTHORIZATION = "sessionId";
private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
public MySessionManager() {
super();
}
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
//如果请求头中有 token 则其值为sessionId
if (!StringUtils.isEmpty(id)) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
} else {
//否则按默认规则从cookie取sessionId
return super.getSessionId(request, response);
}
}
}
package cn.wisenergy.web.shir.filter;
import com.alibaba.fastjson.JSONObject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.http.HttpStatus;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* 跨域配置拦截器,继承FormAuthenticationFilter
*/
public class AuthenticationFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
// 错误异常提示
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
String sessionId = ((HttpServletRequest) request).getHeader("sessionId");
if (sessionId == null) {
setHeader(httpRequest, httpResponse);
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json");
Map<String, Object> map = new HashMap<>();
map.put("status", "1001");
map.put("message", "登录已超时,请重新登录!");
httpResponse.getWriter().write(JSONObject.toJSONString(map));
return false;
} else {
return true;
}
}
/**
* 为response设置header,实现跨域
*/
private void setHeader(HttpServletRequest request, HttpServletResponse response) {
//跨域的header设置
response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", request.getMethod());
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
//防止乱码,适用于传输JSON数据
//Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild
response.setHeader("Content-Type", "application/json;charset=UTF-8");
response.setStatus(HttpStatus.OK.value());
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (request instanceof HttpServletRequest) {
if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
return true;
}
}
//小程序放行
if(isFilter((HttpServletRequest) request)){
return true;
}
return super.isAccessAllowed(request, response, mappedValue);
}
//判断请求头中是否带有identity(标识是小程序)
public boolean isFilter(HttpServletRequest request) {
return null != request.getHeader("identity") && request.getHeader("identity").equals("miniprogram");
}
}
package cn.wisenergy.web.shir.filter;
import cn.wisenergy.model.app.AccountInfo;
import cn.wisenergy.model.app.Staff;
import cn.wisenergy.model.app.User;
import cn.wisenergy.web.shir.cache.MySessionManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.itextpdf.text.log.Logger;
import com.itextpdf.text.log.LoggerFactory;
import org.apache.logging.log4j.ThreadContext;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.ExpiredSessionException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
public class KickoutSessionControlFilter extends AccessControlFilter {
private final Logger logger = LoggerFactory.getLogger(KickoutSessionControlFilter.class);
private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
private int maxSession = 1; //同一个帐号最大会话数 默认1
private MySessionManager sessionManager;
private Cache<String, Deque<Serializable>> cache;
public void setKickoutAfter(boolean kickoutAfter) {
this.kickoutAfter = kickoutAfter;
}
public void setMaxSession(int maxSession) {
this.maxSession = maxSession;
}
public void setSessionManager(MySessionManager sessionManager) {
this.sessionManager = sessionManager;
}
//设置Cache的key的前缀
/* public void setCacheManager(CacheManager cacheManager) {
this.cache = cacheManager.getCache("shiro_redis_cache");
}*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
//如果是小程序,就放行
boolean filter = isFilter((HttpServletRequest) request);
return filter;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
Subject subject = getSubject(request, response);
Map<String, Object> map = new HashMap<>();
//判断是否登录
if (!subject.isAuthenticated() && !subject.isRemembered()) {
return true;
}
Session session = subject.getSession();
//设置session超时时长,30分钟(1800000毫秒)
//session.setTimeout(1800000);
//5分钟
session.setTimeout(300000);
Serializable sessionId = null;
String username = null;
// Deque<Serializable> deque = null;
try {
//客户端
User user = (User) SecurityUtils.getSubject().getPrincipal();
username = user.getPhone();
sessionId = session.getId();
//读取缓存,没有就存入
//deque = cache.get(username);
} catch (Exception e) {
try {
//管理端
sessionId = session.getId();
AccountInfo accountInfo = (AccountInfo) SecurityUtils.getSubject().getPrincipal();
username = accountInfo.getUserName();
//读取缓存,没有就存入
//deque = cache.get(username);
} catch (Exception en) {
//员工端
Staff staff = (Staff) SecurityUtils.getSubject().getPrincipal();
username = staff.getLoginName();
sessionId = session.getId();
//读取缓存,没有就存入
//deque = cache.get(username);
}
}
/* //如果此用户没有session队列,也就是还没有登录过,缓存中没有,就new一个空队列,不然deque对象为空,会报空指针
if (deque == null) {
deque = new LinkedList<>();
}
//如果队列里没有此sessionId,且用户没有被踢出;放入队列
if (!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
//将sessionId存入队列
deque.push(sessionId);
//将用户的sessionId队列缓存
cache.put(username, deque);
}
//如果队列里的sessionId数超出最大会话数,开始踢人
while (deque.size() > maxSession) {
Serializable kickoutSessionId;
if (kickoutAfter) { //如果踢出后者
kickoutSessionId = deque.removeFirst();
//踢出后再更新下缓存队列
} else { //否则踢出前者
kickoutSessionId = deque.removeLast();
//踢出后再更新下缓存队列
}
cache.put(username, deque);
try {
//获取被踢出的sessionId的session对象
DefaultSessionKey defaultSessionKey = new DefaultSessionKey(kickoutSessionId);
Session kickoutSession = sessionManager.getSession(defaultSessionKey);
//Session kickoutSession = (Session) sessionManager.getSession(String.valueOf(new DefaultSessionKey(kickoutSessionId)));
if (kickoutSession != null) {
//设置会话的kickout属性表示踢出了
kickoutSession.setAttribute("kickout", true);
}
} catch (Exception e) {
}
}*/
//如果被踢出了,直接退出,重定向到踢出后的地址
if (session.getAttribute("kickout") != null) {
logger.info("------" + "踢出用户" + username + "登录sessionId=" + sessionId + "------");
//会话被踢出了
try {
//退出登录
subject.logout();
} catch (Exception e) {
}
saveRequest(request);
map.put("status", "1002");
map.put("message", "您已经在其他地方登录,请重新登录。如有疑问请联系管理员!");
out(response, map);
return false;
}
return true;
}
private void out(ServletResponse response, Map<String, Object> map) throws IOException {
response.setContentType("text/json");
//设置字符集为'UTF-8'
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.write(JSON.toJSONString(map, SerializerFeature.WriteMapNullValue));
out.flush();
out.close();
}
public boolean isFilter(HttpServletRequest request) {
return null != request.getHeader("identity") && request.getHeader("identity").equals("miniprogram");
}
}
package cn.wisenergy.web.shir.realm;
import cn.wisenergy.common.utils.Md5Util;
import cn.wisenergy.mapper.AccountMapper;
import cn.wisenergy.model.app.AccountInfo;
import cn.wisenergy.service.util.UserToken;
import cn.wisenergy.web.shir.util.SessionUntil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
public class AdminRealm extends AuthorizingRealm {
@Autowired
private AccountMapper accountMapper;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UserToken userToken = (UserToken)token;
String userName=userToken.getUsername();
QueryWrapper<AccountInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_name", userName);
queryWrapper.eq("is_delete", 0);
AccountInfo accountInfo = accountMapper.selectOne(queryWrapper);
if (accountInfo == null) {
return null;
}
String password = Md5Util.digestMD5(accountInfo.getPassword());
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
accountInfo,
password,
accountInfo.getUserName()
);
SessionUntil sessionUntil = new SessionUntil();
sessionUntil.changeSession(2);
return authenticationInfo;
}
}
package cn.wisenergy.web.shir.realm;
import cn.wisenergy.mapper.UsersMapper;
import cn.wisenergy.model.app.User;
import cn.wisenergy.model.enums.SourceType;
import cn.wisenergy.service.cache.RedisService;
import cn.wisenergy.service.common.CachePrefix;
import cn.wisenergy.service.common.Common;
import cn.wisenergy.service.util.UserToken;
import cn.wisenergy.web.shir.util.SessionUntil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
public class CodeRealm extends AuthorizingRealm {
@Autowired
private UsersMapper usersMapper;
@Autowired
private RedisService redisService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UserToken userToken = (UserToken)token;
String userName=userToken.getUsername();
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("phone", userName);
queryWrapper.eq("is_delete", 0);
User userInfo = usersMapper.selectOne(queryWrapper);
if (userInfo == null) {
return null;
}
String source = SourceType.getByCode(userInfo.getSource());
//获取短信验证码key
String key = CachePrefix.SMS_CODE.getPrefix() + "_" + source + "_" + userInfo.getPhone();
Object obj = redisService.get(key);
String code = obj.toString();
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
userInfo,
code,
userInfo.getPhone()
);
SessionUntil sessionUntil = new SessionUntil();
sessionUntil.changeSession(1);
return authenticationInfo;
}
}
package cn.wisenergy.web.shir.realm;
import cn.wisenergy.mapper.StaffMapper;
import cn.wisenergy.model.app.Staff;
import cn.wisenergy.service.util.UserToken;
import cn.wisenergy.web.shir.util.SessionUntil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
public class StaffRealm extends AuthorizingRealm {
@Autowired
private StaffMapper staffMapper;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UserToken userToken = (UserToken)token;
String userName=userToken.getUsername();
QueryWrapper<Staff> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("login_name", userName);
queryWrapper.eq("is_delete", 0);
Staff staff = staffMapper.selectOne(queryWrapper);
if (staff == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
staff,
staff.getPassword(),
staff.getLoginName()
);
SessionUntil sessionUntil = new SessionUntil();
sessionUntil.changeSession(3);
return authenticationInfo;
}
}
package cn.wisenergy.web.shir.realm;
import cn.wisenergy.mapper.UsersMapper;
import cn.wisenergy.model.app.User;
import cn.wisenergy.service.util.UserToken;
import cn.wisenergy.web.shir.util.SessionUntil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.Serializable;
public class UserRealm extends AuthorizingRealm {
@Autowired
private UsersMapper usersMapper;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UserToken userToken = (UserToken)token;
String userName=userToken.getUsername();
//通过username从数据库中查找 User对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("phone", userName);
queryWrapper.eq("is_delete", 0);
User userInfo = usersMapper.selectOne(queryWrapper);
if (userInfo == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
userInfo,
userInfo.getPassword(),
userInfo.getPhone()
);
return authenticationInfo;
}
}
package cn.wisenergy.web.shir.util;
import cn.wisenergy.model.app.AccountInfo;
import cn.wisenergy.model.app.Staff;
import cn.wisenergy.model.app.User;
import cn.wisenergy.web.shir.cache.MySessionManager;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.subject.Subject;
import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;
public class SessionUntil {
private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
private int maxSession = 1; //同一个帐号最大会话数 默认1
private MySessionManager sessionManager;
private Cache<String, Deque<Serializable>> cache;
public void setKickoutAfter(boolean kickoutAfter) {
this.kickoutAfter = kickoutAfter;
}
public void setMaxSession(int maxSession) {
this.maxSession = maxSession;
}
public void setSessionManager(MySessionManager sessionManager) {
this.sessionManager = sessionManager;
}
//设置Cache的key的前缀
public void setCacheManager(CacheManager cacheManager) {
this.cache = cacheManager.getCache("shiro_redis_cache");
}
public void changeSession(int type){
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.setTimeout(300000);
Serializable sessionId = session.getId();
String username = null;
Deque<Serializable> deque = null;
if(type == 1){
User user=(User)subject.getPrincipal();
username=user.getPhone();
}else if(type == 2){
AccountInfo account=(AccountInfo)subject.getPrincipal();
username=account.getUserName();
}else{
Staff staff=(Staff)subject.getPrincipal();
username=staff.getLoginName();
}
//读取缓存,没有就存入
deque = cache.get(username);
//如果此用户没有session队列,也就是还没有登录过,缓存中没有,就new一个空队列,不然deque对象为空,会报空指针
if (deque == null) {
deque = new LinkedList<>();
}
//如果队列里没有此sessionId,且用户没有被踢出;放入队列
if (!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
//将sessionId存入队列
deque.push(sessionId);
//将用户的sessionId队列缓存
cache.put(username, deque);
}
//如果队列里的sessionId数超出最大会话数,开始踢人
while (deque.size() > maxSession) {
Serializable kickoutSessionId;
if (kickoutAfter) { //如果踢出后者
kickoutSessionId = deque.removeFirst();
//踢出后再更新下缓存队列
} else { //否则踢出前者
kickoutSessionId = deque.removeLast();
//踢出后再更新下缓存队列
}
cache.put(username, deque);
try {
//获取被踢出的sessionId的session对象
DefaultSessionKey defaultSessionKey = new DefaultSessionKey(kickoutSessionId);
Session kickoutSession = sessionManager.getSession(defaultSessionKey);
//Session kickoutSession = (Session) sessionManager.getSession(String.valueOf(new DefaultSessionKey(kickoutSessionId)));
if (kickoutSession != null) {
//设置会话的kickout属性表示踢出了
kickoutSession.setAttribute("kickout", true);
}
} catch (Exception e) {
}
}
}
}
package cn.wisenergy.web.shir.util;
import cn.wisenergy.model.app.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.LogoutAware;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisSessionDAO;
import java.util.Collection;
import java.util.Objects;
public class ShiroUtils {
/**
* 私有构造器
**/
private ShiroUtils() {
}
private static RedisSessionDAO redisSessionDAO = SpringUtil.getBean(RedisSessionDAO.class);
/**
* 获取当前用户Session
*
* @Return SysUserEntity 用户信息
*/
public static Session getSession() {
return SecurityUtils.getSubject().getSession();
}
/**
* 用户登出
*/
public static void logout() {
SecurityUtils.getSubject().logout();
}
/**
* 获取当前用户信息
*
* @Return SysUserEntity 用户信息
*/
public static User getUserInfo() {
User user = (User) SecurityUtils.getSubject().getPrincipal();
return user;
}
/**
* 获取登录用户的id
*
* @return
*/
public static Integer getLoginUserId() {
return getUserInfo().getId();
}
/**
* 获取登录用户的名称
*
* @return
*/
public static String getLoginUserName() {
return getUserInfo().getUserName();
}
/**
* 删除用户缓存信息
*
* @Param username 用户名称
* @Param isRemoveSession 是否删除Session,删除后用户需重新登录 如果为false代表只需要重新授权即可
*/
public static void deleteCache(String username, boolean isRemoveSession) {
//从缓存中获取Session
Session session = null;
// 获取当前已登录的用户session列表
Collection<Session> sessions = redisSessionDAO.getActiveSessions();
Object attribute = null;
// 遍历Session,找到该用户名称对应的Session
for (Session sessionInfo : sessions) {
attribute = sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (attribute == null) {
continue;
}
String name = ((SimplePrincipalCollection) attribute).getPrimaryPrincipal().toString();
if (name == null) {
continue;
}
if (Objects.equals(name, username)) {
session = sessionInfo;
// 清除该用户以前登录时保存的session,强制退出 -> 单用户登录处理
if (isRemoveSession) {
redisSessionDAO.delete(session);
}
}
}
if (session == null || attribute == null) {
return;
}
//删除session重新登录
if (isRemoveSession) {
redisSessionDAO.delete(session);
}
//删除Cache,再访问受限接口时会重新授权
DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
Authenticator authc = securityManager.getAuthenticator();
((LogoutAware) authc).onLogout((SimplePrincipalCollection) attribute);
}
/**
* 从缓存中获取指定用户名的Session
*
* @param username
*/
private static Session getSessionByUsername(String username) {
// 获取当前已登录的用户session列表
Collection<Session> sessions = redisSessionDAO.getActiveSessions();
User user;
Object attribute;
// 遍历Session,找到该用户名称对应的Session
for (Session session : sessions) {
attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (attribute == null) {
continue;
}
user = (User) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
if (user == null) {
continue;
}
if (Objects.equals(user.getPhone(), username)) {
return session;
}
}
return null;
}
}
package cn.wisenergy.web.shir.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/*@Component*/
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
System.out.println("========ApplicationContext配置成功,在普通类可以通过调用SpringUtils.getAppContext()获取applicationContext对象,applicationContext="+SpringUtil.applicationContext+"========");
System.out.println("---------------------------------------------------------------------");
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
package cn.wisenergy.web.shir.util;
import cn.wisenergy.service.util.UserToken;
import com.itextpdf.text.log.Logger;
import com.itextpdf.text.log.LoggerFactory;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import java.util.ArrayList;
import java.util.Collection;
public class UserModularRealmAuthenticator extends ModularRealmAuthenticator {
private static final Logger logger = LoggerFactory.getLogger(UserModularRealmAuthenticator.class);
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
throws AuthenticationException {
logger.info("UserModularRealmAuthenticator:method doAuthenticate() execute ");
// 判断getRealms()是否返回为空
assertRealmsConfigured();
// 强制转换回自定义的CustomizedToken
UserToken userToken = (UserToken) authenticationToken;
// 登录类型
String loginType = userToken.getLoginType();
// 所有Realm
Collection<Realm> realms = getRealms();
// 登录类型对应的所有Realm
Collection<Realm> typeRealms = new ArrayList<>();
for (Realm realm : realms) {
if (realm.getName().contains(loginType));
typeRealms.add(realm);
}
// 判断是单Realm还是多Realm
if (typeRealms.size() == 1){
logger.info("doSingleRealmAuthentication() execute ");
return doSingleRealmAuthentication(((ArrayList<Realm>) typeRealms).get(0), userToken);
}
else{
logger.info("doMultiRealmAuthentication() execute ");
return doMultiRealmAuthentication(typeRealms, userToken);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment