我一直在研究一个简单的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。
再次感谢斯蒂芬!
此代码对我有效,基于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 也是这个答案。