shiro框架实现登录次数限制和登录人数限制进该链接https://www.jianshu.com/p/ddd96a821d23
1、使用redis或redis集群来管理session,redis搭建见之前的文章
2、编写用户认证类实现AuthorizingRealm接口
package com.yang.shiro;
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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import com.yang.bean.Permission;
import com.yang.bean.Role;
import com.yang.bean.User;
import com.yang.service.IUserService;
//实现AuthorizingRealm接口用户用户认证
public class MyShiroRealm extends AuthorizingRealm{
//用于用户查询
@Autowired
private IUserService userService;
//角色权限和对应权限添加
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//若不适用shiro框架认证,那么该里面的逻辑不写
//获取登录用户名
String name= (String) principalCollection.getPrimaryPrincipal();
//查询用户名称
User user = userService.getUserByName(name);
//添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (Role role:user.getRoles()) {
//添加角色
simpleAuthorizationInfo.addRole(role.getRoleName());
for (Permission permission:role.getPermissions()) {
//添加权限
simpleAuthorizationInfo.addStringPermission(permission.getPermission());
}
}
return simpleAuthorizationInfo;
}
//用户认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
//若不适用shiro框架认证,那么该里面的逻辑不写
//加这一步的目的是在Post请求的时候会先进认证,然后在到请求
if (authenticationToken.getPrincipal() == null) {
return null;
}
//获取用户信息
String name = authenticationToken.getPrincipal().toString();
User user = userService.getUserByName(name);
if (user == null) {
//这里返回后会报出对应异常
return null;
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName());
return simpleAuthenticationInfo;
}
}
}
3、编写管理session的RedisSessionDAO类
package com.yang.shiro;
import com.alibaba.fastjson.JSON;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisCluster;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@Service
public class RedisSessionDAO extends AbstractSessionDAO {
private Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
//毫秒
private int redisSessionExpire=3600000;
//秒
private int redisExpire = 3600;
@Autowired
private JedisCluster jedisCluster;
private String SESSION_REDIS_KEY="SHIRO_SESSION:";
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
//将session与sessionId关联起来
this.assignSessionId(session,sessionId);
//根据sessionId将session存到redis;
try{
jedisCluster.setex(SESSION_REDIS_KEY+sessionId,redisExpire,SerializableUtils.serialize(session));
}catch (Exception e){
logger.error("----------创建Session error:",e);
}
return session.getId();
}
@Override
protected Session doReadSession(Serializable serializable) {
Session session = null;
try {
session = (Session) SerializableUtils.deserialize(jedisCluster.get(SESSION_REDIS_KEY + serializable));
if(session != null){
logger.info("------------读取Session之后,sessionId:"+session.getId());
}
} catch (Exception e) {
logger.error("----------读取session失败-----", e);
}
return session;
}
@Override
public void update(Session session) throws UnknownSessionException {
if(session instanceof ValidatingSession && !((ValidatingSession) session).isValid()){
//session无效则不需要更新了
return;
}
try{
jedisCluster.setex(SESSION_REDIS_KEY+session.getId(),redisExpire,SerializableUtils.serialize(session));
}catch (Exception e){
logger.error("----------创建Session error:",e);
}
}
@Override
public void delete(Session session) {
if(session == null || session.getId() == null){
return;
}
try{
jedisCluster.del(SerializableUtils.serialize(session.getId()));
logger.info("------删除Session------");
}catch (Exception e){
logger.error("-------删除Session失败---",e);
}
}
/**
* 该方法可以用来实现在线人数的获取
* s实现此功能,需要创建Session的时候在redis加个前缀,这样可以通过redisCluster.keys(前缀*)来获取所有的用户
* @return
*/
@Override
public Collection<Session> getActiveSessions() {
Set<Session> set = new HashSet();
Set<String> keys = jedisCluster.hkeys(SESSION_REDIS_KEY + "*");
logger.info("----------keys:" + JSON.toJSONString(keys));
for (String key : keys) {
Session session = (Session) SerializableUtils.deserialize(jedisCluster.get(key));
set.add(session);
}
logger.info("----------set:" + JSON.toJSONString(set));
return set;
}
public int getRedisSessionExpire() {
return redisSessionExpire;
}
public void setRedisSessionExpire(int redisSessionExpire) {
this.redisSessionExpire = redisSessionExpire;
}
}
4、编写启动时shiro的自动配置类,包括cookie的名字定义,路径的访问权限设置
package com.yang.shiro;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.mgt.SecurityManager;
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.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfiguration {
// @Value("${shiro.loginUrl}")
private String masterLoginUrl;
//将自己的验证方式加入容器
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
//启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
myShiroRealm.setAuthenticationCachingEnabled(false);
return myShiroRealm;
}
//权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
securityManager.setSessionManager(shiroSessionManager());
return securityManager;
}
//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//LinkedHashMap保证拦截的有序性,从上到下
Map<String,String> map = new LinkedHashMap<String, String>();
//登入,anon标识/user/login可以匿名访问
map.put("/user/login", "anon");
// //静态资源
// map.put("/assets/**", "anon");
// //404界面
// map.put("/404.html","anon");
// //登出 logout表示访问/user/logout路径时,页面会自动跳转到setLoginUrl设置的登陆页面login.html
// map.put("/user/logout","logout");
//对所有用户认证 authc表示该/**路径下都要进行认证,页面跳转到登录页面必须先进行登录页面setLoginUrl
// map.put("/**","authc");
map.put("/**","anon");
//登录 ,,必须设置该行,否则无法访问login.html前端页面,前后端分离
// shiroFilterFactoryBean.setLoginUrl("/login.jsp");
//首页
// shiroFilterFactoryBean.setSuccessUrl("/index.html");
//未授权的页面,需要自定义异常才能跳转到该页面
// shiroFilterFactoryBean.setUnauthorizedUrl("/user/unanth");
//错误页面,认证不通过跳转
//登录接口
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
// session管理
@Bean
public DefaultWebSessionManager shiroSessionManager(){
ShiroSessionManager shiroSessionManager =new ShiroSessionManager();
shiroSessionManager.setSessionDAO(redisSessionDAO());
shiroSessionManager.setSessionIdCookie(simpleCookie());
//是否删除过期的session
shiroSessionManager.setDeleteInvalidSessions(true);
//设置全局 session的时效
shiroSessionManager.setGlobalSessionTimeout(redisSessionDAO().getRedisSessionExpire());
//是否定时检查session有效状态
shiroSessionManager.setSessionValidationSchedulerEnabled(true);
return shiroSessionManager;
}
@Bean
public RedisSessionDAO redisSessionDAO(){
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
return redisSessionDAO;
}
/**
* 修改Cookie中的SessionId的key,默认为JSESSIONID,自定义名称
*/
@Bean
public SimpleCookie simpleCookie(){
//通过 Cookie[] cookies = HttpServletRequest.getCookies();来获得该COOKIE
SimpleCookie simpleCookie = new SimpleCookie("SHIROSESSION");
simpleCookie.setPath("/");
return simpleCookie;
}
//加入注解的使用,不加入以下三个这个注解不生效
// @Bean
// public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
// AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
// authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
// return authorizationAttributeSourceAdvisor;
// }
// * 该类如果不设置为static,@Value注解就无效,原因未知
// @Bean
// public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
// return new LifecycleBeanPostProcessor();
// }
//
// @Bean
// public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
// return new DefaultAdvisorAutoProxyCreator();
// }
}
5、编写ShiroSessionManager类,解决shiro多次从redis读取session的问题
package com.yang.shiro;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import javax.servlet.ServletRequest;
import java.io.Serializable;
public class ShiroSessionManager extends DefaultWebSessionManager {
/**
* 搜索session
* @param sessionKey
* @returnu-
* @throws UnknownSessionException
*/
/**
* 重写sessonManager
* 解决shiro多次从redis读取session的问题
*/
@Override
protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
//获取sessionId
Serializable sessionId = getSessionId(sessionKey);
//根据sessionID获取session
ServletRequest request = null;
if(sessionKey instanceof WebSessionKey){
request = ((WebSessionKey) sessionKey).getServletRequest();
}
if(sessionId != null && request != null){
Object object = request.getAttribute(sessionId.toString());
if(object != null){
return (Session) object;
}
}
//搜索session,从redisSessionDAO中读取session,,创建,更新session
Session session = super.retrieveSession(sessionKey);
if(sessionId != null && request != null){
request.setAttribute(sessionId.toString(),session);
}
return session;
}
}
6、序列化成session的工具类
package com.yang.shiro;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.Session;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
*
* <p>Version: 1.0
*/
public class SerializableUtils {
public static String serialize(Object ob) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(ob);
return Base64.encodeToString(bos.toByteArray());
} catch (Exception e) {
throw new RuntimeException("serialize session error", e);
}
}
public static Object deserialize(String serializableStr) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(serializableStr));
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
} catch (Exception e) {
throw new RuntimeException("deserialize session error", e);
}
}
}
7、测试类
package com.yang.controller;
import com.alibaba.fastjson.JSON;
import com.yang.bean.ResultObject;
import com.yang.shiro.SerializableUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.JedisCluster;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
@RestController
public class SessionController {
private final Logger logger = LoggerFactory.getLogger(SessionController.class);
@Autowired
private JedisCluster jedisCluster;
@RequestMapping("/getActiveSessions")
public ResultObject getActiveSessions(HttpSession session, HttpServletRequest request){
ResultObject out =new ResultObject();
try{
//其中
Cookie[] cookies = request.getCookies();
logger.info("------cookies:"+JSON.toJSONString(cookies));
String sessionId = session.getId();
logger.info("---------sesssionId:"+sessionId);
Set<Session> set = new HashSet();
Set<String> keys = jedisCluster.hkeys("SHIRO_SESSION:" + "*");
logger.info("----------keys:" + JSON.toJSONString(keys));
for (String key : keys) {
Session se = (Session) SerializableUtils.deserialize(jedisCluster.get(key));
set.add(se);
}
logger.info("----------set:" + JSON.toJSONString(set));
logger.info("--------set.size:"+set.size());
// return set;
}catch (Exception e){
}
return out;
}
// C146FD352CA2731BEF023486FE46C54E
}