2

我一直在研究一个简单的 API 示例,它是带有身份验证的 ServiceStack Hello World 示例的修改版本。概念证明的目标是创建一个 RESTful API,其中包含需要身份验证的服务,这些服务完全可以通过 Ajax 从几个不同的 Web 项目中访问。

我已经阅读了 wiki 并实现了身份验证和授权以及实现 CORS(许多结果 [对不起,没有足够的信任指向相关链接])。此时,我的 Hello 服务可以使用覆盖 CredentialsAuthProvider 和自定义用户会话对象的自定义身份验证机制进行身份验证。我创建或借用了一个简单的测试应用程序(一个完全独立的项目来模拟我们的需求),可以进行身份​​验证,然后调用 Hello 服务,传递一个名称,并通过一个单一的接收“Hello Fred”响应浏览器会话。也就是说,我可以调用 url 中的 /auth/credentials 路径,传递用户名和 id,并收到正确的响应。然后我可以将 url 更新为 /hello/fred 并接收有效响应。

我的理解失败是如何为所有 ajax 调用实现身份验证。我在下面的初始登录工作正常。无论我做什么,我尝试通过 ajax 调用经过身份验证的服务,我要么收到 OPTIONS 404 错误或 Not Found 错误,要么 Origin http // localhost:12345 (pseudo-link) is not allowed by Access-Control-Allow -产地等

我需要走这条路吗?

对不起,如果这令人困惑。如果需要,我可以提供更多详细信息,但认为这可能足以帮助知识渊博的人帮助我缺乏理解。

    function InvokeLogin() {
    var Basic = new Object();
    Basic.UserName = "MyUser";
   Basic.password = "MyPass";

    $.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        data: JSON.stringify(Basic),
        url: "http://localhost:58795/auth/credentials",
        success: function (data, textStatus, jqXHR) {
                alert('Authenticated! Now you can run Hello Service.');
            },
        error: function(xhr, textStatus, errorThrown) {
            var data = $.parseJSON(xhr.responseText);
            if (data === null)
                alert(textStatus + " HttpCode:" + xhr.status);
            else
                alert("ERROR: " + data.ResponseStatus.Message + (data.ResponseStatus.StackTrace ? " \r\n Stack:" + data.ResponseStatus.StackTrace : ""));
        }
    });
}

编辑:

根据 Stefan 提供的回复和链接,我做了一些更改:

我的配置 (注意:我正在使用自定义身份验证和会话对象,并且一切正常。)

public override void Configure(Funq.Container container)
{
    Plugins.Add(new AuthFeature(() => new CustomUserSession(), 
                new IAuthProvider[] {
                new CustomCredentialsAuthProvider(),
                    }));

    base.SetConfig(new EndpointHostConfig
    {
        GlobalResponseHeaders = {
            { "Access-Control-Allow-Origin", "*" },
            { "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" },
            { "Access-Control-Allow-Headers", "Content-Type, Authorization" },
        },
        DefaultContentType = "application/json"
    });

    Plugins.Add(new CorsFeature());
    this.RequestFilters.Add((httpReq, httpRes, requestDto) =>
    {
        //Handles Request and closes Responses after emitting global HTTP Headers
        if (httpReq.HttpMethod == "OPTIONS")
            httpRes.EndRequest();   //   extension method
    });

    Routes
        .Add<Hello>("/Hello", "GET, OPTIONS");


    container.Register<ICacheClient>(new MemoryCacheClient());
    var userRep = new InMemoryAuthRepository();
    container.Register<IUserAuthRepository>(userRep);
}

我的简单问候服务

[EnableCors]
public class HelloService : IService
{
    [Authenticate]
    public object GET(Hello request)
    {
        Looks strange when the name is null so we replace with a generic name.
        var name = request.Name ?? "John Doe";
        return new HelloResponse { Result = "Hello, " + name };
    }
}

在上面进行登录调用之后,我随后调用 Hello 服务现在产生 401 错误,这是进度,尽管不是我需要的。( Jquery.support.cors= true 在我的脚本文件中设置。)

function helloService() {
    $.ajax({
        type: "GET",
        contentType: "application/json",
        dataType: "json",
        url: "http://localhost:58795/hello",
        success: function (data, textStatus, jqXHR) {
            alert(data.Result);
        },
        error: function (xhr, textStatus, errorThrown) {
            var data = $.parseJSON(xhr.responseText);
            if (data === null)
                alert(textStatus + " HttpCode:" + xhr.status);
            else
                alert("ERROR: " + data.ResponseStatus.Message +
                    (data.ResponseStatus.StackTrace ? " \r\n Stack:" + data.ResponseStatus.StackTrace : ""));
        }
    });
}

同样,如果我首先正确调用 /auth/credentials,然后再调用 /hello,这在 RESTConsole 中有效。

最终编辑 在 Stefan 的建议下,包括许多其他链接,我终于能够得到这个工作。除了 Stefan 的代码之外,我还必须进行一项额外的修改:

Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization"));

迎接下一个挑战:更新 Jonas Eriksson 的 CustomAuthenticateAttibute 代码(似乎使用的是旧版本的 ServiceStack,因为一些功能不再可用。

再次感谢斯特凡!!

4

1 回答 1

2

此代码适用于我,基于 Wiki 文档自定义身份验证和授权

代码也基于 ServiceStack 上社区资源 CORS BasicAuth 的博客文章,带有自定义身份验证

对于基本身份验证,自定义提供程序

   public class myAuthProvider : BasicAuthProvider
    {
           public myAuthProvider() : base() { }

       public override bool TryAuthenticate(IServiceBase authService, string userName, string  password)
    {
        //Add here your custom auth logic (database calls etc)
        //Return true if credentials are valid, otherwise false
        if (userName == "admin" && password == "test")
                      return true;
         else
               return false;

    }

    public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
    {
        //Fill the IAuthSession with data which you want to retrieve in the app 
       //  the base AuthUserSession properties e.g
       session.FirstName = "It's me";
        //...   
       //  derived CustomUserSession properties e.g
        if(session is CustomUserSession)
       ((CustomUserSession) session).MyData = "It's me";
        //...
        //Important: You need to save the session!
        authService.SaveSession(session, SessionExpiry);
    }
}

public class CustomUserSession : AuthUserSession
{

    public string MyData { get; set; }
}

在 AppHost 中

     using System.Web;
     using ServiceStack;     // v.3.9.60  httpExtensions methods, before in ServiceStack.WebHost.Endpoints.Extensions;

     using ....

AppHost.Configure

     public override void Configure(Container container)
      {
          SetConfig(new ServiceStack.WebHost.Endpoints.EndpointHostConfig
          {
               DefaultContentType = ContentType.Json 
                 ..
               //   remove GlobalResponseHeaders  because CordFeature adds the CORS headers to  Config.GlobalResponseHeaders

            }); 
       Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization")); //Registers global CORS Headers
        this.RequestFilters.Add((httpReq, httpRes, requestDto) =>
        {
            if (httpReq.HttpMethod == "OPTIONS")
                  httpRes.EndRequestWithNoContent();   // v 3.9.60 httpExtensions method before httpRes.EndServiceStackRequest();  

        });

          //Register all Authentication methods you want to enable for this web app.
            Plugins.Add(new AuthFeature(() => new  CustomUserSession(), // OR the AuthUserSession 
                new IAuthProvider[] {
                new  myAuthProvider(),   
              }) { HtmlRedirect = null }); //  Redirect on fail 

Html重定向答案

             Routes.Add<TestRequest>("/TestAPI/{Id}", "POST,GET, OPTIONS");
        ....
      } 

服务中

           [Authenticate]
          public class TestAPI : Service
            {    
                 ...
            }

在javascript中

     jQuery.support.cors = true;

       function make_base_auth(user, password) {
          var tok = user + ':' + password;
          var hash = btoa(tok);
          return "Basic " + hash;
      }

先登录

           function Authenticate() {

              $.ajax({
              type: 'Post',
              contentType: 'application/json',
              url: serverIP + 'Auth',
              cache: false,
              async: false,
              data: {},
              dataType: "json",
              beforeSend: function (xhr) {
                  xhr.setRequestHeader("Authorization", make_base_auth(username, password));
              },
              success: function (response, status, xhr) {
                  localStorage.sessionId = data.SessionId;
                  var UserName  = response.userName;
              },
              error: function (xhr, err) {
                  alert(err);
              }
          });
      }  

并要求

         function DoTest() {
              var TestRequest = new Object();
              TestRequest.name = "Harry Potter";             
              TestRequest.Id = 33;
             var username = "admin";
             var password = "test"; 

             $.ajax({
              type: 'Post',
              contentType: 'application/json',
              cache: false,
              async: false,
              url: serverIP + '/TestAPI/'+ TestRequest.Id,
              data: JSON.stringify(TestRequest),
              dataType: "json",                  
              beforeSend: function (xhr) {                    
                xhr.setRequestHeader("Session-Id", localStorage.sessionId);
              },
           success: function (response, status, xhr) {
                  var s= response.message;      
              },
              error: function (xhr, err) {
                  alert(xhr.statusText);
              }
          });
      }

这些问题herehere很有帮助。

这也是 CredentialsAuthProvider 的答案,以防我们可以使用 cookie 和session

于 2013-09-25T13:54:18.150 回答