.net – 如何获取客户端DotNetOpenAuth.OAuth2返回的错误消息?
我使用ExchangeUserCredentialForToken函数从授权服务器获取令牌。当我的用户存在于我的数据库中时,它的工作正常,但当凭据不正确时,我想向客户端发回一条消息。我使用以下2行代码来设置错误消息:
context.SetError("Autorization Error","The username or password is incorrect!"); context.Rejected(); 但是在客户端,我只得到协议错误(错误400)。你可以帮助我如何在授权服务器上得到服务器端的错误信息? 授权服务器的完整应用配置: using Constants; using Microsoft.Owin; using Microsoft.Owin.Security; using Microsoft.Owin.Security.Cookies; using Microsoft.Owin.Security.Infrastructure; using Microsoft.Owin.Security.OAuth; using Owin; using System; using System.Collections.Concurrent; using System.Linq; using System.Security.Claims; using System.Security.Principal; using System.Threading.Tasks; using AuthorizationServer.Entities; using AuthorizationServer.Entities.Infrastructure.Abstract; using AuthorizationServer.Entities.Infrastructure.Concrete; namespace AuthorizationServer { public partial class Startup { private IEmployeeRepository Repository; public void ConfigureAuth(IAppBuilder app) { //instanciate the repository Repository = new EmployeeRepository(); // Enable Application Sign In Cookie app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = "Application",AuthenticationMode = AuthenticationMode.Passive,LoginPath = new PathString(Paths.LoginPath),LogoutPath = new PathString(Paths.LogoutPath),}); // Enable External Sign In Cookie app.SetDefaultSignInAsAuthenticationType("External"); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = "External",CookieName = CookieAuthenticationDefaults.CookiePrefix + "External",ExpireTimeSpan = TimeSpan.FromMinutes(5),}); // Enable google authentication app.UseGoogleAuthentication(); // Setup Authorization Server app.USEOAuthAuthorizationServer(new OAuthAuthorizationServerOptions { AuthorizeEndpointPath = new PathString(Paths.AuthorizePath),TokenEndpointPath = new PathString(Paths.TokenPath),ApplicationCanDisplayErrors = true,#if DEBUG AllowInsecureHttp = true,#endif // Authorization server provider which controls the lifecycle of Authorization Server Provider = new OAuthAuthorizationServerProvider { OnValidateClientRedirectUri = ValidateClientRedirectUri,OnValidateClientAuthentication = ValidateClientAuthentication,OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,OnGrantClientCredentials = GrantClientCredetails },// Authorization code provider which creates and receives authorization code AuthorizationCodeProvider = new AuthenticationTokenProvider { OnCreate = CreateAuthenticationCode,OnReceive = ReceiveAuthenticationCode,},// Refresh token provider which creates and receives referesh token RefreshTokenProvider = new AuthenticationTokenProvider { OnCreate = CreateRefreshToken,OnReceive = ReceiveRefreshToken,} }); // indicate our intent to use bearer authentication app.USEOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions { AuthenticationType = "Bearer",AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active }); } private Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) { if (context.ClientId == Clients.Client1.Id) { context.Validated(Clients.Client1.RedirectUrl); } else if (context.ClientId == Clients.Client2.Id) { context.Validated(Clients.Client2.RedirectUrl); } return Task.FromResult(0); } private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { string clientname; string clientpassword; if (context.TryGetBasicCredentials(out clientname,out clientpassword) || context.TryGetFormCredentials(out clientname,out clientpassword)) { employee Employee = Repository.GetEmployee(clientname,clientpassword); if (Employee != null) { context.Validated(); } else { context.SetError("Autorization Error","The username or password is incorrect!"); context.Rejected(); } } return Task.FromResult(0); } private Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { var identity = new ClaimsIdentity(new GenericIdentity(context.UserName,OAuthDefaults.AuthenticationType),context.Scope.Select(x => new Claim("urn:oauth:scope",x))); context.Validated(identity); return Task.FromResult(0); } private Task GrantClientCredetails(OAuthGrantClientCredentialsContext context) { var identity = new ClaimsIdentity(new GenericIdentity(context.ClientId,x))); context.Validated(identity); return Task.FromResult(0); } private readonly ConcurrentDictionary<string,string> _authenticationCodes = new ConcurrentDictionary<string,string>(StringComparer.Ordinal); private void CreateAuthenticationCode(AuthenticationTokenCreateContext context) { context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n")); _authenticationCodes[context.Token] = context.SerializeTicket(); } private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context) { string value; if (_authenticationCodes.TryRemove(context.Token,out value)) { context.DeserializeTicket(value); } } private void CreateRefreshToken(AuthenticationTokenCreateContext context) { context.SetToken(context.SerializeTicket()); } private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context) { context.DeserializeTicket(context.Token); } } } 解决方法
这是一个完整的解决方案,使用Jeff的概念与我的原始帖子相结合。
1)在上下文中设置错误消息 如果您在设置错误消息后调用context.Rejected(),则会删除错误消息(请参见下面的示例): context.SetError("Account locked","You have exceeded the total allowed failed logins. Please try back in an hour."); context.Rejected(); 您将要从您的任务中删除context.Rejected()。请注意,被拒绝和SetError方法的定义是: 拒绝:
SETERROR:
再次,通过在设置错误后调用Rejected方法,上下文将被标记为没有错误,错误消息将被删除。 2)设置响应的状态代码:使用Jeff的例子,用一点点旋转。 而不是使用魔术字符串,我将创建一个全局属性来设置状态代码的标签。在静态全局类中,创建一个用于标识状态代码的属性(我使用X-Challenge,但是当然可以使用任何您选择的内容)。这将用于标记响应中添加的标题属性。 public static class ServerGlobalVariables { //Your other properties... public const string OwinChallengeFlag = "X-Challenge"; } 然后在您的OAuthAuthorizationServerProvider的各种任务中,您将添加标签作为响应中的新标头值的键。将HttpStatusCode枚举与全局标志结合使用,您将可以访问所有各种状态代码,并避免使用魔术字符串。 //Set the error message context.SetError("Account locked","You have exceeded the total allowed failed logins. Please try back in an hour."); //Add your flag to the header of the response context.Response.Headers.Add(ServerGlobalVariables.OwinChallengeFlag,new[] { ((int)HttpStatusCode.Unauthorized).ToString() }); 在客户OwinMiddleware中,您可以使用全局变量搜索标题中的标志: //This class handles all the OwinMiddleware responses,so the name should //not just focus on invalid authentication public class CustomAuthenticationMiddleware : OwinMiddleware { public CustomAuthenticationMiddleware(OwinMiddleware next) : base(next) { } public override async Task Invoke(IOwinContext context) { await Next.Invoke(context); if (context.Response.StatusCode == 400 && context.Response.Headers.ContainsKey( ServerGlobalVariables.OwinChallengeFlag)) { var headerValues = context.Response.Headers.GetValues (ServerGlobalVariables.OwinChallengeFlag); context.Response.StatusCode = Convert.ToInt16(headerValues.FirstOrDefault()); context.Response.Headers.Remove( ServerGlobalVariables.OwinChallengeFlag); } } } 最后,正如Jeff指出的那样,您必须在Startup.Configuration或Startup.ConfigureAuth方法中注册此自定义OwinMiddleware: app.Use<CustomAuthenticationMiddleware>(); 使用上述解决方案,您现在可以设置状态代码和自定义错误消息,如下所示: >无效的用户名或密码 3)从ProtocolException提取错误消息 在客户端应用程序中,需要捕获并处理ProtocolException。这样的事情会给你答案: //Need to create a class to deserialize the Json //Create this somewhere in your application public class OAuthErrorMsg { public string error { get; set; } public string error_description { get; set; } public string error_uri { get; set; } } //Need to make sure to include Newtonsoft.Json using Newtonsoft.Json; //Code for your object.... private void login() { try { var state = _webServerClient.ExchangeUserCredentialForToken( this.emailTextBox.Text,this.passwordBox.Password.Trim(),scopes: new string[] { "PublicProfile" }); _accessToken = state.AccessToken; _refreshToken = state.RefreshToken; } catch (ProtocolException ex) { var webException = ex.InnerException as WebException; OAuthErrorMsg error = JsonConvert.DeserializeObject<OAuthErrorMsg>( ExtractResponseString(webException)); var errorMessage = error.error_description; //Now it's up to you how you process the errorMessage } } public static string ExtractResponseString(WebException webException) { if (webException == null || webException.Response == null) return null; var responseStream = webException.Response.GetResponseStream() as MemoryStream; if (responseStream == null) return null; var responseBytes = responseStream.ToArray(); var responseString = Encoding.UTF8.GetString(responseBytes); return responseString; } 我已经测试了这个,它在VS2013 Pro与4.5完美的工作! (请注意,我没有包括所有必需的命名空间或附加代码,因为这将因应用程序而异:WPF,MVC或Winform。另外,我没有讨论错误处理,所以你需要确保在您的解决方案中实施适当的错误处理。) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 如何在ASP.NET中获取服务器端的输入值(类型文本)?
- asp.net – 使用Active Directory的REST API的授权方法
- asp.net-mvc – 在ASP MVC中,如何将新视图和文件返回给用户
- ASP.NET – Ajax Calendar Extender CSS继承自表
- asp.net-mvc – 从ASP.NET MVC操作返回什么来允许jQuery aj
- asp.net-mvc-3 – mvc3 https&http
- ASP.NET MVC3无法访问字体(静态内容)
- asp.net-mvc – ASP.NET MVC:无法在我的控制器中获取字符串
- asp.net mvc客户端验证
- asp.net – 使用UpdatePanel的CollectionPager问题
- Asp.net mvc 知多少(九)
- asp.net-mvc – ViewModel应该位于ASP.NET MVC应
- asp.net – 如何从异常中获取更多细节?
- asp.net – Oracle.ManagedDataAccess:TNS:无法
- asp.net – MVC会话到期 – 继续左边
- 如何在ASP.NET MVC 5.2.3应用程序的其他地方获取
- asp.net – Visual Studio在Aspx文件中缺少扩展/
- 令人敬畏的ASP.NET和C#教程初学者
- asp.net – Web.Config中的Assemblies节点的目的
- asp.net-mvc – asp.net mvc 4 – 好的是每个线程