如何实施 Tableau 可信身份验证?
How to implement Tableau Trusted Authentication?
1) 在 Web 应用程序中查看嵌入式仪表板时,系统会提示用户登录 Tableau。
2) 如果他们关闭浏览器、启动不同的浏览器会话或让 Tableau cookie 过期,系统将提示他们重新登录。
3) 一整天,当您尝试查看仪表板时,系统可能会提示您多次登录。这很快就会变得烦人和厌烦。
Tableau 提供了一个名为 "Trusted Authentication" 的解决方案,它可以绕过手动登录过程。经过一周的调试和故障排除,我能够完成这项工作。我在 Whosebug 上找不到任何解决方案,所以我想分享我是如何实现这一点的知识,希望能帮助到其他人。
Link 到 Tableau 的 How Trusted Authentication Works
关于我如何实施可信身份验证的高级视图
1) Tableau 服务器必须有一个 wgserver.trusted_hosts 文件条目,其中包含您的 Web 应用程序的主机名,这样才能正常工作。
2)传递三个重要参数:
username 212456449
server http://[server]
target_site YourTargetSiteName
3) 如果 HTTP POST 请求有效并且用户拥有正确的 Tableau 许可证,Tableau 会创建一个只有 3 分钟有效的 48 个唯一字符的票证。
4) 我在 Tableau 兑换它之前以编程方式将 48 个唯一字符票添加到嵌入式 JavaScript 中。
代码在我的网络应用程序中的工作方式
我创建了一个包含两种方法的 TrustedAuth class:requestTicket()
和 addTicket()
。 requestTicket()
是一个采用三个必需参数(sso、服务器、站点)的异步方法。 HTTP POST 被触发并等待响应。如果 Tableau 响应为 -1 ,则 HTTP 握手失败或用户无效。如果有效,响应将是一个 48 个字符的加密字符串。
addTicket()
是一个采用两个参数(ticket、reportLink)的同步方法。此方法获取 48 个字符的加密票证并将其附加到嵌入的 JavaScript (reportLink).
Web 应用程序向 Tableau 发送 HTTP GET 请求,其中包含带有加密票证的嵌入式 JavaScript(报告Link)。 Tableau Server 兑换票证,创建会话,让用户登录,没有显示登录提示
TrustedAuth Class
public class TrustedAuth
{
public async Task<string> requestTicket(int sso, string server, string site)
{
try
{
//Assign parameters and values
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("username", sso.ToString()));
values.Add(new KeyValuePair<string, string>("target_site", site));
//Web Application is HTTP and Tableau is HTTPS, there are certification issues. I need to fake the certs out and return them as true.
System.Net.ServicePointManager.ServerCertificateValidationCallback = (senderX, certificate, chain, sslPolicyErrors) => { return true; };
//Instantiate HttpClient class
var client = new HttpClient();
//Encode Content
var req = new HttpRequestMessage(HttpMethod.Post, server) { Content = new FormUrlEncodedContent(values) };
//POST request
var res = await client.SendAsync(req);
//Get response value
var responseString = await res.Content.ReadAsStringAsync();
return responseString;
}
catch (Exception e)
{
System.IO.File.AppendAllText(@"c:\inetpub\wwwroot\WebApplication\TrustedAuthError.txt", ":::ERROR::: " + System.DateTime.Today.ToString() + ":::" + e.ToString() + Environment.NewLine);
//Add Log4Net logging
}
return "-1";
}
public string addTicket(string ticket, string reportLink)
{
//Add ticket parameter with ticket value. I'm using </object> as my keyword to find and replace
string addedTicket = reportLink.Replace("</object>", "<param name='ticket' value='" + ticket + "' /></object>");
return addedTicket;
}
}
仪表板控制器
public async Task<ActionResult> Dashboard(int Report_Num)
{
//db will be your database model where your Report_Link is stored
Report_Completion_Status_NEW report_Completion_Status = db.Report_Completion_Status_NEW.Find(Report_Num);
if (report_Completion_Status == null)
{
return HttpNotFound();
}
var ticket = "";
//Get Trusted Tableau Authentication Ticket
try
{
//For example purposes, I'm hard-coding the Tableau Server Name and Site Name for the example _trustedAuth.requestTicket method. In my actual code, I'm storing these in my web.config.
ticket = await _trustedAuth.requestTicket(b.getSSO(User.Identity.Name), "https://ProdTableauUrlGoesHere.com/trusted", "YourTargetSiteNameHere");
}
catch
{
ticket = "-1";
}
//Only add trusted Tableau Authentication ticket if it's valid, else kick user to default Report_Link which will make them login manually.
//You get a nasty error message if you pass in a '-1'
if (!ticket.Equals("-1"))
{
ViewBag.Link = _trustedAuth.addTicket(ticket.ToString(), report_Completion_Status.Report_Link);
}
else
{
ViewBag.Link = report_Completion_Status.Report_Link;
}
var model = await this.GetFullAndPartialViewModel(Report_Num);
return this.View(model);
}
新嵌入 JavaScript(报告Link)插入了票证参数
仪表板视图
@model WebReportingToolDAL.Models.ViewModels.ReportCategoryListModel
@{
ViewBag.Title = "Dashboard";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<body>
@Html.Raw(ViewBag.Link)
</body>
如果一切正常,您应该不会再看到 Tableau 登录页面。
我就是这样做的
[NonAction]
private static async Task<String> GetTableauStringAsync(string userForTableau)
{
string postData = "username="+ userForTableau;
byte[] data = System.Text.Encoding.ASCII.GetBytes(postData);
var myTicket = "";
try
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://myTableauServer.com/trusted");
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = postData.Length;
Stream outStream = req.GetRequestStream();
outStream.Write(data, 0, data.Length);
outStream.Close();
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
StreamReader inStream = new StreamReader(res.GetResponseStream());
string resString = inStream.ReadToEnd();
inStream.Close();
myTicket = resString;
}
catch (Exception ex)
{
string exceptionMessage = ex.Message;
string innerException = ex.InnerException.Message;
myTicket = "ERROR";
}
return myTicket;
}
控制器
[HttpGet]
public async Task<ActionResult> Index()
{
string resultText = String.Empty;
var task = GetTableauStringAsync(subjectName);
var result = await task;
resultText = result;
ViewBag.TableauTicket = resultText ?? " _";
return View();
}
Java 脚本
@section Scripts {
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var myTicket = $("#lblTableauTicket").text();
var patientBKVal = $("#lblPatient_BK").text();
var destination = "https://myTableauServer.com/trusted/" + myTicket + "/views/MyScorecard_15804618842350/MyScorecard?Patient_BK=" + patientBKVal + "&iframeSizedToWindow=true&:embed=y&:showAppBanner=false&:display_count=no&:showVizHome=no&:origin=viz_share_link";
window.location.href = destination;
});
</script>
}
1) 在 Web 应用程序中查看嵌入式仪表板时,系统会提示用户登录 Tableau。
2) 如果他们关闭浏览器、启动不同的浏览器会话或让 Tableau cookie 过期,系统将提示他们重新登录。
3) 一整天,当您尝试查看仪表板时,系统可能会提示您多次登录。这很快就会变得烦人和厌烦。
Tableau 提供了一个名为 "Trusted Authentication" 的解决方案,它可以绕过手动登录过程。经过一周的调试和故障排除,我能够完成这项工作。我在 Whosebug 上找不到任何解决方案,所以我想分享我是如何实现这一点的知识,希望能帮助到其他人。
Link 到 Tableau 的 How Trusted Authentication Works
关于我如何实施可信身份验证的高级视图
1) Tableau 服务器必须有一个 wgserver.trusted_hosts 文件条目,其中包含您的 Web 应用程序的主机名,这样才能正常工作。
2)传递三个重要参数:
username 212456449
server http://[server]
target_site YourTargetSiteName
3) 如果 HTTP POST 请求有效并且用户拥有正确的 Tableau 许可证,Tableau 会创建一个只有 3 分钟有效的 48 个唯一字符的票证。
4) 我在 Tableau 兑换它之前以编程方式将 48 个唯一字符票添加到嵌入式 JavaScript 中。
代码在我的网络应用程序中的工作方式
我创建了一个包含两种方法的 TrustedAuth class:requestTicket()
和 addTicket()
。 requestTicket()
是一个采用三个必需参数(sso、服务器、站点)的异步方法。 HTTP POST 被触发并等待响应。如果 Tableau 响应为 -1 ,则 HTTP 握手失败或用户无效。如果有效,响应将是一个 48 个字符的加密字符串。
addTicket()
是一个采用两个参数(ticket、reportLink)的同步方法。此方法获取 48 个字符的加密票证并将其附加到嵌入的 JavaScript (reportLink).
Web 应用程序向 Tableau 发送 HTTP GET 请求,其中包含带有加密票证的嵌入式 JavaScript(报告Link)。 Tableau Server 兑换票证,创建会话,让用户登录,没有显示登录提示
TrustedAuth Class
public class TrustedAuth
{
public async Task<string> requestTicket(int sso, string server, string site)
{
try
{
//Assign parameters and values
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("username", sso.ToString()));
values.Add(new KeyValuePair<string, string>("target_site", site));
//Web Application is HTTP and Tableau is HTTPS, there are certification issues. I need to fake the certs out and return them as true.
System.Net.ServicePointManager.ServerCertificateValidationCallback = (senderX, certificate, chain, sslPolicyErrors) => { return true; };
//Instantiate HttpClient class
var client = new HttpClient();
//Encode Content
var req = new HttpRequestMessage(HttpMethod.Post, server) { Content = new FormUrlEncodedContent(values) };
//POST request
var res = await client.SendAsync(req);
//Get response value
var responseString = await res.Content.ReadAsStringAsync();
return responseString;
}
catch (Exception e)
{
System.IO.File.AppendAllText(@"c:\inetpub\wwwroot\WebApplication\TrustedAuthError.txt", ":::ERROR::: " + System.DateTime.Today.ToString() + ":::" + e.ToString() + Environment.NewLine);
//Add Log4Net logging
}
return "-1";
}
public string addTicket(string ticket, string reportLink)
{
//Add ticket parameter with ticket value. I'm using </object> as my keyword to find and replace
string addedTicket = reportLink.Replace("</object>", "<param name='ticket' value='" + ticket + "' /></object>");
return addedTicket;
}
}
仪表板控制器
public async Task<ActionResult> Dashboard(int Report_Num)
{
//db will be your database model where your Report_Link is stored
Report_Completion_Status_NEW report_Completion_Status = db.Report_Completion_Status_NEW.Find(Report_Num);
if (report_Completion_Status == null)
{
return HttpNotFound();
}
var ticket = "";
//Get Trusted Tableau Authentication Ticket
try
{
//For example purposes, I'm hard-coding the Tableau Server Name and Site Name for the example _trustedAuth.requestTicket method. In my actual code, I'm storing these in my web.config.
ticket = await _trustedAuth.requestTicket(b.getSSO(User.Identity.Name), "https://ProdTableauUrlGoesHere.com/trusted", "YourTargetSiteNameHere");
}
catch
{
ticket = "-1";
}
//Only add trusted Tableau Authentication ticket if it's valid, else kick user to default Report_Link which will make them login manually.
//You get a nasty error message if you pass in a '-1'
if (!ticket.Equals("-1"))
{
ViewBag.Link = _trustedAuth.addTicket(ticket.ToString(), report_Completion_Status.Report_Link);
}
else
{
ViewBag.Link = report_Completion_Status.Report_Link;
}
var model = await this.GetFullAndPartialViewModel(Report_Num);
return this.View(model);
}
新嵌入 JavaScript(报告Link)插入了票证参数
仪表板视图
@model WebReportingToolDAL.Models.ViewModels.ReportCategoryListModel
@{
ViewBag.Title = "Dashboard";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<body>
@Html.Raw(ViewBag.Link)
</body>
如果一切正常,您应该不会再看到 Tableau 登录页面。
我就是这样做的
[NonAction]
private static async Task<String> GetTableauStringAsync(string userForTableau)
{
string postData = "username="+ userForTableau;
byte[] data = System.Text.Encoding.ASCII.GetBytes(postData);
var myTicket = "";
try
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://myTableauServer.com/trusted");
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = postData.Length;
Stream outStream = req.GetRequestStream();
outStream.Write(data, 0, data.Length);
outStream.Close();
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
StreamReader inStream = new StreamReader(res.GetResponseStream());
string resString = inStream.ReadToEnd();
inStream.Close();
myTicket = resString;
}
catch (Exception ex)
{
string exceptionMessage = ex.Message;
string innerException = ex.InnerException.Message;
myTicket = "ERROR";
}
return myTicket;
}
控制器
[HttpGet]
public async Task<ActionResult> Index()
{
string resultText = String.Empty;
var task = GetTableauStringAsync(subjectName);
var result = await task;
resultText = result;
ViewBag.TableauTicket = resultText ?? " _";
return View();
}
Java 脚本
@section Scripts {
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var myTicket = $("#lblTableauTicket").text();
var patientBKVal = $("#lblPatient_BK").text();
var destination = "https://myTableauServer.com/trusted/" + myTicket + "/views/MyScorecard_15804618842350/MyScorecard?Patient_BK=" + patientBKVal + "&iframeSizedToWindow=true&:embed=y&:showAppBanner=false&:display_count=no&:showVizHome=no&:origin=viz_share_link";
window.location.href = destination;
});
</script>
}