Shiro安全框架之会话管理

简介

  在 shiro 里所有用户的会话信息都会由 Shiro 来进行控制,它提供的会话可以用于JavaSE/JavaEE 环境,不依赖于任何底层容器,可以独立使用,是完整的会话模块。通过 Shiro 的会话管理器(SessionManager)进行统一的会话管理。

一、会话管理简介

  SessionManager(会话管理器):管理所有 Subject 的 session 包括创建、维护、删除、失效、验证等工作。SessionManager是顶层组件(接口),具体的是由 SecurityManager 管理。
Shiro 提供了三个默认实现:

  • DefaultSessionManager: 用于JavaSE环境
  • ServletContainerSessionManager: 用于Web环境,直接使用servlet容器的会话。
  • DefaultWebSessionManager: 用于web环境,自己维护会话(自己维护着会话,直接废弃了Servlet容器的会话管理)。

在web程序中(就是前几篇文章的例子),通过 shiro 的 Subject.login() 方法登录成功后,用户的认证信息实际上是保存在 HttpSession 中的。

二、Shiro结合redis的统一会话管理图示

微服务会话管理示意图
会话管理步骤

三、Shiro结合redis的统一会话管理环境准备

  • 主要依赖
<dependency>
	<groupId>org.crazycake</groupId>
	<artifactId>shiro-redis</artifactId>
	<version>3.0.0</version>
</dependency>
  • 在配置文件中添加 redis 配置信息
redis:
 host: 127.0.0.1
 port: 6379
  • 自定义 Shiro 会话管理器
/** 自定义的sessionManager */
public class CustomSessionManager extends DefaultWebSessionManager {

  /**
   * 头信息中具有sessionid 请求头:Authorization: sessionid
   *
   * <p>指定sessionId的获取方式
   */
  @Override
  protected Serializable getSessionId(ServletRequest request, ServletResponse response) {

    // 获取请求头Authorization中的数据
    String id = WebUtils.toHttp(request).getHeader("Authorization");
    if (StringUtils.isEmpty(id)) {
      // 如果没有携带,生成新的sessionId
      return super.getSessionId(request, response);
    } else {
      // 返回sessionId(固定写法)
      request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header"); //标记sessionId的来源
      request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); //当前sessionId具体的值
      request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); //sessionId需不需要验证
      return id;
    }
  }
}
  • 修改之前的Shiro配置类
@Configuration
public class ShiroConfiguration {
  // 1.创建realm
  @Bean
  public CustomRealm getRealm() {
    return new CustomRealm();
  }

  // 2.创建安全管理器
  @Bean
  public SecurityManager getSecurityManager(CustomRealm realm) {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(realm);

/*************添加Shiro会话管理配置*********************************/
    // 将自定义的会话管理器注册到安全管理器中
    securityManager.setSessionManager(sessionManager());
    // 将自定义的redis缓存管理器注册到安全管理器中
    securityManager.setCacheManager(cacheManager());
/*****************************************************************/
    return securityManager;
  }

  // 3.配置shiro的过滤器工厂

  /** 再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制 */
  @Bean
  public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
    // -创建过滤器工厂
    ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
    // -设置安全管理器
    filterFactory.setSecurityManager(securityManager);
    // -通用配置(跳转登录页面,为授权跳转的页面)
    filterFactory.setLoginUrl(url地址); // 设置没登陆时跳转url地址
    filterFactory.setUnauthorizedUrl(url地址); // 设置未授权时跳转的url地址
    // -设置过滤器集合
    /** 设置所有的过滤器:选择有顺序map key = 拦截的url地址 value = 过滤器类型 */
    Map<String, String> filterMap = new LinkedHashMap<>();
    filterMap.put("/user/home","anon"); //表示"/user/home"请求地址可以被匿名访问  
    filterMap.put("/user/**", "authc"); //表示"/user/"下的所有请求地址必须认证之后可以才被访问  
    // -将过滤器集合配置到filter中
    filterFactory.setFilterChainDefinitionMap(filterMap);
    return filterFactory;
  }

  // 4.开启对shior注解的支持
  @Bean
  public AuthorizationAttributeSourceAdvisor  authorizationAttributeSourceAdvisor(
      SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    advisor.setSecurityManager(securityManager);
    return advisor;
  }
}

/*************添加Shiro会话管理配置*********************************/
  @Value("${spring.redis.host}")
  private String host;

  @Value("${spring.redis.port}")
  private int port;

  /** 1.redis的控制器,操作redis */
  public RedisManager redisManager() {
    RedisManager redisManager = new RedisManager();
    redisManager.setHost(host);
    redisManager.setPort(port);
    return redisManager;
  }

  /** 2.sessionDao */
  public RedisSessionDAO redisSessionDAO() {
    RedisSessionDAO sessionDAO = new RedisSessionDAO();
    sessionDAO.setRedisManager(redisManager());
    return sessionDAO;
  }

  /** 3.会话管理器 */
  public DefaultWebSessionManager sessionManager() {
    CustomSessionManager sessionManager = new CustomSessionManager();
    sessionManager.setSessionDAO(redisSessionDAO());
    return sessionManager;
  }

  /** 4.缓存管理器 */
  public RedisCacheManager cacheManager() {
    RedisCacheManager redisCacheManager = new RedisCacheManager();
    redisCacheManager.setRedisManager(redisManager());
    return redisCacheManager;
  }
  • 修改实体类,实现 AuthCachePrincipal 接口
/** 用户实体类 */
@Entity
@Table(name = "pe_user")
@Getter
@Setter
/** AuthCachePrincipal: redis和shiro插件包提供的接口 */
public class User implements Serializable, AuthCachePrincipal {
  private static final long serialVersionUID = 4297464181093070302L;
  /** ID */
  @Id private String id;

  private String username;
  private String password;

  @ManyToMany(fetch = FetchType.EAGER)
  @JoinTable(
      name = "pe_user_role",
      joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
      inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
  private Set<Role> roles = new HashSet<Role>(); // 用户与角色   多对多

  @Override
  public String getAuthCacheKey() {
    return null;
  }
}
发布了35 篇原创文章 · 获赞 52 · 访问量 1280

猜你喜欢

转载自blog.csdn.net/weixin_40242806/article/details/104485769