iOS开发之cookie

42 篇文章 0 订阅

  • 最近公司项目安全检查检测到缓存中的用户名和密码,虽然已经加密,但还是不安全;所以公司要求用cookie或者token去请求数据。由于公司项目比较老,里面既用了ASIHTTPRequest框架,又用了AFNetworking框架,发现其实这两个框架默认都是自动保持cookie的,我们不用去刻意处理它(获取与上传)。但是由于想搞清楚还是研究了一下cookie,并用NSURLSessionASIHTTPRequestAFNetworking自己手动管理cookie
  • cookie: 是网站服务端为了辩别用户身份,在服务器端生生成并存储在用户本地终端(电脑、手机)上的数据。其实cookie主要是用来免密登录的,我们这只是通过用户名和密码获取一个身份令牌,用于后面的接口调用,更像是token

NSHTTPCookieStorage 管理cookie

  • NSHTTPCookieStorage提供了管理所有NSHTTPCookie对象的接口,在OS X里,cookie是在所有程序中共享的,而在iOS中,cookie只在当当前应用中有效。。Session Cookie(SessionOnly返回YES的Cookie)只能在单一进程中使用。

NSHTTPCookieStorage和NSHTTPCookie关系

  • NSHTTPCookieStorage可以获取,删除,设置单例里面的cookies,设置cookie的管理策略等。
//只读的单例对象
@property(class, readonly, strong) NSHTTPCookieStorage *sharedHTTPCookieStorage;
//获取里面的cookie对象数组
@property (nullable , readonly, copy) NSArray<NSHTTPCookie *> *cookies;
//添加cookie
- (void)setCookie:(NSHTTPCookie *)cookie;
//删除cookie
- (void)deleteCookie:(NSHTTPCookie *)cookie;
//删除某个日期之前的cookie
- (void)removeCookiesSinceDate:(NSDate *)date API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
//设置cookie的管理策略
@property NSHTTPCookieAcceptPolicy cookieAcceptPolicy;
//获取对应url返回的cookie数组
- (nullable NSArray<NSHTTPCookie *> *)cookiesForURL:(NSURL *)URL;
  • NSHTTPCookie里面有个properties的属性,其组成如下:
<__NSArrayM 0x283bec540>(
<NSHTTPCookie
	version:0
	name:JSESSIONID
	value:A0787301B667F5E1C7B5BABAAE58B104
	expiresDate:'(null)'
	created:'2018-12-27 02:39:06 +0000'
	sessionOnly:TRUE
	domain:192.168.1.121
	partition:none
	sameSite:none
	path:/inner
	isSecure:FALSE
	isHTTPOnly: YES
 path:"/inner" isSecure:FALSE isHTTPOnly: YES>,
<NSHTTPCookie
	version:0
	name:token
	value:1545878346436_111_cd90cdd1-9938-4e18-8ccd-79ad54d5527c
	expiresDate:'2019-01-26 02:39:06 +0000'
	created:'2018-12-27 02:39:06 +0000'
	sessionOnly:FALSE
	domain:192.168.1.121
	partition:none
	sameSite:none
	path:/inner
	isSecure:FALSE
 path:"/inner" isSecure:FALSE>
)
name必选规定 cookie 的名称
value必选规定 cookie 的值
expire可选规定 cookie 的有效期
path可选规定 cookie 的服务器路径,只有该路劲下的文件接口才能使用
domain可选规定 cookie 的域名
secure可选规定是否通过安全的 HTTPS 连接来传输 cookie

其中sessionOnly:TRUE的cookie是后台服务器自动创建的。

  • cookiesession默认都是后台创建,后端会将cookie放在在responseheader中返回给前端,将session缓存在服务器中;前端获取到cookie可以自己手动保存,也可以交给NSHTTPCookieStorage单例来保存;前端将cookie放在requestheader中传给服务器,服务器会去查对应的session,然后去交换获得用户信息(用户登录ID等)请求接口,这就是整个cookie获取、设置和请求数据的流程。
  • 获取cookie的几种方法:
 //方式1 :  NSHTTPCookie获取cookie
               NSArray * array = [NSHTTPCookie cookiesWithResponseHeaderFields:httpresponse.allHeaderFields forURLresponse.URL];
                for (NSHTTPCookie * cookie in array) {
                    //保存到sharedHTTPCookieStorage中
                    [[NSHTTPCookieStorage sharedHTTPCookieStorage]setCookie:cookie];
                }
                
//方式2:NSHTTPURLResponse获取
NSHTTPURLResponse * httpresponse = (NSHTTPURLResponse *)response;
                NSDictionary * dic = httpresponse.allHeaderFields;
                //获取cookie字符串
                NSString * cookiesStr = [dic valueForKey:@"Set-Cookie"];
                
//方式3:NSHTTPCookieStorage获取cookie
NSArray * cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage]cookiesForURL:response.URL];
  • 多个cookie的格式通常为:cookie1=value1; cookie2=value2; cookie3=value3; 这里特别要注意,多个cookie之间用分号+空格分隔开,不是&也不是单纯的空格。我所了解到的iOS设置Cookie的方法有两种。
    设置cookie方式1:NSDictionary * dic = httpresponse.allHeaderFields;返回的字典有个"Set-Cookie"的字符串值就是多个cookie的默认样式,我们获取到后可以直接设置cookierequest的 header中。
{
    "Cache-Control" = "no-cache,must-revalidate";
    "Content-Length" = 7;
    Date = "Thu, 27 Dec 2018 07:11:51 GMT";
    Expires = "Thu, 01 Jan 1970 00:00:00 GMT";
    Pragma = "no-cache";
    Server = "Apache-Coyote/1.1";
    "Set-Cookie" = "JSESSIONID=D6F65DA599B3B6B22C1927C5D85B53F0; Path=/inner; HttpOnly, token=1545894711778_111_17906b71-9c77-48c5-8e5a-b9e52522a538; Expires=Sat, 26-Jan-2019 07:11:51 GMT; Path=/inner";
}
NSString * cookiesStr = [dic valueForKey:@"Set-Cookie"];
//设置cookies
 [request setValue:cookiesaStr forHTTPHeaderField:@"Cookie"];

设置cookie方式2:构建多个NSHTTPCookie实例对象的数组,根据NSHTTPCookie实例数组生成对应的HTTP cookie header,设置cookierequestheader中。

NSDictionary *properties1 = [NSDictionary dictionaryWithObjectsAndKeys:
                                @"domain.com", NSHTTPCookieDomain,
                                @"/", NSHTTPCookiePath,
                                @"userid", NSHTTPCookieName,
                                strUserId, NSHTTPCookieValue,
                                nil];
    
    NSDictionary *properties2 = [NSDictionary dictionaryWithObjectsAndKeys:
                                 @"domain.com", NSHTTPCookieDomain,
                                 @"/", NSHTTPCookiePath,
                                 @"pid", NSHTTPCookieName,
                                 pid, NSHTTPCookieValue,
                                 nil];
    
    
    NSHTTPCookie *cookie1 = [NSHTTPCookie cookieWithProperties:properties1];
    NSHTTPCookie *cookie2 = [NSHTTPCookie cookieWithProperties:properties2];
    NSArray* cookies = [NSArray arrayWithObjects: cookie1, cookie2, nil];
    //根据NSHTTPCookie实例数组生成对应的HTTP cookie header
    NSDictionary * headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
    //设置cookie到header中
    request.allHTTPHeaderFields = headers; 

NSURLSession管理cookie

  • NSURLSession中有两个+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;初始化方法,这个configuration可以给NSURLSession实例对象配置一下属性策略等,与cookie有关的属性有下:
//发送请求时是否设置cookie
@property BOOL HTTPShouldSetCookies;
//设置cookie的接收策略
@property NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy;
typedef NS_ENUM(NSUInteger, NSHTTPCookieAcceptPolicy) {
    NSHTTPCookieAcceptPolicyAlways,//接收所有的cookie,默认策略.
    NSHTTPCookieAcceptPolicyNever,//不接收所有的cookie
    NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain//只接收main document domain中的cookie.
};
//保存设置cookie的,如果不设置默认是[NSHTTPCookieStorage sharedHTTPCookieStorage],设置为nil管理cookie
@property (nullable, retain) NSHTTPCookieStorage *HTTPCookieStorage;

所以NSURLSession多了一种设置cookie的方式,只要设置了HTTPCookieStorage属性就可以自动设置了cookie。

1.使用系统管理cookie的类NSHTTPCookieStorage管理cookie

我们请求完成后必须手动将cookie保存到[NSHTTPCookieStorage sharedHTTPCookieStorage]中,不会自动将响应体header中的cookie保存,下面是具体实例。

//获取cookie
- (void)getCookies1{
    NSURL * url = [NSURL URLWithString:@"http://192.168.1.121:8080/inner/mobile/actionSh/Abc!login.action?"];
    NSString * post = [NSString stringWithFormat:@"loginId=%@",@"111"];
    NSData * postData = [post dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBody = postData;
    //defaultSessionConfiguration 使用默认session配置,类似NSURLConnection的标准配置,使用硬盘来存储缓存数据,会将缓存、cookie等存在本地
    //ephemeralSessionConfiguration 临时session配置,与默认配置相比,不使用永久持存cookie、证书、缓存的配置,最佳优化数据传输。
    //backgroundSessionConfiguration 后台session配置,与默认配置类似,不同的是会在后台开启另一个线程来处理网络数据
    NSURLSessionConfiguration * defultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    //不允许设置cookie
    defultConfiguration.HTTPShouldSetCookies = NO;
    //设置不允许缓存cookiechelue
    defultConfiguration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
    //清除所有的cookie
    for(NSHTTPCookie *cookie in [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies)
    {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie: cookie];
   }
     NSLog(@"清除所有的cookies : %@",[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies);
    defultConfiguration.HTTPCookieStorage = nil;
    NSURLSession * session = [NSURLSession sessionWithConfiguration:defultConfiguration delegate:nil delegateQueue:[NSOperationQueue currentQueue]];
    NSURLSessionTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"请求完成!!!");
        if (!error) {
            NSString * result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            if (result) {
                NSLog(@"解析成功:%@",result);
                NSLog(@"response:%@",response);
                NSHTTPURLResponse * httpresponse = (NSHTTPURLResponse *)response;
                NSDictionary * dic = httpresponse.allHeaderFields;
//                NSString * cookies = [dic valueForKey:@"Set-Cookie"];
//                NSLog(@"cookies : %@ --- %@",cookies,[NSThread currentThread]);
//                NSArray * NSArray = [[NSHTTPCookieStorage sharedHTTPCookieStorage]cookiesForURL:response.URL];
                //获取cookie
               NSArray * array = [NSHTTPCookie cookiesWithResponseHeaderFields:dic forURL:response.URL];
                for (NSHTTPCookie * cookie in array) {
                    //保存到sharedHTTPCookieStorage中
                    [[NSHTTPCookieStorage sharedHTTPCookieStorage]setCookie:cookie];
                }
               NSLog(@"NSHTTPCookieStorage :  %@",[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies);
                
            }else{
                NSLog(@"解析失败!!!");
            }
        }else{
            NSLog(@"请求失败 : %@",error.localizedDescription);
        }
    }];
    [task resume];
}

//设置cookie
- (void)requestWithCookies1{
    NSString * urlStr = @"http://192.168.1.121:8080/inner/mobile/actionSh/Abc!getData.action?";
    //encode去掉中午和特殊字符
    urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSURL * url = [NSURL URLWithString:urlStr];
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    NSURLSessionConfiguration * defultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    defultConfiguration.HTTPShouldSetCookies = YES;
    defultConfiguration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyAlways;
    NSLog(@"NSHTTPCookieStorage :  %@",[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies);
    defultConfiguration.HTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    NSURLSession * session = [NSURLSession sessionWithConfiguration:defultConfiguration delegate:nil delegateQueue:[NSOperationQueue currentQueue]];
    NSURLSessionTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"请求完成!!!");
        if (!error) {
            NSString * result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            if (result) {
                NSLog(@"解析成功:%@",result);
            }else{
                NSLog(@"解析失败!!!");
            }
        }else{
            NSLog(@"请求失败 : %@",error.localizedDescription);
        }
    }];
    [task resume];
}
2.自己手动管理cookie

上面的方法有时候并不能立即将cookie设置进去,所以我们自己手动管理cookie比较保险,就是在requestheader中设置cookie,在responseheader中获取到得cookie保存到偏好设置或单例中自己管理。

//获取cookie
- (void)getCookies2{
    NSURL * url = [NSURL URLWithString:@"http://192.168.1.121:8080/inner/mobile/actionSh/Abc!login.action?"];
    NSString * post = [NSString stringWithFormat:@"loginId=%@",@"111"];
    NSData * postData = [post dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBody = postData;
    NSURLSessionConfiguration * defultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    //不允许设置cookie
    defultConfiguration.HTTPShouldSetCookies = NO;
    //不接受cookie
    defultConfiguration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
    
    NSURLSession * session = [NSURLSession sessionWithConfiguration:defultConfiguration delegate:nil delegateQueue:[NSOperationQueue currentQueue]];
    NSURLSessionTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"请求完成!!!");
        if (!error) {
            NSString * result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            if (result) {
//                NSArray * cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage]cookiesForURL:response.URL];
                NSLog(@"解析成功:%@",result);
                NSLog(@"response:%@",response);
                NSHTTPURLResponse * httpresponse = (NSHTTPURLResponse *)response;
                NSDictionary * dic = httpresponse.allHeaderFields;
                //获取cookie字符串
                NSString * cookiesStr = [dic valueForKey:@"Set-Cookie"];
                NSData * cookiesData = [NSKeyedArchiver archivedDataWithRootObject:cookiesStr];
                //保存到偏好设置中
                [[NSUserDefaults standardUserDefaults] setValue:cookiesData forKey:@"BoncUserDefaultsCookie"];
                [[NSUserDefaults standardUserDefaults]synchronize];
            }else{
                NSLog(@"解析失败!!!");
            }
        }else{
            NSLog(@"请求失败 : %@",error.localizedDescription);
        }
    }];
    [task resume];
}


//设置cookie
- (void)requestWithCookies2{
    NSString * urlStr = @"http://192.168.1.121:8080/inner/mobile/actionSh/Abc!getData.action?";
    //encode去掉中午和特殊字符
    urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSURL * url = [NSURL URLWithString:urlStr];
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
    NSData * cookiesData = [[NSUserDefaults standardUserDefaults] valueForKey:@"BoncUserDefaultsCookie"];
    NSString * cookiesaStr = [NSKeyedUnarchiver unarchiveObjectWithData:cookiesData];
    //设置cookies
    [request setValue:cookiesaStr forHTTPHeaderField:@"Cookie"];
    //如果是NSURLRequest,可用下面方式设置
//    request.allHTTPHeaderFields = nil;
    NSURLSessionConfiguration * defultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    defultConfiguration.HTTPShouldSetCookies = NO;
    defultConfiguration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
    NSURLSession * session = [NSURLSession sessionWithConfiguration:defultConfiguration delegate:nil delegateQueue:[NSOperationQueue currentQueue]];
    NSURLSessionTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"请求完成!!!");
        if (!error) {
            NSString * result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            if (result) {
                NSLog(@"解析成功:%@",result);
            }else{
                NSLog(@"解析失败!!!");
            }
        }else{
            NSLog(@"请求失败 : %@",error.localizedDescription);
        }
    }];
    [task resume];
}

AFNetworking自己管理cookie

  • AFNetworking是自动保持cookie的,我们不用去刻意处理它(获取与上传),除非你有一些需要。这里要讲的是我们手动管理cookieAFNetworking中并没有专门为cookie封装的代码,不过底层使用的是NSURLRequest,所以我们可以获取到请求时服务器返回的cookie,然后保存起来(删除和保存由我们自己管理),请求时候设置到requestheader中即可。
  • 设置不保持cookie
//默认是YES,cookie会被存储在共享的 NSHTTPCookieStorage 容器中
_manager.requestSerializer.HTTPShouldHandleCookies = NO;
  • 获取cookie,并保存起来
      // 获取所有数据报头信息
        NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)task.response;
        NSDictionary *fields = [HTTPResponse allHeaderFields];
        // 获取cookie
        NSString *cookieString = [fields valueForKey:@"Set-Cookie"];
        //保存到偏好设置中
        [[NSUserDefaults standardUserDefaults] setObject:cookieString forKey:@"BoncUserDefaultsCookie"];
        [[NSUserDefaults standardUserDefaults]synchronize];
  • 设置cookie
   NSString * cookie = [[NSUserDefaults standardUserDefaults] valueForKey:@"BoncUserDefaultsCookie"];
    [self.manager.requestSerializer setValue:cookie forHTTPHeaderField:@"Cookie"];

ASIHTTPRequest自己管理cookie

  • ASIHTTPRequest是自动保持cookie的,如果我们所用cookie请求数据,默认情况下我们不需要做其他任何操作。不同于AFNetworking的是ASIHTTPRequestcookie进行了封装,不管是获取还是设置都及其方便,这里是我们自己管理cookie的做法。

  • 设置不保持cookie

//默认是YES,cookie会被存储在共享的 NSHTTPCookieStorage 容器中,并且会自动被其他request重用。
request.useCookiePersistence = NO;
//清空session期间创建的所有cookie
//[ASIFormDataRequest setSessionCookies:nil];
//清除session期间产生的所有的cookie和缓存的授权数据。
// [ASIFormDataRequest clearSession];
  • 获取和保存cookie:
 //获取cookie
 NSArray * cookies = [request responseCookies];
 //获取responseHeader
// NSDictionary *headers = [request responseHeaders];
 //保存cookie
 NSData * cookiesData = [NSKeyedArchiver archivedDataWithRootObject:cookies];
 [[NSUserDefaults standardUserDefaults]setValue:cookiesData forKey:@"BoncUserDefaultsCookies"];
 [[NSUserDefaults standardUserDefaults]synchronize];
  • 设置cookie
    //依然要设置不保持cookie,否则传递cookie就不是我们自己保存到cookie
    [request setUseCookiePersistence:NO];
    //取出cookie
    NSData * cookiesData = [[NSUserDefaults standardUserDefaults] valueForKey:@"BoncUserDefaultsCookies"];
    NSArray * cookies = [NSKeyedUnarchiver unarchiveObjectWithData:cookiesData];
     //设置cookie
    [request setRequestCookies:cookies.mutableCopy];
  • AFNetworkingASIHTTPRequest默认都是开启cookie的,并且都是用[NSHTTPCookieStorage sharedHTTPCookieStorage]管理cookie的,所有二者可以通用,我们这里手动管理cookie,可以自己控制cookie的生命周期,避免过期的情况。其实默认情况已经够我们正常使用了,这里需要注意点是默认都是开启cookie的情况下,如在我们登录获取cookie时候,一定要先清除cookie,否则可能出现退出登录(不退出应用)换了个用户登录,登录后的数据还是上个用户的信息(未清除jsessionID),虽然可以交给后台取处理(后台判断是不是登录接口,如果是就不取cookie),但我们客户端也应该处理一下,避免该错误的发生。

AFNetworking请求登录接口时候清除cookie:

AFHTTPSessionManager  * manager =  [AFHTTPSessionManager manager];
[manager.requestSerializer setValue:nil forHTTPHeaderField:@"Cookie"];
//有时候这样设置并不好使,虽然也是在不保持cookie,但[NSHTTPCookieStorage sharedHTTPCookieStorage]还是保持了cookie,这样jsessionID并没有清除,所以为保险起见要清空[NSHTTPCookieStorage sharedHTTPCookieStorage]里面的cookies
- (void)clearCookies{
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    NSArray *_tmpArray = [NSArray arrayWithArray:[cookieStorage cookies]];
    for (id obj in _tmpArray) {
        [cookieStorage deleteCookie:obj];
    }
}

ASIHTTPRequest请求登录接口时候清除cookie:

[ASIFormDataRequest setSessionCookies:nil];

UIWebView和WKWebView的cookie管理机制

  • UIWebView会将NSHttpRequest的所有请求产生的cookie自动保存到NSHTTPCookieStorage容器中,并且在同一个app内多个UIWebView之间共享,不需要我们做任何操作,在后续访问中会将 cookie 自动带到request 请求当中。

  • WKWebViewcookie问题在于 WKWebView发起的请求不会自动带上存储于 NSHTTPCookieStorage容器中的Cookie,实践发现WKWebView实例其实也会将cookie存储于 NSHTTPCookieStorage 中,但存储时机有延迟,在iOS 8上,当页面跳转的时候,当前页面的 cookie 会写入NSHTTPCookieStorage中,而在 iOS 10 上,JS 执行 document.cookie 或服务器 set-cookie注入的 cookie会很快同步到 NSHTTPCookieStorage中,在执行 [WKWebView loadReques:] 前将 NSHTTPCookieStorage中的内容复制到 WKHTTPCookieStore中,以此来达到 WKWebView cookie注入的目的。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值