一尘不染

使用Ajax访问ServiceStack身份验证服务

ajax

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

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

我的理解上的分解是如何对所有ajax调用实施身份验证。我的初始登录如下所示。无论我做什么,尝试通过ajax调用身份验证的服务,或者收到OPTIONS404错误或Not Found错误,或者Access-Control-Allow不允许Origin http // localhost:12345(伪链接)
-原产地等

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

    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);
}

我的简单Hello服务

[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。

再次感谢斯蒂芬!


阅读 212

收藏
2020-07-26

共1个答案

一尘不染

此代码对我有效,基于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





             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);
              }
          });
      }

这些问题在这里
这里都是有帮助的。

如果我们可以使用cookie和session,那么对于CredentialsAuthProvider
也是这个答案

2020-07-26