保护您的数据

可以使用类提供的方法保护数据。

Crypto

类中的方法提供标准 用于创建摘要、消息身份验证代码和签名的算法 作为加密和解密信息。这些可用于保护 Salesforce,或与 Google 或 Amazon 等外部服务集成 网络服务 (AWS)。Crypto

示例:集成 Amazon WebServices

以下示例演示了 Amazon WebServices 与 销售人员:

public class HMacAuthCallout {

   public void testAlexaWSForAmazon() {
 
   // The date format is yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
      DateTime d = System.now();
      String timestamp = ''+ d.year() + '-' +
      d.month() + '-' +
      d.day() + '\'T\'' +
      d.hour() + ':' +
      d.minute() + ':' +
      d.second() + '.' +
      d.millisecond() + '\'Z\'';
      String timeFormat = d.formatGmt(timestamp);

      String urlEncodedTimestamp = EncodingUtil.urlEncode(timestamp, 'UTF-8');
      String action = 'UrlInfo';
      String inputStr = action + timeFormat;
      String algorithmName = 'HMacSHA1';
      Blob mac = Crypto.generateMac(algorithmName,  Blob.valueOf(inputStr), 
                                                    Blob.valueOf('your_signing_key'));
      String macUrl = EncodingUtil.urlEncode(EncodingUtil.base64Encode(mac), 'UTF-8');
 
      String urlToTest = 'amazon.com';
      String version = '2005-07-11'; 
      String endpoint = 'http://awis.amazonaws.com/';
      String accessKey = 'your_key';
 
      HttpRequest req = new HttpRequest();
      req.setEndpoint(endpoint +
                      '?AWSAccessKeyId=' + accessKey +
                      '&Action=' + action +
                      '&ResponseGroup=Rank&Version=' + version +
                      '&Timestamp=' + urlEncodedTimestamp +
                      '&Url=' + urlToTest +
                      '&Signature=' + macUrl);
 
      req.setMethod('GET');
      Http http = new Http();
      try {
         HttpResponse res = http.send(req);
         System.debug('STATUS:'+res.getStatus());
         System.debug('STATUS_CODE:'+res.getStatusCode());
         System.debug('BODY: '+res.getBody());
      } catch(System.CalloutException e) {
         System.debug('ERROR: '+ e);
      }
   }
}

示例:加密和 解密

下面的示例使用 和 方法以及类的方法。encryptWithManagedIVdecryptWithManagedIVgenerateAesKeyCrypto

// Use generateAesKey to generate the private key
Blob cryptoKey = Crypto.generateAesKey(256);

// Generate the data to be encrypted.
Blob data = Blob.valueOf('Test data to encrypted');

// Encrypt the data and have Salesforce generate the initialization vector 
Blob encryptedData = Crypto.encryptWithManagedIV('AES256', cryptoKey, data);

// Decrypt the data
Blob decryptedData = Crypto.decryptWithManagedIV('AES256', cryptoKey, encryptedData);

下面是为 和 Crypto 方法编写单元测试的示例。

encryptWithManagedIVdecryptWithManagedIV

@isTest
private class CryptoTest {
    static testMethod void testValidDecryption() {

        // Use generateAesKey to generate the private key
        Blob key = Crypto.generateAesKey(128);
        // Generate the data to be encrypted.
        Blob data = Blob.valueOf('Test data');
        // Generate an encrypted form of the data using base64 encoding
        String b64Data = EncodingUtil.base64Encode(data);
        // Encrypt and decrypt the data
        Blob encryptedData = Crypto.encryptWithManagedIV('AES128', key, data);
        Blob decryptedData = Crypto.decryptWithManagedIV('AES128', key, encryptedData);
        String b64Decrypted = EncodingUtil.base64Encode(decryptedData);
        // Verify that the strings still match
        System.assertEquals(b64Data, b64Decrypted);
    }
    static testMethod void testInvalidDecryption() {
        // Verify that you must use the same key size for encrypting data
        // Generate two private keys, using different key sizes
        Blob keyOne = Crypto.generateAesKey(128);
        Blob keyTwo = Crypto.generateAesKey(256);
        // Generate the data to be encrypted.
        Blob data = Blob.valueOf('Test data');
        // Encrypt the data using the first key 
        Blob encryptedData = Crypto.encryptWithManagedIV('AES128', keyOne, data);
        try {
         // Try decrypting the data using the second key   
            Crypto.decryptWithManagedIV('AES256', keyTwo, encryptedData);
            System.assert(false);
        } catch(SecurityException e) {
            System.assertEquals('Given final block not properly padded', e.getMessage());
        }
    }
}

对数据进行编码

您可以对 URL 进行编码和解码,并将字符串转换为十六进制 使用类提供的方法设置格式。

EncodingUtil

此示例演示如何对 UTF-8 中的时间戳值进行 URL 编码 通过调用 .urlEncode

DateTime d = System.now();
String timestamp = ''+ d.year() + '-' +
    d.month() + '-' +
    d.day() + '\'T\'' +
    d.hour() + ':' +
    d.minute() + ':' +
    d.second() + '.' +
    d.millisecond() + '\'Z\'';
System.debug(timestamp);
String urlEncodedTimestamp = EncodingUtil.urlEncode(timestamp, 'UTF-8');
System.debug(urlEncodedTimestamp);

下一个示例演示如何使用 HTTP 摘要计算客户端响应 身份验证 (RFC2617)。convertToHex

@isTest
private class SampleTest {
   static testmethod void testConvertToHex() {
      String myData = 'A Test String';
      Blob hash = Crypto.generateDigest('SHA1',Blob.valueOf(myData));
      String hexDigest = EncodingUtil.convertToHex(hash);
      System.debug(hexDigest);  
    } 
}

使用模式和匹配器

Apex 提供模式和匹配器,使您能够使用 正则表达式。

模式是正则表达式的编译表示形式。 匹配器使用模式对字符执行匹配操作 字符串。

正则表达式是用于匹配的字符串 另一个字符串,使用特定语法。Apex 支持通过其 Pattern 和 Matcher 类使用正则表达式。

注意

在 Apex 中,Patterns 和 Matchers 以及正则表达式都是基于 在 Java 中的对应物上。请参见 http://java.sun.com/j2se/1.5.0/docs/api/index.html?java/util/regex/Pattern.html。

许多 Matcher 对象可以共享同一个 Pattern 对象,如下所示 在下图中:

可以从同一个 Pattern 对象创建许多 Matcher 对象A flow chart showing flow from Regular Expression to Matcher object

Apex 中的正则表达式遵循 Java 中使用的正则表达式的标准语法。 任何基于 Java 的正则表达式字符串都可以轻松导入到 您的 Apex 代码。

注意

Salesforce 限制常规输入序列的次数 表达式可以访问 1,000,000 次。如果达到该限制, 收到运行时错误。

所有正则表达式都指定为字符串。最常规 表达式首先编译到 Pattern 对象中:只有 String 方法采用正则表达式 这没有被编译。split通常,在将正则表达式编译为 Pattern 之后 对象,则只需使用 Pattern 对象一次即可创建 Matcher 对象。 然后,使用 Matcher 对象执行所有进一步的操作。为 例:

// First, instantiate a new Pattern object "MyPattern"
Pattern MyPattern = Pattern.compile('a*b');

// Then instantiate a new Matcher object "MyMatcher"
Matcher MyMatcher = MyPattern.matcher('aaaaab');

// You can use the system static method assert to verify the match
System.assert(MyMatcher.matches());

如果只打算使用一次正则表达式,请使用 class 方法编译表达式 并在单个调用中将字符串与其匹配。例如 以下内容等效于上面的代码:Patternmatches

Boolean Test = Pattern.matches('a*b', 'aaaaab');
  • 使用区域
  • 使用匹配操作
  • 使用边界
  • 了解捕获组
  • 模式和匹配器示例

使用区域

Matcher 对象在其输入字符串的子集中查找匹配项 称为区域。Matcher 对象的默认区域 始终是输入字符串的整个部分。但是,您可以更改 使用该方法获取某个区域的起点和终点,您可以查询 使用 和 方法的区域终结点。regionregionStartregionEnd

该方法需要 起始值和结束值。下表提供了示例 如何在不设置另一个值的情况下设置另一个值。region

区域开始区域结束代码示例
显式指定保持不变MyMatcher.region(start, MyMatcher.regionEnd());
保持不变显式指定MyMatcher.region(MyMatcher.regionStart(), end);
重置为默认值显式指定MyMatcher.region(0, end);

使用匹配操作

Matcher 对象对角色执行匹配操作 序列,通过解释 Pattern。

Matcher 对象是通过 Pattern 的方法从 Pattern 实例化的。创建后,匹配器 对象可用于执行以下类型的匹配操作:matcher

  • 将 Matcher 对象的整个输入字符串与模式进行匹配 使用方法matches
  • 将 Matcher 对象的输入字符串与模式匹配,从 在开始时,但不匹配整个区域,使用该方法lookingAt
  • 扫描 Matcher 对象的输入字符串以查找下一个子字符串 与使用方法的模式匹配find

这些方法中的每一个都返回一个指示成功或失败的布尔值。

使用这些方法中的任何一种后,您可以找到更多信息 关于上一个匹配项,即找到的内容,通过使用以下命令 Matcher 类方法:

  • end:一旦匹配 made,此方法返回匹配字符串中 匹配的最后一个字符。
  • start:一旦匹配 made,此方法返回第一个字符串中的位置 匹配的字符。
  • group:一旦匹配 made,此方法返回匹配的子序列。

使用边界

默认情况下,区域由定位边界分隔,这意味着线锚点(例如 或 )在区域边界处匹配,即使区域边界也是如此 已从输入字符串的开头和结尾移开。您可以 指定区域是否对方法使用定位边界。默认情况下, 区域始终使用定位边界。如果设置为 ,则线锚点仅匹配 输入字符串的真正末尾。^$useAnchoringBoundsuseAnchoringBoundsfalse

默认情况下,不会搜索位于区域之外的所有文本, 也就是说,该区域具有不透明的边界。但是,使用透明边界可以搜索外部的文本 一个区域。仅当区域不再使用透明边界时 包含整个输入字符串。您可以指定边界的类型 区域具有使用该方法。useTransparentBounds

假设您正在搜索以下字符串,以及您所在的地区 只是“STRING”这个词:

This is a concatenated STRING of cats and dogs.

如果你搜索“猫”这个词,你不会 接收匹配项,除非您设置了透明边界。

了解捕获组

在匹配操作期间,输入字符串的每个子字符串 与模式匹配的将被保存。这些匹配的子字符串称为捕获组

捕获组通过计算其左括号进行编号 从左到右。例如,在正则表达式字符串中,有四个捕获 组:((A)(B(C)))

  1. ((A)(B(C)))
  2. (A)
  3. (B(C))
  4. (C)

组 0 始终代表整个表达式。

与组关联的捕获输入始终是子字符串 最近匹配的组,即返回的组 Matcher 类匹配操作之一。

如果使用其中一个匹配操作第二次评估组, 如果第二次评估,则保留其先前捕获的值(如果有) 失败。

模式和匹配器示例

Matcher 类方法 返回匹配字符串中最后一个字符之后的位置 那是匹配的。在解析字符串时,您将使用它 并希望在找到匹配项后对其进行其他工作, 比如找到下一个匹配项。end

在正则表达式语法中,表示匹配一次或根本不匹配,表示匹配 1 次或多次。?+

在以下示例中,使用 Matcher 传入的字符串 对象与模式匹配,因为匹配字符串 – 后跟一次。然后它与最后一个匹配 – 然后是根本不匹配。(a(b)?)‘ab’‘a’‘b’‘a’‘a’‘b’

pattern myPattern = pattern.compile('(a(b)?)+'); 
matcher myMatcher = myPattern.matcher('aba');
System.assert(myMatcher.matches() && myMatcher.hitEnd());

// We have two groups: group 0 is always the whole pattern, and group 1 contains 
// the substring that most recently matched--in this case, 'a'. 
// So the following is true:

System.assert(myMatcher.groupCount() == 2 &&
              myMatcher.group(0) == 'aba' && 
              myMatcher.group(1) == 'a');
 
// Since group 0 refers to the whole pattern, the following is true:

System.assert(myMatcher.end() == myMatcher.end(0));

// Since the offset after the last character matched is returned by end, 
// and since both groups used the last input letter, that offset is 3
// Remember the offset starts its count at 0. So the following is also true:

System.assert(myMatcher.end() == 3 && 
              myMatcher.end(0) == 3 && 
              myMatcher.end(1) == 3);

在以下示例中,电子邮件地址被规范化并重复 如果存在不同的顶级域名或子域名,则会报告 对于相似的电子邮件地址。例如,规范化为 。john@fairway.smithcojohn@smithco

class normalizeEmailAddresses{

    public void hasDuplicatesByDomain(Lead[] leads) {
           // This pattern reduces the email address to 'john@smithco' 
           // from 'john@*.smithco.com' or 'john@smithco.*'
        Pattern emailPattern = Pattern.compile('(?<=@)((?![\\w]+\\.[\\w]+$)
                                               [\\w]+\\.)|(\\.[\\w]+$)');
           // Define a set for emailkey to lead:
        Map<String,Lead> leadMap = new Map<String,Lead>();
                for(Lead lead:leads) {
                    // Ignore leads with a null email
                    if(lead.Email != null) {
                           // Generate the key using the regular expression
                       String emailKey = emailPattern.matcher(lead.Email).replaceAll('');
                           // Look for duplicates in the batch
                       if(leadMap.containsKey(emailKey)) 
                            lead.email.addError('Duplicate found in batch');
                       else {
                           // Keep the key in the duplicate key custom field
                            lead.Duplicate_Key__c = emailKey;
                            leadMap.put(emailKey, lead);
                       }
                 }
             }
                // Now search the database looking for duplicates 
                for(Lead[] leadsCheck:[SELECT Id, duplicate_key__c FROM Lead WHERE 
                duplicate_key__c IN :leadMap.keySet()]) {
               for(Lead lead:leadsCheck) {
               // If there's a duplicate, add the error.
                   if(leadMap.containsKey(lead.Duplicate_Key__c)) 
                      leadMap.get(lead.Duplicate_Key__c).email.addError('Duplicate found 
                         in salesforce(Id: ' + lead.Id + ')');
            }
        }
    }
 }