2017-05-01 178 views
0

我有Web API和在不同解决方案中调用Web API的客户端。我需要使用本地身份验证和现有用户数据对用户进行身份验证,并使用Google和Facebook等外部身份验证进行身份验证。外部身份验证ASP.NET Web API

本地身份验证正常工作。但在验证外部登录时,我在调用api/Account/UserInfo时收到未经授权的响应。请注意,我可以正确地从Google帐户生成代币。

这是我的AccountController.cs文件。

[Authorize] 
[RoutePrefix("api/Account")] 
[EnableCors("*","*","*")] 
public class AccountController : ApiController 
{ 
    private const string LocalLoginProvider = "Local"; 
    private ApplicationUserManager _userManager; 

    public AccountController() 
    { 
    } 

    public AccountController(ApplicationUserManager userManager, 
     ISecureDataFormat<AuthenticationTicket> accessTokenFormat) 
    { 
     UserManager = userManager; 
     AccessTokenFormat = accessTokenFormat; 
    } 

    public ApplicationUserManager UserManager 
    { 
     get 
     { 
      return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>(); 
     } 
     private set 
     { 
      _userManager = value; 
     } 
    } 

    public ISecureDataFormat<AuthenticationTicket> AccessTokenFormat { get; private set; } 

    // GET api/Account/UserInfo 
    [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] 
    //[AllowAnonymous] 
    [Route("UserInfo")] 
    public UserInfoViewModel GetUserInfo() 
    { 
     ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity); 

     return new UserInfoViewModel 
     { 
      Email = User.Identity.GetUserName(), 
      HasRegistered = externalLogin == null, 
      LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null 
     }; 
    } 

    // POST api/Account/Logout 
    [Route("Logout")] 
    public IHttpActionResult Logout() 
    { 
     Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType); 
     return Ok(); 
    } 

    // GET api/Account/ManageInfo?returnUrl=%2F&generateState=true 
    [Route("ManageInfo")] 
    public async Task<ManageInfoViewModel> GetManageInfo(string returnUrl, bool generateState = false) 
    { 
     IdentityUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); 

     if (user == null) 
     { 
      return null; 
     } 

     List<UserLoginInfoViewModel> logins = new List<UserLoginInfoViewModel>(); 

     foreach (IdentityUserLogin linkedAccount in user.Logins) 
     { 
      logins.Add(new UserLoginInfoViewModel 
      { 
       LoginProvider = linkedAccount.LoginProvider, 
       ProviderKey = linkedAccount.ProviderKey 
      }); 
     } 

     if (user.PasswordHash != null) 
     { 
      logins.Add(new UserLoginInfoViewModel 
      { 
       LoginProvider = LocalLoginProvider, 
       ProviderKey = user.UserName, 
      }); 
     } 

     return new ManageInfoViewModel 
     { 
      LocalLoginProvider = LocalLoginProvider, 
      Email = user.UserName, 
      Logins = logins, 
      ExternalLoginProviders = GetExternalLogins(returnUrl, generateState) 
     }; 
    } 

    // POST api/Account/ChangePassword 
    [Route("ChangePassword")] 
    public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, 
      model.NewPassword); 

     if (!result.Succeeded) 
     { 
      return GetErrorResult(result); 
     } 

     return Ok(); 
    } 

    // POST api/Account/SetPassword 
    [Route("SetPassword")] 
    public async Task<IHttpActionResult> SetPassword(SetPasswordBindingModel model) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword); 

     if (!result.Succeeded) 
     { 
      return GetErrorResult(result); 
     } 

     return Ok(); 
    } 

    // POST api/Account/AddExternalLogin 
    [Route("AddExternalLogin")] 
    public async Task<IHttpActionResult> AddExternalLogin(AddExternalLoginBindingModel model) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie); 

     AuthenticationTicket ticket = AccessTokenFormat.Unprotect(model.ExternalAccessToken); 

     if (ticket == null || ticket.Identity == null || (ticket.Properties != null 
      && ticket.Properties.ExpiresUtc.HasValue 
      && ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow)) 
     { 
      return BadRequest("External login failure."); 
     } 

     ExternalLoginData externalData = ExternalLoginData.FromIdentity(ticket.Identity); 

     if (externalData == null) 
     { 
      return BadRequest("The external login is already associated with an account."); 
     } 

     IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), 
      new UserLoginInfo(externalData.LoginProvider, externalData.ProviderKey)); 

     if (!result.Succeeded) 
     { 
      return GetErrorResult(result); 
     } 

     return Ok(); 
    } 

    // POST api/Account/RemoveLogin 
    [Route("RemoveLogin")] 
    public async Task<IHttpActionResult> RemoveLogin(RemoveLoginBindingModel model) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     IdentityResult result; 

     if (model.LoginProvider == LocalLoginProvider) 
     { 
      result = await UserManager.RemovePasswordAsync(User.Identity.GetUserId()); 
     } 
     else 
     { 
      result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), 
       new UserLoginInfo(model.LoginProvider, model.ProviderKey)); 
     } 

     if (!result.Succeeded) 
     { 
      return GetErrorResult(result); 
     } 

     return Ok(); 
    } 

    // GET api/Account/ExternalLogin 
    [OverrideAuthentication] 
    [HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)] 
    [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] 
    [AllowAnonymous] 
    [Route("ExternalLogin", Name = "ExternalLogin")] 
    public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null) 
    { 
     if (error != null) 
     { 
      return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error)); 
     } 

     if (!User.Identity.IsAuthenticated) 
     { 
      return new ChallengeResult(provider, this); 
     } 

     ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity); 

     if (externalLogin == null) 
     { 
      return InternalServerError(); 
     } 

     if (externalLogin.LoginProvider != provider) 
     { 
      Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie); 
      return new ChallengeResult(provider, this); 
     } 

     ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider, 
      externalLogin.ProviderKey)); 

     bool hasRegistered = user != null; 

     if (hasRegistered) 
     { 
      Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie); 

      ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager, 
       OAuthDefaults.AuthenticationType); 
      ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager, 
       CookieAuthenticationDefaults.AuthenticationType); 

      AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName); 
      Authentication.SignIn(properties, oAuthIdentity, cookieIdentity); 
     } 
     else 
     { 
      IEnumerable<Claim> claims = externalLogin.GetClaims(); 
      ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType); 
      Authentication.SignIn(identity); 
     } 

     return Ok(); 
    } 

    // GET api/Account/ExternalLogins?returnUrl=%2F&generateState=true 
    [AllowAnonymous] 
    [Route("ExternalLogins")] 
    public IEnumerable<ExternalLoginViewModel> GetExternalLogins(string returnUrl, bool generateState = false) 
    { 
     IEnumerable<AuthenticationDescription> descriptions = Authentication.GetExternalAuthenticationTypes(); 
     List<ExternalLoginViewModel> logins = new List<ExternalLoginViewModel>(); 

     string state; 

     if (generateState) 
     { 
      const int strengthInBits = 256; 
      state = RandomOAuthStateGenerator.Generate(strengthInBits); 
     } 
     else 
     { 
      state = null; 
     } 

     foreach (AuthenticationDescription description in descriptions) 
     { 
      ExternalLoginViewModel login = new ExternalLoginViewModel 
      { 
       Name = description.Caption, 
       Url = Url.Route("ExternalLogin", new 
       { 
        provider = description.AuthenticationType, 
        response_type = "token", 
        client_id = Startup.PublicClientId, 
        redirect_uri = new Uri(Request.RequestUri, returnUrl).AbsoluteUri, 
        state = state 
       }), 
       State = state 
      }; 
      logins.Add(login); 
     } 

     return logins; 
    } 

    // POST api/Account/Register 
    [AllowAnonymous] 
    [Route("Register")] 
    public async Task<IHttpActionResult> Register(RegisterBindingModel model) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     var user = new ApplicationUser() 
     { 
      UserName = model.Email, 
      Email = model.Email, 
      FirstName = model.FirstName, 
      LastName = model.LastName, 
      CellNumber = model.CellNumber, 
      CompanyName = model.CompanyName, 
      Address = model.Address, 
      City = model.City, 
      Country = model.Country, 
      State = model.State, 
      Zip = model.Zip, 
      EmailConfirmed = true 
     }; 

     IdentityResult result = await UserManager.CreateAsync(user, model.Password); 

     if (!result.Succeeded) 
     { 
      return GetErrorResult(result); 
     } 

     return Ok("User Registered Successfully!"); 
    } 

    // POST api/Account/RegisterExternal 
    [OverrideAuthentication] 
    //[AllowAnonymous] 
    [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] 
    [Route("RegisterExternal")] 
    public async Task<IHttpActionResult> RegisterExternal() 
    { 
     var info = await Authentication.GetExternalLoginInfoAsync(); 
     if (info == null) 
     { 
      return InternalServerError(); 
     } 

     var user = new ApplicationUser() { UserName = info.Email, Email = info.Email }; 

     IdentityResult result = await UserManager.CreateAsync(user); 
     if (!result.Succeeded) 
     { 
      return GetErrorResult(result); 
     } 

     result = await UserManager.AddLoginAsync(user.Id, info.Login); 
     if (!result.Succeeded) 
     { 
      return GetErrorResult(result); 
     } 
     return Ok(); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing && _userManager != null) 
     { 
      _userManager.Dispose(); 
      _userManager = null; 
     } 

     base.Dispose(disposing); 
    } 

    #region Helpers 

    private IAuthenticationManager Authentication 
    { 
     get { return Request.GetOwinContext().Authentication; } 
    } 

    private IHttpActionResult GetErrorResult(IdentityResult result) 
    { 
     if (result == null) 
     { 
      return InternalServerError(); 
     } 

     if (!result.Succeeded) 
     { 
      if (result.Errors != null) 
      { 
       foreach (string error in result.Errors) 
       { 
        ModelState.AddModelError("", error); 
       } 
      } 

      if (ModelState.IsValid) 
      { 
       // No ModelState errors are available to send, so just return an empty BadRequest. 
       return BadRequest(); 
      } 

      return BadRequest(ModelState); 
     } 

     return null; 
    } 

    private class ExternalLoginData 
    { 
     public string LoginProvider { get; set; } 
     public string ProviderKey { get; set; } 
     public string UserName { get; set; } 

     public IList<Claim> GetClaims() 
     { 
      IList<Claim> claims = new List<Claim>(); 
      claims.Add(new Claim(ClaimTypes.NameIdentifier, ProviderKey, null, LoginProvider)); 

      if (UserName != null) 
      { 
       claims.Add(new Claim(ClaimTypes.Name, UserName, null, LoginProvider)); 
      } 

      return claims; 
     } 

     public static ExternalLoginData FromIdentity(ClaimsIdentity identity) 
     { 
      if (identity == null) 
      { 
       return null; 
      } 

      Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier); 

      if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer) 
       || String.IsNullOrEmpty(providerKeyClaim.Value)) 
      { 
       return null; 
      } 

      if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer) 
      { 
       return null; 
      } 

      return new ExternalLoginData 
      { 
       LoginProvider = providerKeyClaim.Issuer, 
       ProviderKey = providerKeyClaim.Value, 
       UserName = identity.FindFirstValue(ClaimTypes.Name) 
      }; 
     } 
    } 

    private static class RandomOAuthStateGenerator 
    { 
     private static RandomNumberGenerator _random = new RNGCryptoServiceProvider(); 

     public static string Generate(int strengthInBits) 
     { 
      const int bitsPerByte = 8; 

      if (strengthInBits % bitsPerByte != 0) 
      { 
       throw new ArgumentException("strengthInBits must be evenly divisible by 8.", "strengthInBits"); 
      } 

      int strengthInBytes = strengthInBits/bitsPerByte; 

      byte[] data = new byte[strengthInBytes]; 
      _random.GetBytes(data); 
      return HttpServerUtility.UrlTokenEncode(data); 
     } 
    } 

    #endregion 
} 

下面是StartUp.Auth.cs的ConfigureAuth方法文件

 public void ConfigureAuth(IAppBuilder app) 
    { 
     // Configure the db context and user manager to use a single instance per request 
     app.CreatePerOwinContext(ApplicationDbContext.Create); 
     app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); 

     // Enable the application to use a cookie to store information for the signed in user 
     // and to use a cookie to temporarily store information about a user logging in with a third party login provider 
     app.UseCookieAuthentication(new CookieAuthenticationOptions()); 
     app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); 
     app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); 
     // Configure the application for OAuth based flow 
     PublicClientId = "self"; 
     OAuthOptions = new OAuthAuthorizationServerOptions 
     { 
      TokenEndpointPath = new PathString("/Token"), 
      Provider = new ApplicationOAuthProvider(PublicClientId), 
      AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"), 
      AccessTokenExpireTimeSpan = TimeSpan.FromDays(14), 
      // In production mode set AllowInsecureHttp = false 
      AllowInsecureHttp = true 
     }; 

     // Enable the application to use bearer tokens to authenticate users 
     app.UseOAuthBearerTokens(OAuthOptions); 

     // Uncomment the following lines to enable logging in with third party login providers 
     //app.UseMicrosoftAccountAuthentication(
     // clientId: "", 
     // clientSecret: ""); 

     //app.UseTwitterAuthentication(
     // consumerKey: "", 
     // consumerSecret: ""); 
     var facebookoptions = new FacebookAuthenticationOptions() 
     { 
      AppId = "", 
      AppSecret = "", 
      BackchannelHttpHandler = new FaceBookBackChannelHandler(), 
      UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=email,first_name,last_name,gender" 
     }; 
     facebookoptions.Scope.Add("email"); 
     app.UseFacebookAuthentication(facebookoptions); 

     //app.UseFacebookAuthentication(
     // appId: "", 
     // appSecret: ""); 

     app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions() 
     { 
      ClientId = "", 
      ClientSecret = "" 
     }); 
    } 

而且这里的jQuery方法调用的API端点

function getAccessToken() { 
if (location.hash) { 
    if (location.hash.split('access_token=')) { 
     var accessToken = location.hash.split('access_token=')[1].split('&')[0]; 
     if (accessToken) { 
      isUserRegistered(accessToken); 
     } 
    } 
} 

}

 function isUserRegistered(accessToken) { 
$.ajax({ 
    url: 'http://localhostXXXX/api/Account/UserInfo', 
    method: 'GET', 
    cors: 
     { 
      headers: { 
       'content-type': 'application/JSON', 
       'Authorization': 'Bearer ' + accessToken 
      } 
     }, 
    success: function (response) { 
     if (response.HasRegistered) { 
      localStorage.setItem('accessToken', accessToken); 
      localStorage.setItem('userName', response.Email); 
      window.location.href = "Data.aspx"; 
     } 
     else { 
      signupExternalUser(accessToken); 
     } 
    } 
}); 

}

 function signupExternalUser(accessToken) { 
$.ajax({ 
    url: 'http://localhost:XXXXX/api/Account/RegisterExternal', 
    method: 'POST', 
    cors: 
     { 
      headers: { 
       'content-type': 'application/json', 
       'Authorization': 'Bearer ' + accessToken 
      } 
     }, 
    success: function() { 
     window.location.href = ""; 
    } 
}); 

}

回答

1

这里是我用得到我的信息的代码。它的工作很好:

 // check if authorized 
     if (Request["code"] == null) 
     { 
      Response.Redirect(string.Format(
       "https://graph.facebook.com/oauth/authorize?client_id={0}&redirect_uri={1}&scope={2}", 
       app_id, Request.Url.AbsoluteUri, scope)); 
     } 
     else 
     { 
      FacebookAccessToken token = new FacebookAccessToken(); 
      FacebookUser user = new FacebookUser(); 

      //Requesting for access token 
      string url = string.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&redirect_uri={1}&scope={2}&code={3}&client_secret={4}", 
       app_id, Request.Url.AbsoluteUri, scope, Request["code"].ToString(), app_secret); 

      HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 

      using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) 
      { 
       StreamReader reader = new StreamReader(response.GetResponseStream()); 

       string vals = reader.ReadToEnd(); 
       // Deserialize json object 
       token = JsonConvert.DeserializeObject<FacebookAccessToken>(data); 
      } 

      //Getting user info 
      url = string.Format("https://graph.facebook.com/v2.8/me?access_token={0}", token.AccessToken); 
      request = WebRequest.Create(url) as HttpWebRequest; 
      using (var client = request.GetResponse() as HttpWebResponse) 
      { 
       StreamReader reader = new StreamReader(client.GetResponseStream()); 
       string data = reader.ReadToEnd(); 
       // data:"{\"name\":\"Er Vatsal D Patel\",\"id\":\"13168723650*****\"}"; 
       user = JsonConvert.DeserializeObject<FacebookUser>(data); 

      } 

     return "done"; 
    } 
+0

再次感谢您,我需要谷歌和脸书登录,以及本地web api登录以及。如果任何控制器中的任何方法被调用,我还需要知道哪个用户登录。您共享的Facebook实施是好的。您能否帮我使用Google和本地身份验证?当我通过网络从远程计算机访问托管API时,我收到了答复:无效的授权类型。请帮忙! –

+0

如果您有可以请通过github分享您的代码。正如我之前所说的,我为web api和客户端提供了独立的解决方案,它调用api –

+0

检查您是否通过了有效的授权类型。并使用谷歌外部认证,更喜欢以下链接:https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/external-authentication-services#FACEBOOK –