当前位置: 首页 > 编程日记 > 正文

iOS封装HTTPS双向和单向验证

1.HttpsUtil

(1) 对双向和单向验证的封装

#import <Foundation/Foundation.h>

#import "AFNetworking.h"


@interface HttpsUtil : NSObject


// 双向认证

+ (void)configHTTPSessionManager:(AFHTTPSessionManager *)manager serverCers:(NSArray *) serverCerNames clientP12:(NSString *) clientp12Name clientP12Password:(NSString *) clientP12Password isSelfCa:(BOOL) isSelfCa;


// 单向认证

+ (void)cconfigHTTPSessionManager:(AFHTTPSessionManager *)manager serverCers:(NSArray *) serverCerNames isSelfCa:(BOOL) isSelfCa;


@end

(2)实现方法

#import "HttpsUtil.h"


@implementation HttpsUtil


// 双向认证

+ (void)configHTTPSessionManager:(AFHTTPSessionManager *) manager serverCers:(NSArray *) serverCerNames clientP12:(NSString *) clientp12Name clientP12Password:(NSString *) clientP12Password isSelfCa:(BOOL) isSelfCa{

    

    __weakAFHTTPSessionManager*_manager = manager;

    

    if(!_manager){

        return;

    }

    

    NSMutableSet * serverCerDatas= [[NSMutableSetalloc] init];

    

    for (NSString * serverCerNamein serverCerNames){

        if(!serverCerName){

            continue;

        }

        NSString *cerPath = [[NSBundlemainBundle] pathForResource:serverCerNameofType:nil];// 证书的路径

        NSData *certData = [NSDatadataWithContentsOfFile:cerPath];

        [serverCerDatas addObject:certData];

    };

    

    _manager.securityPolicy.pinnedCertificates=serverCerDatas;

    

    // 需要验证客户端证书(双向认证)

    if(clientp12Name && clientP12Password){

        

        [manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session,NSURLAuthenticationChallenge *challenge,NSURLCredential *__autoreleasing*_credential) {

            

            NSURLSessionAuthChallengeDisposition disposition =NSURLSessionAuthChallengePerformDefaultHandling;

            

            __autoreleasingNSURLCredential *credential =nil;

            

            if([challenge.protectionSpace.authenticationMethodisEqualToString:NSURLAuthenticationMethodServerTrust]) {

                

                if([_manager.securityPolicyevaluateServerTrust:challenge.protectionSpace.serverTrustforDomain:challenge.protectionSpace.host]){

                    

                    credential = [NSURLCredentialcredentialForTrust:challenge.protectionSpace.serverTrust];

                    

                    if(credential) {

                        disposition =NSURLSessionAuthChallengeUseCredential;

                    } else {

                        disposition =NSURLSessionAuthChallengePerformDefaultHandling;

                    }

                } else {

                    disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;

                }

            } else {

                // client authentication

                SecIdentityRef identity =NULL;

                SecTrustRef trust =NULL;

                NSString *p12Path = [[NSBundlemainBundle] pathForResource:clientp12NameofType:nil];

                NSFileManager *fileManager =[NSFileManagerdefaultManager];

                

                if(![fileManagerfileExistsAtPath:p12Path]){

                    NSLog(@"客户端证书不存在!");

                } else {

                    NSData *PKCS12Data = [NSDatadataWithContentsOfFile:p12Path];

                    

                    if ([[selfclass]extractIdentity:&identityandTrust:&trust fromPKCS12Data:PKCS12Datapkcs12Password:clientP12Password]) {

                        NSLog(@"加载客户端证书成功");

                        SecCertificateRef certificate =NULL;

                        SecIdentityCopyCertificate(identity, &certificate);

                        constvoid*certs[] = {certificate};

                        CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);

                        credential =[NSURLCredentialcredentialWithIdentity:identitycertificates:(__bridge NSArray*)certArraypersistence:NSURLCredentialPersistencePermanent];

                        disposition =NSURLSessionAuthChallengeUseCredential;

                    }

                }

            }

            *_credential = credential;

            return disposition;

        }];

    }

    

    if(isSelfCa){

        manager.securityPolicy.allowInvalidCertificates =YES;

        manager.securityPolicy.validatesDomainName=NO;

            }else{

        // 注释掉Info.plist中整个NSAppTransportSecurity节点的配置

    }

}


// 单向认证

+ (void)cconfigHTTPSessionManager:(AFHTTPSessionManager *)manager serverCers:(NSArray *) serverCerNames isSelfCa:(BOOL) isSelfCa{

    

    [[selfclass]configHTTPSessionManager:managerserverCers:serverCerNamesclientP12:nilclientP12Password:nilisSelfCa:isSelfCa];

}


// 解压初始化客户端证书

+ (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data pkcs12Password:(NSString *) p12Password{

    

    OSStatus securityError =errSecSuccess;

    

    NSDictionary* optionsDictionary = [NSDictionarydictionaryWithObject:p12PasswordforKey:(__bridgeid)kSecImportExportPassphrase];

    

    CFArrayRef items =CFArrayCreate(NULL,0, 0,NULL);

    securityError = SecPKCS12Import((__bridgeCFDataRef)inPKCS12Data,(__bridgeCFDictionaryRef)optionsDictionary,&items);

    

    if(securityError ==0) {

        // 成功

        CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);

        constvoid*tempIdentity =NULL;

        tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);

        *outIdentity = (SecIdentityRef)tempIdentity;

        constvoid*tempTrust =NULL;

        tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);

        *outTrust = (SecTrustRef)tempTrust;

    } else {

        NSLog(@"初始化客户端证书失败,errorCode:%d",(int)securityError);

        returnNO;

    }

    returnYES;

}


@end

/
2.HttpsHandler 对网络请求的封装(get请求,post请求,post图片上传)

#import <Foundation/Foundation.h>

#import "AFNetworking.h"


@interface HttpsHandler : NSObject


+ (void)GET:(NSString *)URLString parameters:(id)patameters success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure;

+ (void)POST:(NSString *)URLString parameters:(id)patameters success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure;

+ (void)POST:(NSString *)URLString parameters:(id)patameters constructingBodyWithBlock:(void (^) (id <AFMultipartFormData> formData))block success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure;


@end


//

#import "HttpsHandler.h"

#import "HttpsUtil.h"


@implementation HttpsHandler


+ (void)GET:(NSString *)URLString parameters:(id)patameters success:(void (^)(id))success failure:(void (^)(NSError *))failure {

    

    AFHTTPSessionManager *manager =[AFHTTPSessionManagermanager];

    

    NSArray *serverCersNames = [[NSArrayalloc] initWithObjects:@"mykey.cer",nil];

    [HttpsUtilconfigHTTPSessionManager:managerserverCers:serverCersNamesclientP12:@"mykey.p12"clientP12Password:@"password"isSelfCa:true];// 使用自签名CA给服务器server证书签名的isSelfCa为true,第三方权威CA签名的isSelfCa为false,当设置isSelfCa为false时,需要注释掉Info.plist中整个NSAppTransportSecurity节点的配置

    manager.responseSerializer = [AFHTTPResponseSerializerserializer];

    manager.requestSerializer.timeoutInterval =60.0f;

     manager.responseSerializer = [AFJSONResponseSerializerserializer];//申明返回的结果是json类型

    [manager.requestSerializersetValue:@"Content-Type"forHTTPHeaderField:@"application/json; charset=utf-8"];

    manager.responseSerializer.acceptableContentTypes = [NSSetsetWithObjects:@"application/json",@"text/json", @"text/javascript",@"text/html",nil];

    [manager GET:URLStringparameters:patameters progress:nilsuccess:^(NSURLSessionDataTask *_Nonnull task, id _Nullable responseObject) {

        if (success)

        {

            success(responseObject);

        }

    } failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) {

        if (failure)

        {

            failure(error);

            return ;

        }

    }];

    

}


+ (void)POST:(NSString *)URLString parameters:(id)patameters success:(void (^)(id))success failure:(void (^)(NSError *))failure {

    

    AFHTTPSessionManager *manager =[AFHTTPSessionManagermanager];

    /*

    NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"214051022010694" ofType:@"key"];

    NSData *cerData = [NSData dataWithContentsOfFile:cerPath];

    NSLog(@"%@",cerData);

    manager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey withPinnedCertificates:[[NSArray alloc] initWithObjects:cerData, nil]];

    manager.securityPolicy.allowInvalidCertificates = YES;

    [manager.securityPolicy setValidatesDomainName:NO];

    manager.requestSerializer = [AFJSONRequestSerializer serializer];

    manager.responseSerializer = [AFJSONResponseSerializer serializer];

    */

  

    NSArray *serverCersNames = [[NSArrayalloc] initWithObjects:@"mykey.cer",nil];

    [HttpsUtilconfigHTTPSessionManager:managerserverCers:serverCersNamesclientP12:@"mykey.p12"clientP12Password:@"password"isSelfCa:true];// 使用自签名CA给服务器server证书签名的isSelfCa为true,第三方权威CA签名的isSelfCa为false,当设置isSelfCa为false时,需要注释掉Info.plist中整个NSAppTransportSecurity节点的配置

    manager.requestSerializer.timeoutInterval =60.0f;

    manager.requestSerializer = [AFHTTPRequestSerializerserializer];//表明请求的是json

    manager.responseSerializer = [AFJSONResponseSerializerserializer];//申明返回的结果是json类型

    [manager.requestSerializersetValue:@"Content-Type"forHTTPHeaderField:@"charset=utf-8"];

    manager.responseSerializer.acceptableContentTypes = [NSSetsetWithObjects:@"application/json",@"text/json", @"text/javascript",@"text/html",@"text/xml",nil];

    

    [manager.requestSerializersetValue:@"Content-Type"forHTTPHeaderField:@"charset=utf-8"];

    manager.responseSerializer.acceptableContentTypes = [NSSetsetWithObjects:@"application/json",@"text/json", @"text/javascript",@"text/html",@"text/xml",nil];

    [manager POST:URLStringparameters:patameters progress:nilsuccess:^(NSURLSessionDataTask *_Nonnull task, id _Nullable responseObject) {

        if (success)

        {

            success(responseObject);

        }

    } failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) {

        if (failure)

        {

            failure(error);

        }

    }];

}



+ (void)POST:(NSString *)URLString parameters:(id)patameters constructingBodyWithBlock:(void (^)(id<AFMultipartFormData>))block success:(void (^)(id))success failure:(void (^)(NSError *))failure {

    

    AFHTTPSessionManager *manager =[AFHTTPSessionManagermanager];

    

    NSArray *serverCersNames = [[NSArrayalloc] initWithObjects:@"mykey.cer",nil];

    [HttpsUtilconfigHTTPSessionManager:managerserverCers:serverCersNamesclientP12:@"mykey.p12"clientP12Password:@"password"isSelfCa:true];// 使用自签名CA给服务器server证书签名的isSelfCa为true,第三方权威CA签名的isSelfCa为false,当设置isSelfCa为false时,需要注释掉Info.plist中整个NSAppTransportSecurity节点的配置

    manager.responseSerializer = [AFHTTPResponseSerializerserializer];

    manager.requestSerializer.timeoutInterval =60.0f;

     manager.requestSerializer = [AFHTTPRequestSerializerserializer];//表明请求的是json

     manager.responseSerializer = [AFJSONResponseSerializerserializer];//申明返回的结果是json类型

     [manager.requestSerializersetValue:@"application/json"forHTTPHeaderField:@"Content-Type"];

    [manager.requestSerializersetValue:@"application/json"forHTTPHeaderField:@"Accept"];

    manager.responseSerializer.acceptableContentTypes = [NSSetsetWithObjects:@"application/json",@"text/json", @"text/javascript",@"text/html,@“text/plain",nil];

    [manager POST:URLStringparameters:patameters constructingBodyWithBlock:^(id<AFMultipartFormData_Nonnull formData) {

       if (block)

       {

           block(formData);

       }

    } progress:nilsuccess:^(NSURLSessionDataTask *_Nonnull task, id _Nullable responseObject) {

       if (success)

       {

           success(responseObject);

       }

    } failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) {

       if (failure)

       {

           failure(error);

       }

    }];

    

}


//+ (NSString*)dataToJsonString:(id)object

//{

//    NSString *jsonString = nil;

//    NSError *error;

//    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object

//                                                       options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string

//                                                         error:&error];

//    if (! jsonData) {

//        NSLog(@"Got an error: %@", error);

//    } else {

//        jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

//    }

//    return jsonString;

//}








@end



相关文章:

开源库BearSSL介绍及使用

BearSSL是用C语言实现的SSL/TLS协议&#xff0c;它的源码可直接通过git clone https://www.bearssl.org/git/BearSSL 下载&#xff0c;它的license是MIT&#xff0c;最新版本为0.6。 BearSSL的主要特性是&#xff1a; (1). 正确且安全&#xff1a;对不安全的协议版本和算法选…

个推CTO安森:我所理解的数据中台

作者 | 个推CTO安森来源 | 个推技术学院&#xff08;ID:ID: getuitech&#xff09;引言在前面两篇文章&#xff08;《数据智能时代来临&#xff1a;本质及技术体系要求》和《多维度分析系统的选型方法》&#xff09;之中&#xff0c;我们概括性地阐述了对于数据智能的理解&…

玩弹珠手游-杂想

前言 为什么会写这个杂想呢&#xff1f; 因为最近这一个月来&#xff0c;我有点太沉迷怪物弹珠这个游戏了&#xff0c;每天下班回来的时间和上下班路途都在玩这个游戏&#xff0c;占据了我大部分的业余时间&#xff0c;也该是时候放一放玩游戏了。 为什么会玩这个游戏呢&#x…

OC封装时间选择器

#import <UIKit/UIKit.h> protocol TimeDatePickerViewDelegate <NSObject> //必须实现的两个协议 required - (void)changeTime : (NSDate *)date;//当时改变时出发 - (void)daterMine : (NSDate *)date;//更确定时间 end interface TimeDatePickerView :UIView /…

银行卡大小的充电宝,买就送耳机!

每个人的朋友圈和微博上似乎都有那么几个活得让人羡慕的朋友他们的生活看起来不仅精致&#xff0c;还很丰富多彩从早上第一刻就开始了↓出门旅游新一天的穿搭逆天朋友团咖啡馆到书店逼格十足的日料夜景太迷人忍不住发个小视频最后一定不要错过傍晚的夕阳&#xff0c;真的好上镜…

C++中插件使用举例

插件并不是在构建时链接的&#xff0c;而是在运行时发现并加载的。因此&#xff0c;用户可以利用你定义好的插件API来编写自己的插件。这样他们就能以指定方式扩展API的功能。插件库是一个动态库&#xff0c;它可以独立于核心API编译&#xff0c;在运行时根据需要显示加载。不过…

C和C++安全编码笔记:指针诡计

指针诡计(pointer subterfuge)是通过修改指针值来利用程序漏洞的方法的统称。 可以通过覆盖函数指针将程序的控制权转移到攻击者提供的外壳代码(shellcode)。当程序通过函数指针执行一个函数调用时&#xff0c;攻击者提供的代码将会取代原本希望执行的代码而得到执行。 对象指…

runLoop和runtime的分析

一.RunLoop: Runloop是事件接收和分发机制的一个实现。 Runloop提供了一种异步执行代码的机制&#xff0c;不能并行执行任务。 在主队列中&#xff0c;Main RunLoop直接配合任务的执行&#xff0c;负责处理UI事件、定时器以及其他内核相关事件。 (1).RunLoop的主要目的&#…

脑出血遇到深度学习,是否可以无所遁形?

近期大家对身体健康这个话题格外关注&#xff0c;而我们今天公开课的主题也恰巧与此不谋而合。我国脑卒的发病率已经超过心血管疾病&#xff0c;成为致死、致残率最高的疾病&#xff0c;并且发病率呈逐年上升的趋势&#xff0c;此外脑血管病和颅内肿瘤等脑部疾病也危害人们的健…

Cloudera Manager 5.3 和 CDH5.3.0 本地(离线)

为什么80%的码农都做不了架构师&#xff1f;>>> 声明一下&#xff1a;http://my.oschina.net/dataRunner/blog/369129 是本人所写&#xff0c;并非抄袭。 有部分内容来自 http://www.wangyongkui.com/hadoop-cdh5/ 这个文件是根据官网操作&#xff0c;翻译的不…

万字长文详解如何用Python玩转OpenGL | CSDN 博文精选

作者 | 天元浪子来源 | CSDN博文精选【编者按】OpenGL&#xff08;开放式图形库&#xff09;&#xff0c;用于渲染 2D、3D 矢量图形的跨语言、跨平台的应用程序编程接口&#xff0c;C、C、Python、Java等语言都能支持 OpenGL。本文作者以 Python 语法为例&#xff0c;用两万字详…

模仿视频抓帧实现

路口或某些场所可能并不会把从摄像头获取到的视频全部存储下来或对所有的视频帧进行处理&#xff0c;即摄像设备是一直处于打开状态&#xff0c;可能会根据需要间隔性的抓取其中一帧&#xff0c;或当某事件触发时才会抓取当前的一帧数据进行处理。这里使用两个线程来模仿此场景…

iOS--MD5加密封装

#import <Foundation/Foundation.h> interface MD5 : NSObject /** * md5加密 * * param inPutText 需要加密的字符串 * * return 加密好的字符串 */ (NSString *)md5:(NSString *)inPutText; end #import "MD5.h" #import "CommonCrypto/CommonDiges…

Akka路由_RoundRobinRoutingLogic

2019独角兽企业重金招聘Python工程师标准>>> Akka路由_RoundRobinRoutingLogic 使用Round Robin算法的Router&#xff0c;代码中有注释&#xff0c;基本和上篇文章中的代码一样 http://my.oschina.net/xinxingegeya/blog/369721&#xff0c; 具体如下&#xff0c;关…

iOS ---网络请求封装(自动缓存与手动缓存)

#import <Foundation/Foundation.h> interface WNetworkCache : NSObject /** * 手动写入/更新缓存 * * param jsonResponse 要写入的数据 * param URL 请求URL * * return 是否写入成功 */ (BOOL)saveJsonResponseToCacheFile:(id)jsonResponse andURL:(NSStrin…

Windows下获取视频设备的一种改进实现

之前在https://blog.csdn.net/fengbingchun/article/details/102806822中介绍过在Windows下获取视频设备列表的方法。其实那种实现方法是有缺陷的&#xff0c;当PC机上连接多个视频设备&#xff0c;并且其中有设备处于启动运行状态时&#xff0c;再调用相关接口获取视频设备可能…

最新单步目标检测框架,引入双向网络,精度和速度均达到不错效果

作者 | Tiancai Wang等译者 | 路一直都在出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;one-stage的目标检测方法因其具有实时性强、检测精度高等特点&#xff0c;近年来受到广泛关注。目标检测包括分类和定位两个子任务&#xff0c;通常来说&#xff0c;one-stage目…

基于Sentinel的Redis3.2高可用方案

默认情况下&#xff0c;Redis node和sentinel的protected-mode都是yes&#xff0c;在搭建集群时&#xff0c;若想从远程连接redis集群&#xff0c;需要将redis.conf和sentinel.conf的protected-mode修改为no&#xff0c;若只修改redis node&#xff0c;从远程连接sentinel后&am…

从YARN迁移到k8s,滴滴机器学习平台二次开发是这样做的

整理 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;【导读】人工智能时代&#xff0c;机器学习已经渗透进每个领域&#xff0c;改变了这些领域的业务模式、技术架构以及方法论。随着深度学习技术近年来快速发展&#xff0c;高效、易用的机器学习平台对于互联…

最新 macOS Sierra 10.12.3 安装CocoaPods及使用详解

cocoapods官网&#xff1a;https://guides.cocoapods.org 一、什么是CocoaPods 每种语言发展到一个阶段&#xff0c;就会出现相应的依赖管理工具&#xff0c;例如 Java 语言的 Maven&#xff0c;nodejs 的 npm。随着 iOS 开发者的增多&#xff0c;业界也出现了为 iOS 程序提供…

libusb中的热插拔使用举例

以下为判断usb设备是插入还是拔出状态(热插拔)的测试代码&#xff1a; 在Windows下是不支持的&#xff0c;在Linux是支持的&#xff0c;下一个版本可能会支持Windows下的热插拔&#xff1a; #include <chrono> #include <thread> #include <iostream> #incl…

C++复制控制:拷贝构造函数

一、拷贝构造函数是一种特殊构造函数&#xff0c;具有单个形参&#xff0c;该形参&#xff08;常用const修饰&#xff09;是对该类类型的引用。与默认构造函数一样 &#xff0c;拷贝构造函数可由编译器隐式调用。拷贝构造函数应用的场合为&#xff1a; &#xff08;1&#xff0…

关于IOS获取本地通讯录信息(包含iOS9.0前后)

在ios开发当中&#xff0c;获取用户本地的通讯录功能愈加频繁的出现&#xff0c;七两自己也在自己公司的项目当中遇到的获取本地的通讯录信息的功能&#xff08;俗称“种子用户功能”&#xff0c;太可怕了&#xff09;。对此七两总结了自己使用本地通讯录时的注意点&#xff0c…

C和C++安全编码笔记:动态内存管理

4.1 C内存管理&#xff1a; C标准内存管理函数&#xff1a; (1).malloc(size_t size)&#xff1a;分配size个字节&#xff0c;并返回一个指向分配的内存的指针。分配的内存未被初始化为一个已知值。 (2).aligned_alloc(size_t alignment, size_t size)&#xff1a;为一个对象…

作为一名程序员,数学到底对你有多重要?

最近在知乎上看到一个贴子&#xff0c;看完后我沉默了.....沉思后想想&#xff0c;其实每个行业都会分等级&#xff0c;程序员也不例外&#xff01;说好听一点的叫工程师&#xff0c;普通一点的叫程序员&#xff0c;差一点的叫码农&#xff0c;更差的还会叫码畜&#xff0c;码奴…

经典SQL(sqlServer)

一、基础 1、说明&#xff1a;创建数据库CREATE DATABASE database-name 2、说明&#xff1a;删除数据库drop database dbname3、说明&#xff1a;备份sql server--- 创建 备份数据的 deviceUSE masterEXEC sp_addumpdevice disk, testBack, c:\mssql7backup\MyNwind_1.dat--- …

iOS UITextField输入框随键盘弹出界面上移

//点击输入框界面跟随键盘上移 - (void)textFieldDidBeginEditing:(UITextField *)textField { CGRect frame textField.frame; int offSet frame.origin.y 70 - (self.view.frame.size.height - 216.0); //iphone键盘高度为216.iped键盘高度为352 [UIView beginAnimations:…

IEEE分享 | 机器学习在领英的规模化应用

人工智能和机器学习仍然是全球持续增长的领域之一&#xff0c;近年来涌现出越来越多本科生或者非人工智能专业出身的工程师&#xff0c;他们努力学习和使用技术来改进产品&#xff0c;几乎每天都有新的机器学习技术和框架发布。这篇文章将讨论领英如何规模化利用技术&#xff0…

GitHub/GitLab/Gitee中项目互拷贝后仍保留历史提交记录的方法

GitHub、GitLab、Gitee等在同一个网站中执行复制或拷贝一个已有项目到一个新项目比较简单&#xff0c;因为它们在每一个项目上都有一个Fork按钮&#xff0c;直接点击此Fork按钮即可&#xff0c;Fork后的新项目会保留原有项目的历史提交记录。但是如果不在同一个网站上进行此操作…

基于mimeTex的数学公式Webservice的部署和实现

通过Latex语法&#xff0c;实现生成数学公式的解决方案也很多。这里介绍一种方法&#xff0c;使用开源的mimeTex。该项目的官网地址如下&#xff1a;http://www.forkosh.com/mimetex.html网站主页有一个声明。如果你的服务器上已经安装了latex&#xff0c;那么推荐使用mathTex&…