shiro框架session共享(多项目之间的访问)

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
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nginx是一款高性能的HTTP和反向代理服务器,可以实现负载均衡和高可用性。Spring Boot是一个快速构建应用程序的框架Shiro是一个用于安全验证和访问控制的框架,而Session共享是指将用户会话信息存储在多个服务器上,以实现用户请求的无缝切换。 在Nginx和Spring Boot集群中使用Shiro时,可以使用Session共享来实现用户的无状态会话管理。具体实施如下: 1. 集群配置:将多个Spring Boot实例部署在不同的服务器上,并使用Nginx进行反向代理和负载均衡。通过配置Nginx的upstream块,将请求分发给多个Spring Boot实例。 2. Session共享:使用分布式缓存来实现Session共享。可以选择使用Redis、Memcached等分布式缓存系统,将Session数据存储在缓存中。在Spring Boot中,可以使用Spring Session提供的支持来进行Session共享的配置。 3. Shiro配置:在Spring Boot中,可以使用Shiro框架来处理身份验证和访问控制。在Shiro中,可以实现自定义的SubjectDAO来将Session存储到分布式缓存中。同时,通过配置ShiroSessionManager和SessionDAO,将Session的读取和存储操作委托给分布式缓存系统。 4. 应用程序开发:在Spring Boot应用程序中使用Shiro时,可以通过注解或配置文件来配置身份验证和访问控制规则。在验证用户身份后,可以将用户信息存储在Session中,并通过Session共享机制,在多个服务器上共享Session数据。 通过以上步骤,可以实现在Nginx、Spring Boot集群中使用Shiro框架进行Session共享。这样,用户可以在不同的服务器上无缝切换,并享受到一致的用户体验。同时,通过Nginx的负载均衡机制,可以提高系统的性能和可伸缩性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值