可用于大多数功能,限制应用。 General Shield平台加密注意事项和限制: https://developer.salesforce.com/docs/atlas.en-us.securityImplGuide.meta/securityImplGuide/security_pe_considerations_general.htm
将height属性设置为签名请求中提供的clientHeight值。例如: // Where sr is a parsed signed request object. var h = parseInt(sr.context.environment.dimensions.clientHeight, 10); Sfdc.canvas.byId(‘divElementId’).style.height = h;
导航到指定类型的Feed,作用域为subjectId。对于某些Feed类型,subjectId是必需的但是被忽略。对于这些Feed类型,将当前用户的ID作为subjectId传递。 type是Feed类型。可能的值如下。 •BOOKMARKS:包含上下文用户保存为书签的所有Feed项。将当前用户的ID作为subjectId传递。 •COMPANY:包含除TrackedChange类型的Feed项目之外的所有Feed项目。要查看订阅源项,用户必须具有对其父项的共享访问权限。将当前用户的ID作为subjectId传递。 •文件:包含所有包含上下文用户遵循的人员或组发布的文件的源项目。将当前用户的ID作为subjectId传递。 •GROUPS:包含上下文用户拥有或属于其成员的所有组中的所有Feed项。将当前用户的ID作为subjectId传递。 •新闻:包含上下文用户遵循的所有更新,用户所属的组以及用户所关注的文件和记录。包含父级为上下文用户的记录的所有更新。包含提及上下文用户或提及上下文用户所属的组的每个提要项和注释。将当前用户的ID作为subjectId传递。 •PEOPLE:包含上下文用户遵循的所有人发布的所有Feed项。将当前用户的ID作为subjectId传递。 •RECORD:包含父级为指定记录的所有订阅源项,可以是组,用户,对象,文件或任何其他标准或自定义对象。当记录是一个组时,该提要还包含提及该组的提要项。当记录是用户时,该Feed仅包含该用户的Feed项。您可以获取其他用户的记录Feed。将记录的ID作为subjectId传递。 •TO:包含上下文用户提及的所有Feed项。包含上下文用户注释的订阅源项以及由评论的上下文用户创建的订阅源项。将当前用户的ID作为subjectId传递。 •TOPICS:包含包含指定主题的所有Feed项。将主题的ID作为subjectId传递。仅Salesforce for Mobile Web支持此值。 Salesforce for iOS或Salesforce for Android中不提供主题
// Target a specific canvas app
// where “app1” is the canvasId specified in the canvas component.
// For example:
Sfdc.canvas.controller.publish({name : ‘mynamespace.myevent’,
payload : {},
target : {canvas : ‘app1’}});
// Set the height and width explicitly and target a canvas app
// Where ‘mycanvas0’ is the canvasId on the canvas component
// <apex:canvasApp canvasId=”mycanvas0″/>
var target = {canvas : “mycanvas0”};
Sfdc.canvas.controller.resize( {height : “1000px”, width : “900px”}, target);
// Subscribe to a single event.
Sfdc.canvas.controller.subscribe({name : ‘mynamespace.myevent0’,
onData : function (e) {}});
// Subscribe to multiple events in a single call.
Sfdc.canvas.controller.subscribe([
{name : ‘mynamespace.myevent1’, onData : function(e) {}},
{name : ‘mynamespace.myevent2’, onData : function(e) {}}
]);
This is the default authorization method for canvas apps. The signed request authorization flow varies depending on whether the canvas app’s Permitted Users field is set to “Admin approved users are pre-authorized” or “All users may self-authorize.”
/**
*The utility method can be used to validate/verify the signed request.
*In this case, the signed request is verified that it’s from Salesforce and that
*it has not been tampered with.
*This utility class has two methods. One verifies and decodes the request
*as a Java object, the other as a JSON String.
*/
public class SignedRequest {
public static CanvasRequest verifyAndDecode(String input, String secret)
throws SecurityException {
String[] split = getParts(input);
String encodedSig = split[0];
String encodedEnvelope = split[1];
// Deserialize the JSON body.
String json_envelope = new String(new Base64(true).decode(encodedEnvelope));
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.reader(CanvasRequest.class);
CanvasRequest canvasRequest;
String algorithm;
try {
canvasRequest = reader.readValue(json_envelope); algorithm = canvasRequest.getAlgorithm() == null ?
“HMACSHA256” : canvasRequest.getAlgorithm();
} catch (IOException e) {
throw new SecurityException(String.format(“Error [%s] deserializing JSON to Object [%s]”, e.getMessage(), CanvasRequest.class.getName()), e);
}
verify(secret, algorithm, encodedEnvelope, encodedSig);
// If we got this far, then the request was not tampered with.
// Return the request as a Java object.
return canvasRequest;
}
public static String verifyAndDecodeAsJson(String input, String secret) throws SecurityException {
String[] split = getParts(input);
String encodedSig = split[0];
String encodedEnvelope = split[1];
String json_envelope = new String(new Base64(true).decode(encodedEnvelope));
ObjectMapper mapper = new ObjectMapper();
String algorithm; StringWriter writer;
TypeReference<HashMap<String,Object>> typeRef
= new TypeReference<HashMap<String, Object>>() { };
try {
HashMap<String,Object> o = mapper.readValue(json_envelope, typeRef);
writer = new StringWriter();
mapper.writeValue(writer, o);
algorithm = (String)o.get(“algorithm”);
} catch (IOException e) {
throw new SecurityException(String.format(“Error [%s] deserializing JSON to Object [%s]”, e.getMessage(),
typeRef.getClass().getName()), e);
}
verify(secret, algorithm, encodedEnvelope, encodedSig);
// If we got this far, then the request was not tampered with.
// Return the request as a JSON string.
return writer.toString();
}
private static String[] getParts(String input) {
if (input == null || input.indexOf(“.”) <= 0) {
throw new SecurityException(String.format(“Input [%s] doesn’t look like a signed request”, input));
}
String[] split = input.split(“[.]”, 2); return split;
}
private static void verify(String secret, String algorithm, String encodedEnvelope, String encodedSig )
throws SecurityException
{
if (secret == null || secret.trim().length() == 0) {
throw new IllegalArgumentException(“secret is null, did you set your environment variable CANVAS_CONSUMER_SECRET?”);
}
SecretKey hmacKey = null;
try {
byte[] key = secret.getBytes();
hmacKey = new SecretKeySpec(key, algorithm);
Mac mac = Mac.getInstance(algorithm); mac.init(hmacKey);
// Check to see if the body was tampered with.
byte[] digest = mac.doFinal(encodedEnvelope.getBytes());
byte[] decode_sig = new Base64(true).decode(encodedSig);
if (! Arrays.equals(digest, decode_sig)) {
String label = “Warning: Request was tampered with”; throw new SecurityException(label);
}
} catch (NoSuchAlgorithmException e) {
throw new SecurityException(String.format(“Problem with algorithm [%s] Error [%s]”, algorithm, e.getMessage()), e);
} catch (InvalidKeyException e) {
throw new SecurityException(String.format(“Problem with key [%s] Error [%s]”, hmacKey, e.getMessage()), e);
}
// If we got here and didn’t throw a SecurityException then all is good.
}
}
调用verifyAndDecode函数
以下代码显示了获取签名请求,然后使用verifyAndDecode函数验证和解码请求的示例。
// From a JSP or servlet.
<%@ page import=”canvas.SignedRequest” %>
<%@ page import=”java.util.Map” %>
<%
// Pull the signed request out of the request body and verify/decode it. Map<String, String[]> parameters = request.getParameterMap();
String[] signedRequest = parameters.get(“signed_request”); if (signedRequest == null) {%>
This app must be invoked via a signed request!<% return;
}
String yourConsumerSecret=System.getenv(“CANVAS_CONSUMER_SECRET”); String signedRequest = SignedRequest.verifyAndDecode(signedRequest[0],
yourConsumerSecret);
%>
…
// From JavaScript, you can handle the signed request as needed. var signedRequest = ‘<%=signedRequestJson%>’;
// From a JSP or servlet.
<%@ page import=”canvas.SignedRequest” %>
<%@ page import=”java.util.Map” %>
<%
// Pull the signed request out of the request body and verify/decode it. Map<String, String[]> parameters = request.getParameterMap();
String[] signedRequest = parameters.get(“signed_request”); if (signedRequest == null) {%>
This App must be invoked via a signed request!<% return;
}
String yourConsumerSecret=System.getenv(“CANVAS_CONSUMER_SECRET”);
String signedRequestJson = SignedRequest.verifyAndDecodeAsJson(signedRequest[0], yourConsumerSecret);
%>
…
// From JavaScript, you can parse with your favorite JSON library. var signedRequest = JSON.parse(‘<%=signedRequestJson%>’);
// Gets a signed request on demand. Sfdc.canvas.client.refreshSignedRequest(function(data) {
if (data.status === 200) {
var signedRequest = data.payload.response; var part = signedRequest.split(‘.’)[1];
var obj = JSON.parse(Sfdc.canvas.decode(part));
}
}
// Paste the signed request string into a JavaScript object for easy access. var sr = JSON.parse(‘<%=signedRequestJson%>’);
// Reference the Chatter user’s URL from Context.Links object. var chatterUsersUrl = sr.context.links.chatterUsersUrl;
// Make an XHR call back to salesforce through the supplied browser proxy. Sfdc.canvas.client.ajax(chatterUsersUrl,
{client : sr.client, success : function(data){
// Make sure the status code is OK. if (data.status === 200) {
// Alert with how many Chatter users were returned. alert(“Got back ” + data.payload.users.length +
” users”); // Returned 2 users
}
}});
发布到Chatter Feed
以下代码示例显示了将项目发布到上下文用户的Chatter摘要的调用。
var sr = JSON.parse(‘<%=signedRequestJson%>’);
// Reference the Chatter user’s URL from Context.Links object.
var url = sr.context.links.chatterFeedsUrl+”/news/”+sr.context.user.userId+”/feed-items”;
var body = {body : {messageSegments : [{type: “Text”, text: “Some Chatter Post”}]}};
Sfdc.canvas.client.ajax(url,
{client : sr.client, method: ‘POST’,
contentType: “application/json”, data: JSON.stringify(body), success : function(data) {
if (201 === data.status) { alert(“Success”);
}
}
});
// Turn on auto grow with default settings. Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.autogrow(sr.client);
});
// Turn on auto grow with polling interval of 100ms (milliseconds). Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.autogrow(sr.client, true, 100);
});
// Turn off auto grow. Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.autogrow(sr.client, false);
});
// Automatically determine the size. Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.resize(sr.client);
});
// Set the height and width explicitly.
Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.resize(sr.client, {height : “1000px”, width : “900px”});
});
// Set only the height. Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.resize(sr.client, {height : “1000px”});
});
// Subscribe to Streaming API events.
// The PushTopic to subscribe to must be passed in.
// The ‘onComplete’ method may be defined,
// and will fire when the subscription is complete.
Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
var handler1 = function(){ console.log(“onData done”);},
handler2 = function(){ console.log(“onComplete done”);};
Sfdc.canvas.client.subscribe(sr.client,
{name : ‘sfdc.streamingapi’, params:{topic:”/topic/InvoiceStatements”}}, onData : handler1, onComplete : handler2}
);
});
运行以下命令:keytool -keystore keystore -alias jetty -genkey -keyalg RSA。 运行此命令后,系统将提示您输入以下信息。输入123456作为密钥库密码,输入是以在最后确认。出现提示时,“输入的密钥密码”,按Enter键以使用密钥库密码。对于其他信息,您可以输入值或将其留空。 Enter keystore password: 《Choose Your Password》 Re-enter new password: 《Choose Your Password》 What is your first and last name? [Unknown]: 《Enter First and Last Name》 What is the name of your organizational unit? [Unknown]: 《Enter an Org Unit》 What is the name of your organization? [Unknown]: 《Enter an Org》 What is the name of your City or Locality? [Unknown]: 《Enter a City》 What is the name of your State or Province? [Unknown]: 《Enter a State》 What is the two-letter country code for this unit? [Unknown]: 《Enter a Country》 Is CN=XXXX, OU=XXXX, O=XXXX, L=XXXX, ST=XX, C=XX correct? [no]: yes Enter key password for 《jetty》 (RETURN if same as keystore password): 这将在目录c:\ SalesforceCanvasFrameworkSDK中创建名为keystore的文件。 Jetty使用密钥库来支持SSL。
输入以下命令运行Web服务器:target \ bin \ webapp.bat(Windows)或sh target / bin / webapp (Unix / OS X)。 如果您使用的是Unix / OS X,则可能需要先向webapp添加执行权限,然后才能运行它。使用此命令执行此操作:chmod + x target / bin / webapp。
输入以下命令重新启动Web服务器:target \ bin \ webapp.bat(Windows)或sh target / bin / webapp (Unix / OS X)。
如以下代码所示:
<%@ page import=”canvas.SignedRequest” %>
<%@ page import=”java.util.Map” %>
<%
// Pull the signed request out of the request body and verify and decode it. Map<String, String[]> parameters = request.getParameterMap();
String[] signedRequest = parameters.get(“signed_request”); if (signedRequest == null) {%>
This app must be invoked via a signed request!<% return;
}
String yourConsumerSecret=System.getenv(“CANVAS_CONSUMER_SECRET”);
String signedRequestJson = SignedRequest.verifyAndDecodeAsJson(signedRequest[0], yourConsumerSecret);
%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” lang=”en”>
<head>
<title>Hello World Canvas Example</title>
<link rel=”stylesheet” type=”text/css” href=”/sdk/css/connect.css” />
<script type=”text/javascript” src=”/sdk/js/canvas-all.js”></script>
<!– Third part libraries, substitute with your own –>
<script type=”text/javascript” src=”/scripts/json2.js”></script>
<script>
if (self === top) {
// Not in an iFrame.
alert(“This canvas app must be included within an iFrame”);
}
Sfdc.canvas(function() {
var sr = JSON.parse(‘<%=signedRequestJson%>’); Sfdc.canvas.byId(‘username’).innerHTML = sr.context.user.fullName;
});
</script>
</head>
<body>
<br/>
<h1>Hello <span id=’username’></span></h1>
</body>
</html>
运行以下命令:keytool -keystore keystore -alias jetty -genkey -keyalg RSA。 运行此命令后,系统将提示您输入以下信息。输入123456作为密钥库密码,输入是以在最后确认。出现提示时,“输入的密钥密码”,按Enter键以使用密钥库密码。对于其他信息,您可以输入值或将其留空。 Enter keystore password: 《Choose Your Password》 Re-enter new password: 《Choose Your Password》 What is your first and last name? [Unknown]: 《Enter First and Last Name》 What is the name of your organizational unit? [Unknown]: 《Enter an Org Unit》 What is the name of your organization? [Unknown]: 《Enter an Org》 What is the name of your City or Locality? [Unknown]: 《Enter a City》 What is the name of your State or Province? [Unknown]: 《Enter a State》 What is the two-letter country code for this unit? [Unknown]: 《Enter a Country》 Is CN=XXXX, OU=XXXX, O=XXXX, L=XXXX, ST=XX, C=XX correct? [no]: yes Enter key password for 《jetty》 (RETURN if same as keystore password): 这将在目录c:\ SalesforceCanvasFrameworkSDK中创建名为keystore的文件。 Jetty使用密钥库来支持SSL。
输入以下命令运行Web服务器:target \ bin \ webapp.bat(Windows)或sh target / bin / webapp (Unix / OS X)。 如果您使用的是Unix / OS X,则可能需要先向webapp添加执行权限,然后才能运行它。使用此命令执行此操作:chmod + x target / bin / webapp。
输入以下命令重新启动Web服务器:target \ bin \ webapp.bat(Windows)或sh target / bin / webapp (Unix / OS X)。
如以下代码所示:
<%@ page import=”canvas.SignedRequest” %>
<%@ page import=”java.util.Map” %>
<%
// Pull the signed request out of the request body and verify and decode it. Map<String, String[]> parameters = request.getParameterMap();
String[] signedRequest = parameters.get(“signed_request”); if (signedRequest == null) {%>
This app must be invoked via a signed request!<% return;
}
String yourConsumerSecret=System.getenv(“CANVAS_CONSUMER_SECRET”);
String signedRequestJson = SignedRequest.verifyAndDecodeAsJson(signedRequest[0], yourConsumerSecret);
%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” lang=”en”>
<head>
<title>Hello World Canvas Example</title>
<link rel=”stylesheet” type=”text/css” href=”/sdk/css/connect.css” />
<script type=”text/javascript” src=”/sdk/js/canvas-all.js”></script>
<!– Third part libraries, substitute with your own –>
<script type=”text/javascript” src=”/scripts/json2.js”></script>
<script>
if (self === top) {
// Not in an iFrame.
alert(“This canvas app must be included within an iFrame”);
}
Sfdc.canvas(function() {
var sr = JSON.parse(‘<%=signedRequestJson%>’); Sfdc.canvas.byId(‘username’).innerHTML = sr.context.user.fullName;
});
</script>
</head>
<body>
<br/>
<h1>Hello <span id=’username’></span></h1>
</body>
</html>