使用Thinktecture的AngularJs ASP.NET WebApi身份验证

时间:2014-06-06 08:42:59

标签: angularjs authentication asp.net-web-api thinktecture-ident-server thinktecture-ident-model

我正在尝试创建一个AngularJs Web,它将登录名和密码发送到ASP.NET WebApi后端,并使用Thinktecture登录该用户。

我使用WS-Federation让Thinktecture与其他项目ASP.NET MVC一起正常工作。现在我正在尝试做类似的事情,但改变一些组件,我无法使它工作。

如何从ASP.NET WebApi将用户名和密码发送到Thinktecture并验证它?

using System.Collections.Generic;
using System.IdentityModel.Services;
using System.Web.Http;
using WebApi_AngularJs.Model;

namespace WebApi_AngularJs.Controllers
{
    public class AuthorizationController : ApiController
    {    
        // POST: api/Authorization
        public LoginResponse Post([FromBody]Login data)
        {
            //HOW TO SEND data.user and data.password to ThinkTecture and get
            //response if user valid or not??
            var response = new LoginResponse { access_token = "token", data = "data"};
            return response;
        }   

    }
}

谢谢!

2 个答案:

答案 0 :(得分:6)

您需要做一些事情。创建将生成令牌请求的OAuth客户端,并使用该客户端从身份服务器获取访问令牌,以允许您访问Web api端点。为此,您的OAuth客户端需要启用隐式流。然后,您通常会通过弹出窗口向Identity Server发出登录请求,以允许您的OAuth客户端登录。 您需要将登录请求的查询字符串中的OAuth客户端详细信息传递给Idsrv,OAuth客户端配置将是您在OAds客户端的Idsrv管理面板中定义的内容,您可以对其进行参数化并将其附加到oauth2 / authorzie网址:

getIdpOauthEndpointUrl: function () {
                return "https://192.168.1.9/issue/oauth2/authorize";
},
getOAuthConfig: function () {
                return {
                    client_id: "Your Oauth CLient ID that you specifie din Identity Server",
                    scope: "The scope of your RP",
                    response_type: "token",
                    redirect_uri: "https://..YourAngularAppUrl/AuthCallback.html"
                };
}

然后打开登录窗口:

var url = authService.getIdpOauthEndpointUrl() + "?" + $.param(authService.getOAuthConfig());
                    window.open(url, "Login", "height=500,width=350");

在你的OAuth客户端inIdsrv中,你需要指定一个重定向网址,在我们的例子中:

https://YourAngularAppUrl/AuthCallback.html

这是您传递给身份服务器的登录请求以及OAuth客户端详细信息的内容。 AuthCallback.html页面除了将查询字符串中的idsrv返回的访问令牌提取到该页面之外什么都不做,并将其传递到您的角度应用程序中,您如何做到这一点取决于您,但需要放置访问令牌进入$http标题。

可以在AuthCallback.html页面中提取访问令牌,如下所示:

<script src="/Scripts/jquery-2.0.3.js"></script>
<script src="/Scripts/jquery.ba-bbq.min.js"></script>

<script type="text/javascript">
    var params = $.deparam.fragment(location.hash.substring(1));

    window.opener.oAuthCallback(params);
    window.close();
</script>

OAuthCallback函数在我的shell页面中定义,我的index.html负责将它给出的令牌传递到我的角度应用程序并将其放入$http标题中。

function oAuthCallback(OAUTHTOKEN) {
  angular.element(window.document).scope().setHttpAuthHeaderAndAuthenticate(OAUTHTOKEN);
}

我的setHttpAuthHeaderAndAuthenticate()定义了$rootScope函数,它将令牌放入$http授权标题中:

$http.defaults.headers.common.Authorization = 'Bearer ' + OAUTHTOKEN["access_token"];

看看Christian Weyer的this post它正是我们正在做的事情,但它是一个淘汰/ web-api应用程序,同样的概念仍然存在。

下一步是告诉你的web api接受来自idsrv的访问令牌,这很简单;

public static void Configure(HttpConfiguration config)
        {
            var authConfig = new AuthenticationConfiguration();

            authConfig.AddJsonWebToken(
    YourIdsrvSiteId, YourRpsScope/Relam,YourRpsSymmetricSigningKey
);

            config.MessageHandlers.Add(new AuthenticationHandler(authNConfig));
        }

您还可以在此处定义ClaimsAuthenticationManager和ClaimsAuthorizationManager,以允许您转换声明并授予/拒绝访问web api资源。同样,这一切都在Christian Weyer的帖子中有所涉及。希望这会有所帮助。

答案 1 :(得分:5)

最后,在阅读了很多内容后,我有了这个:

在AngularJS中:

'use strict';
app.factory('authService', ['$http', '$q', 'localStorageService', function ($http, $q, localStorageService) {

var serviceBase = 'http://localhost:64346/';
var authServiceFactory = {};

var _authData = localStorageService.get('authorizationData');

var _authentication = {
    isAuth: _authData != null? true : false,
    userName: _authData != null ? _authData.userName : ""
};

var _saveRegistration = function (registration) {

    _logOut();

    return $http.post(serviceBase + 'api/account/register', registration).then(function (response) {
        return response;
    });

};

var _login = function (loginData) {

    var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;

    var deferred = $q.defer();

    $http.post(serviceBase + 'api/authorization', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {

        localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });

        _authentication.isAuth = true;
        _authentication.userName = loginData.userName;

        deferred.resolve(response);

    }).error(function (err, status) {
        _logOut();
        deferred.reject(err);
    });

    return deferred.promise;

};

var _logOut = function () {

    $http.delete(serviceBase + 'api/authorization').success(function() {
        localStorageService.remove('authorizationData');

        _authentication.isAuth = false;
        _authentication.userName = "";
    });
};

var _fillAuthData = function () {

    var authData = localStorageService.get('authorizationData');
    if (authData) {
        _authentication.isAuth = true;
        _authentication.userName = authData.userName;
    }

}

authServiceFactory.saveRegistration = _saveRegistration;
authServiceFactory.login = _login;
authServiceFactory.logOut = _logOut;
authServiceFactory.fillAuthData = _fillAuthData;
authServiceFactory.authentication = _authentication;

return authServiceFactory;
}]);

在WebApi中:

using System.Collections.Generic;
using System.Configuration;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Services;
using System.IdentityModel.Tokens;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.Web.Http;
using System.Xml;
using Thinktecture.IdentityModel.Constants;
using Thinktecture.IdentityModel.WSTrust;
using WebApi_AngularJs.Model;

namespace WebApi_AngularJs.Controllers
{
    public class AuthorizationController : ApiController
    {
        // GET: api/Authorization
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET: api/Authorization/5
        [Authorize]
        public string Get(int id)
        {
            return "value";
        }

    // POST: api/Authorization
    public LoginResponse Post([FromBody]Login data)
    {
        var credentials = new ClientCredentials();
        credentials.UserName.UserName = data.UserName;
        credentials.UserName.Password = data.Password;

        ServicePointManager.ServerCertificateValidationCallback = (obj, certificate, chain, errors) => true;

        var claims = GetClaimsFromIdentityServer(data.UserName, data.Password);

        var response = new LoginResponse();
        if (claims != null)
        {
            //All set so now create a SessionSecurityToken
            var token = new SessionSecurityToken(claims)
            {
                IsReferenceMode = true  //this is 
                //important.this is how you say create 
                //the token in reference mode meaning 
                //your session cookie will contain only a 
                //referenceid(which is very small) and 
                //all claims will be stored on the server
            };
            FederatedAuthentication.WSFederationAuthenticationModule.
            SetPrincipalAndWriteSessionToken(token, true);

            response = new LoginResponse { access_token = token.Id , data = "data"};
        }

        return response;
    }

    // PUT: api/Authorization/5
    public void Put(int id, [FromBody]string value)
    {
    }

    // DELETE: api/Authorization/
    public void Delete()
    {
        //clear local cookie
        FederatedAuthentication.SessionAuthenticationModule.SignOut();
        FederatedAuthentication.SessionAuthenticationModule.DeleteSessionTokenCookie();
        FederatedAuthentication.WSFederationAuthenticationModule.SignOut(false);
    }

    private ClaimsPrincipal GetClaimsFromIdentityServer(string username, string password)
    {
        const string WS_TRUST_END_POINT = "https://srv:4443/issue/wstrust/mixed/username";
        var factory = new System.ServiceModel.Security.WSTrustChannelFactory
        (new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
                                     string.Format(WS_TRUST_END_POINT));
        factory.TrustVersion = TrustVersion.WSTrust13;
        factory.Credentials.UserName.UserName = username;
        factory.Credentials.UserName.Password = password;

        var rst = new System.IdentityModel.Protocols.WSTrust.RequestSecurityToken
        {
            RequestType = RequestTypes.Issue,
            KeyType = KeyTypes.Bearer,
            TokenType = TokenTypes.Saml2TokenProfile11,  
            AppliesTo = new EndpointReference
            ("urn:webapisecurity")
        };
        var st = factory.CreateChannel().Issue(rst);
        var token = st as GenericXmlSecurityToken;
        var handlers = FederatedAuthentication.FederationConfiguration.
        IdentityConfiguration.SecurityTokenHandlers;
        var token = handlers.ReadToken(new XmlTextReader
        (new StringReader(token.TokenXml.OuterXml))) as Saml2SecurityToken;
        var identity = handlers.ValidateToken(token).First();
        var principal = new ClaimsPrincipal(identity);
        return principal;
    }
}
}

在Web.Config中:

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=301879
  -->
<configuration>
  <configSections>
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="ida:FederationMetadataLocation" value="https://srv:4443/FederationMetadata/2007-06/FederationMetadata.xml" />
    <add key="ida:Realm" value="urn:webapisecurity" />
    <add key="ida:AudienceUri" value="urn:webapisecurity" />
    <add key="AppName" value="Web API Security Sample" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.webServer>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    <modules>
      <add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
      <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
    </modules>
    <validation validateIntegratedModeConfiguration="false" />
  </system.webServer>
  <system.identityModel>
    <identityConfiguration>
      <audienceUris>
        <add value="urn:webapisecurity" />
      </audienceUris>
      <claimsAuthorizationManager type="Thinktecture.IdentityServer.Ofi.AuthorizationManager, Thinktecture.IdentityServer.Ofi, Version=1.0.0.0, Culture=neutral" />
      <claimsAuthenticationManager type="Thinktecture.IdentityServer.Ofi.AuthenticationManager, Thinktecture.IdentityServer.Ofi, Version=1.0.0.0, Culture=neutral" />
      <certificateValidation certificateValidationMode="None" />
      <issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <trustedIssuers>
          <add thumbprint="489116B0FCF14DF66D47AE272C3B9FD867D0E050" />
        </trustedIssuers>
      </issuerNameRegistry>
    </identityConfiguration>
  </system.identityModel>
  <system.identityModel.services>
    <federationConfiguration>
      <cookieHandler requireSsl="false" />
      <wsFederation passiveRedirectEnabled="true" issuer="https://srv:4443/issue/wsfed" realm="urn:webapisecurity" reply="http://localhost:64346/" requireHttps="false" />
    </federationConfiguration>
  </system.identityModel.services>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
</configuration>

有了这个,我可以看到在浏览器中设置了FedAuth cookie,它也在WebApi中进行验证。