我的shiro之旅: 七 shiro session 共享

shiro——session会话管理 目录 1. 会话 1.1 Session 接口 1.2 SessionManager 会话管理 1.3 SessionListener 会话监听 2. 会话持久化 2.1 SessionDAO接口 2.2AbstractSessionDAO类 2.3 CachingSessionDAO 类 2.4 EnterpriseCacheSessionDAO 类 2.5 Memo... 阅读详情

博客已移至 http://blog.gogl.top

很久没有写过博文,一来是因为工作比较紧,二来是因为很长一段时间没有写博文的心情。今天,想继续写写shiro的一些文章,这篇文章只要想分享的是shiro 共享session的内容。

        在这里先给出我的shiro配置文件。

      

        <bean id="shiroRealm" class="com.concom.security.infrastructure.shiro.ShiroRealm"/>

	<bean id="shiroCacheManager" class="com.concom.security.infrastructure.shiro.cache.ShiroCacheManager"/>
	
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="shiroRealm" />
		<!-- <property name="sessionMode" value="native"/> -->
		<property name="cacheManager" ref="shiroCacheManager"/>
		<property name="sessionManager" ref="shiroSessionManager"/>
		<!-- <property name="subjectDAO" ref="subjectDAO"/> -->
	</bean>

	
	<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
		<!-- <property name="sessionIdGenerator" ref="sessionIdGenerator"/> -->
	</bean>
	<!-- <bean id="sessionIdGenerator" class="com.concom.security.infrastructure.shiro.ShiroSessionIdGenerator"/> -->

	<bean id="shiroSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
		<property name="sessionDAO" ref="sessionDAO"/>
		<!-- <property name="sessionValidationScheduler" ref="shiroSessionValidationScheduler"/> -->
		<property name="sessionValidationInterval" value="1800000"/>  <!-- 相隔多久检查一次session的有效性 -->
		<property name="globalSessionTimeout" value="1800000"/>  <!-- session 有效时间为半小时 (毫秒单位)-->
		<property name="sessionIdCookie.domain" value="${domain}"/>
		<property name="sessionIdCookie.name" value="${sessionId_name}"/>
		<property name="sessionIdCookie.path" value="${path}"/>
		<!-- <property name="sessionListeners">
			<list>
				<bean class="com.concom.security.interfaces.listener.SessionListener"/>
			</list>
		</property> -->
	</bean>
	
	
	<!-- <bean id="shiroSessionValidationScheduler" class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">
		<constructor-arg name="sessionManager" ref="sessionManager"/>
		<property name="interval" value="10000"/>  定时半小时检验一次有所session是否有效(毫秒单位)
	</bean> -->

	
	<!-- Post processor that automatically invokes init() and destroy() methods -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

 

 

 

从shiro的DefaultWebSecurityManager源码中我们可以看到,该类继承了CachingSecurityManager类,CachingSecurityManager里有个CacheManager属性,这就是shiro的cache,我们主要实现这个cache就可以。贴出源码。

 

public abstract class CachingSecurityManager implements SecurityManager, Destroyable, CacheManagerAware {

    /**
     * The CacheManager to use to perform caching operations to enhance performance.  Can be null.
     */
    private CacheManager cacheManager;

    /**
     * Default no-arg constructor that will automatically attempt to initialize a default cacheManager
     */
    public CachingSecurityManager() {
    }

    /**
     * Returns the CacheManager used by this SecurityManager.
     *
     * @return the cacheManager used by this SecurityManager
     */
    public CacheManager getCacheManager() {
        return cacheManager;
    }

    /**
     * Sets the CacheManager used by this {@code SecurityManager} and potentially any of its
     * children components.
     * <p/>
     * After the cacheManager attribute has been set, the template method
     * {@link #afterCacheManagerSet afterCacheManagerSet()} is executed to allow subclasses to adjust when a
     * cacheManager is available.
     *
     * @param cacheManager the CacheManager used by this {@code SecurityManager} and potentially any of its
     *                     children components.
     */
    public void setCacheManager(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
        afterCacheManagerSet();
    }

    /**
     * Template callback to notify subclasses that a
     * {@link org.apache.shiro.cache.CacheManager CacheManager} has been set and is available for use via the
     * {@link #getCacheManager getCacheManager()} method.
     */
    protected void afterCacheManagerSet() {
    }

    /**
     * Destroys the {@link #getCacheManager() cacheManager} via {@link LifecycleUtils#destroy LifecycleUtils.destroy}.
     */
    public void destroy() {
        LifecycleUtils.destroy(getCacheManager());
        this.cacheManager = null;
    }

}

cacheManager是一个接口,shiro给出了一个实现,就是MemoryConstrainedCacheManager,下面也贴出MemoryConstrainedCacheManager的源码。

 

 

 

public class MemoryConstrainedCacheManager extends AbstractCacheManager {

    /**
     * Returns a new {@link MapCache MapCache} instance backed by a {@link SoftHashMap}.
     *
     * @param name the name of the cache
     * @return a new {@link MapCache MapCache} instance backed by a {@link SoftHashMap}.
     */
    @Override
    protected Cache createCache(String name) {
        return new MapCache<Object, Object>(name, new SoftHashMap<Object, Object>());
    }
}


从MemoryConstrainedCacheManager我们可以看到,MemoryConstrainedCacheManager继承了AbstractCacheManager,我们自己建一个类,也继承AbstractCacheManager,然后实现createCache(String name)方法,替换shiro的默认实现即可。在这里,也给出我的实现。

 

 

package com.concom.security.infrastructure.shiro.cache;

import org.apache.shiro.cache.AbstractCacheManager;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.concom.security.infrastructure.shiro.cache.memcache.ShiroMemcachedCache;

/**
 * @author Dylan
 * @time 2014年1月6日
 */
@SuppressWarnings("rawtypes")
public class ShiroCacheManager extends AbstractCacheManager {

	private static final Logger log = LoggerFactory.getLogger(ShiroCacheManager.class);

	@Override
	protected Cache createCache(String cacheName) throws CacheException {

		if (log.isTraceEnabled()) {
			log.trace("create a cache name : " + cacheName);
		}

		return new ShiroMemcachedCache<Object, Object>(cacheName);
	}

}

 

 

createCache方法返回的是一个Cache,Cache也是一个接口,我写了一个ShiroMemcachedCache,实现了Cache。ShiroMemcachedCache主要是对缓存的一些增删改查的操作。其中ShiroMemcachedCache的实现如下:

 

 

 

package com.concom.security.infrastructure.shiro.cache.memcache;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.concom.security.infrastructure.memcache.MemcacheRepository;
/**
 * @author Dylan
 * @time 2014年1月6日
 * @param <K>
 * @param <V>
 */
@SuppressWarnings("unchecked")
public class ShiroMemcachedCache<K,V> implements Cache<K, V>  {
	
	private static MemcacheRepository cache;
	
	private String CACHE_PREFIX;
	
	private final static Logger LOG = LoggerFactory.getLogger(ShiroMemcachedCache.class);
	
	
	public ShiroMemcachedCache(String cacheName){
		CACHE_PREFIX = cacheName+"-";
	}
	
	@Override
	public V get(K key) throws CacheException {
		V value = ((V)getCache().get(keyToString(key)));
		if(LOG.isDebugEnabled()){
			LOG.debug("get the entity json is " + key + " : " + value);
		}
		return value;
	}

	@Override
	public V put(K key, V value) throws CacheException {
		if(LOG.isDebugEnabled()){
			LOG.debug("begin save the "+ key + " : " + value+" to memcache");
		}
		getCache().save(keyToString(key), value);
		return value;
	}

	@Override
	public V remove(K key) throws CacheException {
		if(LOG.isDebugEnabled()){
			LOG.debug("begin remove the "+key+" from memcache");
		}
		V value = get(key);
		getCache().remove(keyToString(key));
		return value;
	}

	@Override
	public void clear() throws CacheException {
		for(Iterator<K> keys =keys().iterator();keys.hasNext();){
			K key = keys.next();
			remove(key);
		}
	}

	@Override
	public int size() {
		return keys().size();
	}

	@Override
	public Set<K> keys() {
		Set<String> keys = new HashSet<String>();
		for(String key : getCache().keys()){
			if(StringUtils.startsWith(key, CACHE_PREFIX)){
				keys.add(key);
			}
		}
		return (Set<K>)keys ;
	}

	@Override
	public Collection<V> values() {
		List<V> values = new ArrayList<V>();
		for(Iterator<K> keys =keys().iterator();keys.hasNext();){
			K key = keys.next();
			V value = getValue(key);
			values.add(value);
		}
		return values;
	}
	
	private V getValue(K key) throws CacheException {
		V value = ((V)getCache().get(String.valueOf(key)));
		if(LOG.isDebugEnabled()){
			LOG.debug("get the entity json is " + key + " : " + value);
		}
		return value;
	}
	
	private String keyToString(K key){
		String k = String.valueOf(key);
		if(StringUtils.startsWith(k, CACHE_PREFIX)){
			return k;
		}
		return CACHE_PREFIX+k;
	}

	public static MemcacheRepository getCache() {
		return cache;
	}

	public static void setCache(MemcacheRepository cache) {
		ShiroMemcachedCache.cache = cache;
	}

}

 

 

 

上面缓存用的是memcached。这样,shiro的缓存共享就完成了。篇幅有点长,这篇先写到这里,下一篇将继续讲shiro的cache共享。

 

 

 

从零构建高精度温控系统:ADN8835的单电感拓扑与0.01℃稳定性实战解析 本文深入解析了如何利用ADN8835构建高精度温控系统,重点介绍了其创新的单电感拓扑架构如何实现0.01℃的长期稳定性。内容涵盖精密PCB布局、电源滤波设计,并详细阐述了针对不同热容负载的数字PID控制参数整定策略,为开发精密仪器和医疗设备提供了实战指南。 阅读详情

相关推荐

数据中心GPU芯片性能参数速查表(Nvidia+国产算力卡)

本文提供了一份数据中心GPU芯片性能参数速查表,涵盖Nvidia与国产算力卡两大阵营。文章详细解读了算力、显存、互连技术及功耗等关键性能指标,并对比了A100、H100、昇腾910B等主流型号,旨在帮助用户根据AI训练、推理等实际场景,做出高效的硬件选型与成本评估决策。

a0b1c2d3的专栏 837

Shiro学习(10)Session管理

但是如在web环境中,如果用户不主动退出是不知道会话是否过期的,因此需要定期的检测会话是否过期,Shiro提供了会话验证调度器SessionValidationScheduler来做这件事情。Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如web容器tomcat),不管JavaSE还是JavaEE环境都可以使用,提供了会话管理、会话事件监听、会话存储/持久化、容器无关的集群、失效/过期支持、对Web的透明支持、SSO单点登录的支持等特性。更新会话最后访问时间及销毁会话;

m0_67403073的博客 3127

保姆级教程:用Seurat 5.0.1搞定单细胞测序数据从质控到细胞注释的全流程

本文提供了一份详细的Seurat 5.0.1单细胞测序数据分析教程,涵盖从数据质控到细胞注释的全流程。通过R代码示例和可视化分析,帮助用户掌握单细胞RNA测序数据处理的核心技术,包括数据标准化、降维聚类和细胞类型注释,适用于生物信息学研究和临床数据分析。

weixin_29067143的博客 197

Shiro中的session共享问题(如果再也见不到你,祝你早安,午安,晚安。)

当我们通过nginx反向代理到我们的集群后,如果用户再进行登录,用户的session会存储在第一次负载均衡到的服务器上,但是如果我们调用其中的一些功能或者说权限后,用户在另外一台服务器上并没有session信息,就会导致提示未登录问题,需要用户再次进行登录。但是我们不可能让用户登录很多次,这对用户的体验来说是非常不友好的,因此解决shiro安全框架中的session问题是非常有必要的。

生命不止,学习无休 930

分布式存储系统关于GDPR条例中的数据清除原则

文章目录前言HDFS存储系统数据的完全删除基于加解密的输入输出流的数据保护方案 前言 关于GDPR是什么,可能许多同学之前并不太了解,至少笔者在之前也是不清楚的。GDPR全称为通用数据保护条例,General Data Protection Regulation。它是一项来自欧盟的关于计算机数据保护相关的条约,旨在规范系统如何收集,管控用户私人数据的种种行为。但是当前的一些存储系统并没有完全符合...

走在前往架构师的路上 3627

从零构建:毫米波雷达生命信号检测系统的Matlab仿真全流程解析

本文详细解析了毫米波雷达生命信号检测系统的Matlab仿真全流程,涵盖原理、信号处理链路设计及算法优化策略。通过FMCW雷达技术,实现非接触式呼吸心跳信号检测,结合Matlab代码示例展示距离FFT、相位提取与信号分离等关键步骤,为医疗监护和智能家居应用提供实用指南。

weixin_29303701的博客 355

springboot+shiro+redis实现session共享和cache共享

在分布式应用中,若是使用了负载均衡,用户第一次访问,连接的A服务器,进行了登录操作进入了系统,当用户再次操作时,请求被转发到了B服务器,用户并没有在B进行登录,此时用户又来到了登录页面,这是难以理解和接受的,这就引出了session共享。对于shiro安全框架如何实现session共享?shiro共享分为两方面,一个是session共享,一个是cache共享。下面聊聊在springboot工程中整合shiro框架,并通过redis实现session共享和cache共享。

ldcaws的专栏 3490

网易数智CoreAgent实战:5步教你打造企业级AI数字员工(附避坑指南)

本文以网易数智CoreAgent智能体协作中枢为核心,提供了一份企业级AI数字员工的实战部署指南。文章详细拆解了从环境准备、智能体配置、业务集成、安全合规到效果评估的五个关键步骤,并分享了部署过程中的常见“避坑”经验,旨在帮助企业技术团队高效、安全地将AI智能体落地于实际业务场景,解决AI落地难题。

rice5的博客 953

shiro的缓存及session.html

shiro的缓存及session.html

weixin_67696142的博客 381

视觉惯性SLAM系列——ORB-SLAM3的整体介绍及各个线程的功能(五)

跟踪线程(Tracking Thread)作用:负责处理每一帧图像,进行相机位姿的估计和定位。它首先执行与前一帧图像的特征匹配,然后通过仅运动(motion-only)BA(Bundle Adjustment)优化位姿。如果跟踪失败,它会尝试在所有地图中进行重定位。此外,跟踪线程还负责决定何时插入新的关键帧,以及何时将关键帧从局部地图中移除。详细介绍见局部建图线程(Local Mapping Thread)作用:管理局部地图,向活跃地图添加关键帧和点,移除多余的关键帧和点。

曾经也曾立志 1004

基于深度学习的茶叶等级检测识别系统:YOLOv10 + UI界面 + 数据集

YOLOv10是YOLO系列的最新版本,继承了YOLO模型的优点,并对速度和精度进行了优化。YOLOv10在目标检测中使用卷积神经网络(CNN)作为基础,通过改进网络结构和优化训练策略,实现了在更低的延迟下进行高精度的目标检测。本文详细介绍了基于YOLOv10的茶叶等级检测与识别系统的设计与实现过程。我们从数据集准备、模型训练与推理、UI界面设计等多个方面进行了详细讲解,并给出了相应的代码实现。通过本系统,我们可以高效地自动检测茶叶的等级,并通过UI界面直观地展示结果。

YOLO 765

我的shiro之旅: 八 shiro session 共享的进一步

博客已移至 http://blog.gogl.top 在第七篇文章说了一下如何实现shiro的共享。那个共享并不只是session的共享,也包括了Authorization的共享。如果读者还没看过文章七,建议先看看。 在文章七的ShiroCacheManager类继承了AbstractCacheManager,我们来看看AbstractCacheManager类的源码。   public...

Dylan的博文 6659

Python爬虫技术应用:体育赛事直播数据抓取与分析

Web爬虫(Web Crawling)是一种通过模拟浏览器行为从网站获取数据的技术。发送请求:发送HTTP请求(通常是GET或POST请求)获取目标网页的HTML内容。解析网页:使用解析工具(如lxml)从HTML中提取出所需要的信息。存储数据:将抓取到的数据存储到数据库、文件或数据结构中,供后续分析和使用。数据分析与展示:使用数据分析工具(如PandasMatplotlib)对数据进行处理、清洗和可视化,生成有价值的报告和图表。Requests:用于发送静态网页的HTTP请求并获取响应。

2201_76125261的博客 1309

我的shiro之旅: 十七 跨域session共享的一种解决方法

博客已移至 http://blog.gogl.top 在文章七里面有介绍session共享,不过只是在一个域名及其他子域间共享。有时候,我们需要在多个一级域名共享登录的session。当然,用shiro-cas是一个很不错的解决方法,应该也是大部分人使用的方法。不过因为种种原因,并没有选择shiro-cas的方式,就使用了其他方式代替,思路也来自shiro-cas。比如有www.a.com,ww...

Dylan的博文 1万+

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

我的shiro之旅-session共享-redis

基于redis缓存的session共享 结合上面的 MSM 思想,由 redis负责 session 数据的存储,而我们自己实现的 session manager 将负责 session 生命周期的管理。    此架构存在着当redis master故障时, 虽然可以有一到多个备用slave,但是redis不会主动的进行master切换,这时session服务中断。

zhuchunyan_aijia的博客 6179

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

java session跨域共享_我的shiro之旅: 十七 跨域session共享的一种解决方法

importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServletRequest;importorg.apache.commons.lang.StringUtils;importorg.apache.shiro.web.servlet.Adv...

weixin_42109125的博客 315

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

我的shiro之旅: 九 shiro 清理缓存的权限信息

博客已移至 http://blog.gogl.top 在文章八讲到了shiro缓存权限信息然后达到共享目的,不过存在一个问题,当用户的权限发生改变的时候,需要用户重新登录,从新缓存用户权限信息。这篇文章将介绍在改变用户的权限时,如何清理用户的权限。我这里写了一个帮助类,先贴也代码。   package com.concom.security.infrastructure.helper; ...

Dylan的博文 2万+

我的shiro之旅: 十 自定义shiro的SessionIdCookie

博客已移至 http://blog.gogl.top 在使用shiro的时候,曾经有段时间很苦恼,因为我cookie的sessionId经常无故被改,然后抛There is no session with id [xxxx]的异常。我们知道,当请求过来,shiro创建session后,会把sessionId写回到客户端的cookie中。每二个请求过来时,shiro会拿到这个cookie里的ses...

Dylan的博文 2万+

shiro 获取cookie

我的shiro之旅: 十 自定义shiro的SessionIdCookie 分类: shiro | 作者: lhacker 相关 | 发布日期 : 2014-11-12 | 热度 : 597° 在使用shiro的时候,曾经有段时间很苦恼,因为我cookie的sessionId经常无故被改,然后抛There is no session with id [xxxx]的异常。我们知道,当...

Hi, Sun 3440

Apache Shiro 10分钟之旅!

欢迎来到Apache Shiro 10分钟之旅! 希望通过这个简单、快速的示例,可以让你对应用程序中使用Shiro有个深入的了解。嗯,10分钟你应该可以搞定它。 概述 Apache Shiro是什么? Apache Shiro一个功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案。 实际上,Shiro的主要功能是管理应用程...

aoqiaotan2763的博客 83

Shiro-Redis终极指南:快速实现分布式缓存与安全管理完美融合

Shiro-Redis是一个强大的开源项目,它将Apache Shiro安全框架与Redis内存数据库完美结合,为分布式系统提供高效的安全管理和缓存解决方案。如果你正在寻找如何快速实现Shiro分布式缓存的方法,那么你来对地方了!🚀 ## 什么是Shiro-Redis? Shiro-Redis解决了Apache Shiro原生只支持ehcache和concurrentHashMap的局限。通

gitblog_00694的博客 1045

深入解析Apache Shiro Web集成:从Filter链到安全实战

在Java Web应用开发中,权限控制与安全管理是构建企业级应用的核心基础。其实现原理通常基于Servlet Filter机制,通过在请求处理链中插入安全过滤器,实现对资源的访问控制。这项技术的核心价值在于提供声明式、非侵入式的安全解决方案,能够有效分离业务逻辑与安全检查代码,提升系统的可维护性和安全性。典型的应用场景包括后台管理系统、金融交易平台、企业内部应用等需要细粒度权限控制的Web项目。本文聚焦于Apache Shiro框架的Web集成机制,深入剖析其Filter链设计、Session管理以及安全配

weixin_30655569的博客 330

推荐开源项目:Shiro-Redis - 强大的Shiro Redis缓存实现

--- ### 1、项目介绍 Shiro-Redis 是一个专门为 Apache Shiro 设计的 Redis 缓存实现扩展,弥补了Shiro原生仅支持Ehcache和ConcurrentHashMap的局限性。如果你正在寻找一种高效、灵活且可靠的分布式缓存解决方案来增强你的Shiro应用,那么这个项目无疑是理想的选择。 --- ### 2、项目技术分析 Shiro-Redis 无缝集

gitblog_00055的博客 469

探索`shiro-redis-session`: 结合Shiro与Redis的高性能会话管理解决方案

探索shiro-redis-session: 结合Shiro与Redis的高性能会话管理解决方案 在Web开发中,会话管理是一个至关重要的部分,它涉及到用户的登录状态、权限控制等多个核心功能。Apache Shiro是一款强大的Java安全框架,而Redis则是一种高效的数据缓存系统。当两者结合,我们可以创建出一个高度可扩展且可靠的会话管理系统。本文将深入介绍项目,这是一个将Shiro与Redis...

gitblog_00013的博客 842

10分钟教会你Apache Shiro

10分钟教会你Apache Shiro 前言 欢迎来到Apache Shiro 10分钟之旅! 希望通过这个简单、快速的示例,可以让你对应用程序中使用Shiro有个深入的了解。嗯,10分钟你应该可以搞定它。 概述 Apache Shiro是什么? Apache Shiro一个功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话

sunhuiliang85的专栏 377

Apache Shiro权限管理框架的初次认识

前言 欢迎来到Apache Shiro 10分钟之旅! 希望通过这个简单、快速的示例,可以让你对应用程序中使用Shiro有个深入的了解。嗯,10分钟你应该可以搞定它。 概述 Apache Shiro是什么? Apache Shiro一个功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案。 实际上,Shiro

HH出状猿的专栏 717
上一篇: 我的shiro之旅: 六 自定义shiro的sessionId
下一篇: 我的shiro之旅: 八 shiro session 共享的进一步
LHacker
博客等级 码龄17年 162粉丝 24原创
评论 15
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值