401 授权错误 Windows Phone 8 通知

Posted

技术标签:

【中文标题】401 授权错误 Windows Phone 8 通知【英文标题】:401 Authorisation Error Windows Phone 8 Notifications 【发布时间】:2014-07-30 13:09:22 【问题描述】:

我正在尝试使用经过身份验证的服务器向我的 Windows Phone 8 应用发送通知,但每当尝试发送通知时我都会收到 401 错误。

我一直按照 MSDN 页面上的说明进行操作,即 http://msdn.microsoft.com/library/windows/apps/ff941099(v=vs.105).aspx 上面写着 The Key-Usage value of the TLS certificate must be set to include client authentication. 我不知道这是什么意思,但在线搜索也没有给我任何线索。

这可能是不正确的,也可能是我的代码,如下所示。

VB.NET 代码:

Private Async Sub ApplicationBarPin_Click(sender As Object, e As EventArgs)
    ' Holds the push channel that is created or found.
            Dim pushChannel As HttpNotificationChannel

            ' The name of our push channel.
            Dim channelName As String = "WindowsPhoneTrainTileNotification"

            ' Try to find the push channel.
            pushChannel = HttpNotificationChannel.Find(channelName)

            ' If the channel was not found, then create a new connection to the push service.
            If pushChannel Is Nothing Then
                pushChannel = New HttpNotificationChannel(channelName, "redsquirrelsoftware.co.uk")
                uri_timer = New DispatcherTimer
                uri_timer.Interval = TimeSpan.FromSeconds(5)
                AddHandler uri_timer.Tick, AddressOf UriTimerTick
                uri_timer.Start()

                ' Register for all the events before attempting to open the channel.
                AddHandler pushChannel.ChannelUriUpdated, AddressOf PushChannel_TileChannelUriUpdated
                AddHandler pushChannel.ErrorOccurred, AddressOf PushChannel_TileErrorOccurred

                pushChannel.Open()

                pushChannel.BindToShellTile()
            Else
                ' The channel was already open, so just register for all the events.
                AddHandler pushChannel.ChannelUriUpdated, AddressOf PushChannel_TileChannelUriUpdated
                AddHandler pushChannel.ErrorOccurred, AddressOf PushChannel_TileErrorOccurred

                Dim form As New MultipartFormDataContent()
                form.Add(New StringContent(Statics.getUserID), "userId")
                form.Add(New StringContent(pushChannel.ChannelUri.ToString()), "uri")
                form.Add(New StringContent(Statics.CurrentScheduleId), "scheduleId")

                Dim httpClient As HttpClient = New HttpClient()

                Dim response As HttpResponseMessage = Await httpClient.PostAsync("http://redsquirrelsoftware.co.uk/trains/push/WPTileSubscribe.php", form)
            End If

            ShellTile.Create(New Uri("/Route.xaml?scheduleId=" & scheduleId, UriKind.Relative), secondaryTile, True) 'Create SecondaryTile and pass querystring to navigation url.
        Catch ex As Exception
        End Try

    End If
End Sub

Private Async Sub PushChannel_TileChannelUriUpdated(sender As Object, e As NotificationChannelUriEventArgs)
    Dim form As New MultipartFormDataContent()
    form.Add(New StringContent(Statics.getUserID), "userId")
    form.Add(New StringContent(e.ChannelUri.ToString()), "uri")
    form.Add(New StringContent(Statics.CurrentScheduleId), "scheduleId")

    Dim httpClient As HttpClient = New HttpClient()

    Dim response As HttpResponseMessage = Await httpClient.PostAsync("http://redsquirrelsoftware.co.uk/trains/push/WPTileSubscribe.php", form)
End Sub

Private Sub PushChannel_TileErrorOccurred(sender As Object, e As NotificationChannelErrorEventArgs)
    MessageBox.Show("Error creating live tile, please try again")
End Sub

Private Async Sub UriTimerTick(sender As Object, e As EventArgs)
    Try
        If pushChannel.ChannelUri IsNot Nothing Then
            Dim form As New MultipartFormDataContent()
            form.Add(New StringContent(Statics.getUserID), "userId")
            form.Add(New StringContent(pushChannel.ChannelUri.ToString()), "uri")
            form.Add(New StringContent(Statics.CurrentScheduleId), "scheduleId")

            Dim httpClient As HttpClient = New HttpClient()

            Dim response As HttpResponseMessage = Await httpClient.PostAsync("http://redsquirrelsoftware.co.uk/trains/push/WPTileSubscribe.php", form)
            uri_timer.Stop()
        End If
    Catch ex As Exception
    End Try

End Sub

按预期工作,并且 URI 存储在我的数据库中(以 HTTPS 开头,并且 URI 中也不包含throttledthirdparty,表明此代码有效)。

train_movements.php:

<?php
include 'WindowsPhoneTilePush.php';
$WPN = new WPN($packageSID, $clientSecret);
$xml_data = $WPN->build_tile_xml($sched_id, strtoupper($loc), "$eventTypeStr $dt (". strtolower($variation_status) . ")");
$WPNResponse = $WPN->post_tile($uri, $xml_data);
if($WPNResponse->error == true) 
    $my_file = $logFile;
    $handle = fopen($my_file, 'a') or die('Cannot open file:  '.$my_file);
    $data = $WPNResponse->httpCode . ":" . $WPNResponse->message . "\n";
    fwrite($handle, $data);

?>

WindowsPhoneTilePush.php:(改编自here)

<?php
class WPNTypesEnum       
    const Toast = 'wns/toast';
    const Badge = 'wns/badge';
    const Tile  = 'wns/tile';
    const Raw   = 'wns/raw';
                         

class WPNResponse
    public $message = '';
    public $error = false;
    public $httpCode = '';

    function __construct($message, $httpCode, $error = false)
        $this->message = $message;
        $this->httpCode = $httpCode;
        $this->error = $error;
    


class WPN            
    private $access_token = '';
    private $sid = '';
    private $secret = '';

    function __construct($sid, $secret)
        $this->sid = $sid;
        $this->secret = $secret;
    

    private function get_access_token()
        if($this->access_token != '')
            return;
        

        $str = "grant_type=client_credentials&client_id=$this->sid&client_secret=$this->secret&scope=notify.windows.com";
        $url = "https://login.live.com/accesstoken.srf";

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
        curl_setopt($ch, CURLOPT_POSTFIELDS, "$str");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        curl_close($ch);                       

        $output = json_decode($output);

        if(isset($output->error))
            throw new Exception($output->error_description);
        

        $this->access_token = $output->access_token;
    

    public function build_tile_xml($tile_nav_uri, $wide2, $wide3)
        $msg =  "<?xml version=\"1.0\" encoding=\"utf-8\"?>" .
                "<wp:Notification xmlns:wp=\"WPNotification\" Version=\"2.0\">" .
                "<wp:Tile Id=\"/Route.xaml?scheduleId=$tile_nav_uri\" Template=\"IconicTile\">" .
                //"<wp:SmallIconImage>$image_url_small</wp:SmallIconImage>" .
                //"<wp:IconImage>$image_url_large</wp:IconImage>" .
                //"<wp:WideContent1>$wide1</wp:WideContent1>" .
                "<wp:WideContent2>$wide2</wp:WideContent2>" .
                "<wp:WideContent3>$wide3</wp:WideContent3>" .
                //"<wp:Count>$count</wp:Count>" .
                //"<wp:Title>$title</wp:Title>" .
                //"<wp:BackgroundColor>#00FFFFFF</wp:BackgroundColor>" .
                "</wp:Tile>" .
                "</wp:Notification>";

        return $msg;

    

    public function post_tile($uri, $xml_data, $type = WPNTypesEnum::Tile, $tileTag = '', $count = 0)
        if($this->access_token == '')
            $this->get_access_token();
        

        $headers = array('Content-Type: text/xml', "Content-Length: " . strlen($xml_data), "X-WNS-Type: $type", "Authorization: Bearer $this->access_token");
        if($tileTag != '')
            array_push($headers, "X-WNS-Tag: $tileTag");
        

        $ch = curl_init($uri);
        # Tiles: http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868263.aspx
        # http://msdn.microsoft.com/en-us/library/windows/apps/hh465435.aspx
        curl_setopt($ch, CURLOPT_CAPATH, "/var/www/clients/client1/web1/ssl/");
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POSTFIELDS, "$xml_data");
        curl_setopt($ch, CURLOPT_VERBOSE, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        $response = curl_getinfo( $ch );
        curl_close($ch);

        $code = $response['http_code'];
        if($code == 200)
            return new WPNResponse('Successfully sent message', $code);
        
        else if($code == 401)
            $my_file = '/var/log/WPNotify.txt';
            $handle = fopen($my_file, 'a') or die('Cannot open file:  '.$my_file);
            $data = date("d-M-y H:i:s:") . "401 Authorisation Error:" . $this->access_token . "\n";
            fwrite($handle, $data);

            if($count == 10) 
                exit;
            
            $this->access_token = '';
            return $this->post_tile($uri, $xml_data, $type, $tileTag, $count++);
        
        else if($code == 410 || $code == 404)
            return new WPNResponse('Expired or invalid URI', $code, true);
        
        else
            return new WPNResponse('Unknown error while sending message', $code, true);
        
    

?>

我的代码中可能有错误,或者可能是我没有正确设置后端内容(大多数 MSDN 文档都针对 Windows Server 和 .aspx,所以我尝试复制这些说明,但可能在某个地方出错了!)。

如果有人可以帮助修复代码中的错误,或者通过 SSL 证书的客户端身份验证,我们将不胜感激!

谢谢, 杰克

【问题讨论】:

Look up HTTP Error 401,它相当不言自明,甚至可能与您希望我们涉足的大量代码无关。 @RiggsFolly 我知道什么是 401 错误,在过去 3 天试图解决这个问题无济于事之后,我希望这里的人能够对这种情况有所了解。我意识到那里有很多代码,但如果我在没有任何代码的情况下提出问题,我保证第一条评论会是“发布你的代码” 【参考方案1】:

看看 Windows Notifications Service: 401 Invalid Token when trying to create a Toast notification in PHP 尝试创建 toast-n 时的无效令牌

确保在请求访问令牌时将应用的 package SID 作为“client_id”参数而不是应用的 client_id 发送。

这真的很令人困惑,两者都能够从微软的服务器获取 access_token,但是在发送 toast 通知时,只有向你请求的包 SID 的令牌不会给你 401 错误。

【讨论】:

以上是关于401 授权错误 Windows Phone 8 通知的主要内容,如果未能解决你的问题,请参考以下文章

HTTP错误401.0 - 未经授权的错误消息

401 - 在 IE7 中未经授权,仅使用 Windows 身份验证。

如何在 Laravel/Lumen 中捕获 401 响应?

在Windows Server 2003 的IIS6.0发布网站出现401错误

您指定的网页无法访问 错误类型500是啥意思?

MPNS 错误请求 Windows Phone 8.1 (Cordova/Phonegap)