【问题标题】:I want web (MVC) and API on same database我希望 Web (MVC) 和 API 在同一个数据库上
【发布时间】:2017-02-01 16:20:48
【问题描述】:

我有一个数据库,需要用于管理和管理(交易数量、计费和其他管理)的 Web 界面,并需要“手动”从数据库(产品)中提供数据,以及通过 API 将数据(产品)提供给其他更大的客户.显然,所有这些都由 SSL 和 https 保护。

我制作了一个 asp.net MVC 5 应用程序(业务逻辑和管理),并希望实现 API(API 中的菜鸟)来向用户提供数据。

不知道如何实现从 MVC 到 API(同一个数据库)的安全性。

应用程序很小,我可以重写它。我正在考虑尝试使用核心,但担心我会遇到同样的问题。

具体问题:我应该采取什么方法以及在 MVC 5 代或 .core (MVC 6) 中应该采用什么方法才能将一个数据库用于数据、用户及其授权?

(推动一切真正的 API 是我想要避免的)

【问题讨论】:

  • 您当前是否在现有的 MVC 5 应用程序中使用 ASP.Net Identity?
  • 这里是示例链接,参考ASP.Net site
  • 我正在使用 stock identit 2.2 的稍微扩展的实施。用于登录的用户 ID 和用户名的 int,否则为库存解决方案
  • 您在回答中的开头是正确的,您甚至可以将其拆分为微服务,1 个用于交易,1 个用于计费等。这是一个非常常见的解决方案,您最终构建的不是您自己的问题的答案 & 是一个非常过时的解决方案,我敦促您深入了解微服务和容器世界,它美丽、快速且可靠。 github.com/dotnet-architecture/eShopOnContainers

标签: asp.net asp.net-mvc asp.net-web-api web-applications asp.net-identity-2


【解决方案1】:

好的,我的项目完成了。我在 MVC 5 上取得了进展。

(我向你们完美主义者道歉,但我现在没有时间删除不必要的东西,所以我把整个文件原样转储了:)

第一种方法 - 放弃

首先我尝试按照互联网的建议进行设计:.MVC 解决方案、.DB 用于数据库和 .API 解决方案。

结论 - 身份验证和实体框架存在很多问题。最后我放弃了这种方法

第二个成功的方法

只有一个解决方案 .MVC

True NuGet 安装了 .net Api,使用了几个教程扩展的集成授权(不是一个单独的工作)。请注意,我使用ASP.NET Identity 2.0 Extending Identity Models and Using Integer Keys Instead of StringsImplementing HTTPS Everywhere in ASP.Net MVC application

这里是修改和插件:

App_Start -> IdentityConfig.cs

public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
    // *** ADD INT TYPE ARGUMENT TO CONSTRUCTOR CALL:
    public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
        : base(store)
    {
    }

    public static ApplicationUserManager Create(
        IdentityFactoryOptions<ApplicationUserManager> options,
        IOwinContext context)
    {
        // *** PASS CUSTOM APPLICATION USER STORE AS CONSTRUCTOR ARGUMENT:
        var manager = new ApplicationUserManager(
            new ApplicationUserStore(context.Get<ApplicationDbContext>()));

        // Configure validation logic for usernames

        // *** ADD INT TYPE ARGUMENT TO METHOD CALL:
        manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };

        // Configure validation logic for passwords
        manager.PasswordValidator = new PasswordValidator
        {
            RequiredLength = 6,
            RequireNonLetterOrDigit = false,
            RequireDigit = true,
            RequireLowercase = true,
            RequireUppercase = true,
        };


            // other code removed for brevity      
        manager.UserLockoutEnabledByDefault = Convert.ToBoolean(ConfigurationManager.AppSettings["UserLockoutEnabledByDefault"].ToString());
        manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(Double.Parse(ConfigurationManager.AppSettings["DefaultAccountLockoutTimeSpan"].ToString()));
        manager.MaxFailedAccessAttemptsBeforeLockout = Convert.ToInt32(ConfigurationManager.AppSettings["MaxFailedAccessAttemptsBeforeLockout"].ToString());


        // Register two factor authentication providers. 
        // This application uses Phone and Emails as a step of receiving a 
        // code for verifying the user You can write your own provider and plug in here.

        // *** ADD INT TYPE ARGUMENT TO METHOD CALL:
        //manager.RegisterTwoFactorProvider("PhoneCode",
        //  new PhoneNumberTokenProvider<ApplicationUser, int>
        //  {
        //      MessageFormat = "Your security code is: {0}"
        //  });

        //// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
        //manager.RegisterTwoFactorProvider("EmailCode",
        //  new EmailTokenProvider<ApplicationUser, int>
        //  {
        //      Subject = "SecurityCode",
        //      BodyFormat = "Your security code is {0}"
        //  });

        //manager.EmailService = new EmailService();
        //manager.SmsService = new SmsService();
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        {
            // *** ADD INT TYPE ARGUMENT TO METHOD CALL:
            manager.UserTokenProvider =
                new DataProtectorTokenProvider<ApplicationUser, int>(
                    dataProtectionProvider.Create("ASP.NET Identity"));
        }
        return manager;
    }
}


// PASS CUSTOM APPLICATION ROLE AND INT AS TYPE ARGUMENTS TO BASE:
public class ApplicationRoleManager : RoleManager<ApplicationRole, int>
{
    // PASS CUSTOM APPLICATION ROLE AND INT AS TYPE ARGUMENTS TO CONSTRUCTOR:
    public ApplicationRoleManager(IRoleStore<ApplicationRole, int> roleStore)
        : base(roleStore)
    {
    }

    // PASS CUSTOM APPLICATION ROLE AS TYPE ARGUMENT:
    public static ApplicationRoleManager Create(
        IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
    {
        return new ApplicationRoleManager(
            new ApplicationRoleStore(context.Get<ApplicationDbContext>()));
    }
}


public class EmailService : IIdentityMessageService
{
    public Task SendAsync(IdentityMessage message)
    {
        // Plug in your email service here to send an email.
        return Task.FromResult(0);
    }
}


public class SmsService : IIdentityMessageService
{
    public Task SendAsync(IdentityMessage message)
    {
        // Plug in your sms service here to send a text message.
        return Task.FromResult(0);
    }
}

//This is useful if you do not want to tear down the database each time you run the application.
//public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
//This example shows you how to create a new database if the Model changes
public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>
{
    protected override void Seed(ApplicationDbContext context)
    {
        //InitializeIdentityForEF(context); //- Do not Seed - IGOR
        //base.Seed(context);
    }

    //Create User=Admin@Admin.com with password=Admin@123456 in the Admin role        
    //public static void InitializeIdentityForEF(ApplicationDbContext db)
    //{
    //  var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
    //  var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
    //  const string name = "igor@email.mail";
    //  const string password = "LolLol1";
    //  const string roleName = "lol";

    //  //Create Role Admin if it does not exist
    //  var role = roleManager.FindByName(roleName);
    //  if (role == null)
    //  {
    //      role = new ApplicationRole(roleName);
    //      var roleresult = roleManager.Create(role);
    //  }

    //  var user = userManager.FindByName(name);
    //  if (user == null)
    //  {
    //      user = new ApplicationUser { UserName = name, Email = name };
    //      var result = userManager.Create(user, password);
    //      result = userManager.SetLockoutEnabled(user.Id, false);
    //  }

    //  // Add user admin to Role Admin if not already added
    //  var rolesForUser = userManager.GetRoles(user.Id);
    //  if (!rolesForUser.Contains(role.Name))
    //  {
    //      var result = userManager.AddToRole(user.Id, role.Name);
    //  }
    //}
}


public class ApplicationSignInManager : SignInManager<ApplicationUser, int>
{
    public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager) :
        base(userManager, authenticationManager)
    { }

    public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
    {
        return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
    }

    public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
    {
        return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
    }
}

App_Start -> Startup.Auth.cs

public partial class Startup
{
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

    public static string PublicClientId { get; private set; }


    public void ConfigureAuth(IAppBuilder app)
    {
        // Configure the db context, user manager and role manager to use a single instance per request
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.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
        // Configure the sign in cookie
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                // Enables the application to validate the security stamp when the user logs in.
                // This is a security feature which is used when you change a password or add an external login to your account.  
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
                    validateInterval: TimeSpan.FromMinutes(2880),
                    regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
                        // Need to add THIS line because we added the third type argument (int) above:
                        getUserIdCallback: (claim) => int.Parse(claim.GetUserId()))
            }
        });
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
        app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));

        // Enables the application to remember the second login verification factor such as phone or email.
        // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
        // This is similar to the RememberMe option when you log in.
        app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

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

        //app.UseTwitterAuthentication(
        //   consumerKey: "",
        //   consumerSecret: "");

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

        //app.UseGoogleAuthentication(
        //    clientId: "",
        //    clientSecret: "");

        // 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"), //TODO - makni ovo
            AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
            // In production mode set AllowInsecureHttp = false
            AllowInsecureHttp = true
        };

        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptions);
    }
}
//public partial class Startup
//{
//  public void ConfigureAuth(IAppBuilder app)
//  {
//      // Enable the application to use a cookie to store information for the signed in user
//      app.UseCookieAuthentication(new CookieAuthenticationOptions
//      {
//          ExpireTimeSpan = TimeSpan.FromHours(24),
//          CookieSecure = CookieSecureOption.Never,
//          CookieHttpOnly = false,
//          SlidingExpiration = true,
//          AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
//          LoginPath = new PathString("/Account/Login")
//      });
//      // Use a cookie to temporarily store information about a user logging in with a third party login provider
//      app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
//  }
//}

App_Start -> WebApiConfig.cs

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // TODO: Add any additional configuration code.

        // Web API routes
        config.MapHttpAttributeRoutes();

        //config.Routes.MapHttpRoute(
        //  name: "getkey",
        //  routeTemplate: "api/ApiKeys/Get/{term}"

        //);

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        // WebAPI when dealing with JSON & JavaScript!
        // Setup json serialization to serialize classes to camel (std. Json format)
        var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
        formatter.SerializerSettings.ContractResolver =
            new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();

        // make all web-api requests to be sent over https
        config.MessageHandlers.Add(new EnforceHttpsHandler());
    }
}

我用于初始插入和编辑角色和初始用户的 MySysAdmin 控制器。

public SysAdminController(ApplicationUserManager userManager,
        ApplicationRoleManager roleManager)
    {
        UserManager = userManager;
        RoleManager = roleManager;
    }

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

    private ApplicationRoleManager _roleManager;
    public ApplicationRoleManager RoleManager
    {
        get
        {
            return _roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>();
        }
        private set
        {
            _roleManager = value;
        }
    }

    public ActionResult RoleIndex()
    {
        return View(RoleManager.Roles);
    }

    public ActionResult RoleCreate()
    {
        return View();
    }

    [HttpPost]
    public async Task<ActionResult> RoleCreate(SysAdminVM.RoleViewModel roleViewModel)
    {
        if (ModelState.IsValid)
        {
            // Use ApplicationRole, not IdentityRole:
            var role = new ApplicationRole(roleViewModel.Name);
            var roleresult = await RoleManager.CreateAsync(role);
            if (!roleresult.Succeeded)
            {
                ModelState.AddModelError("", roleresult.Errors.First());
                return View();
            }
            return RedirectToAction("RoleIndex");
        }
        return View();
    }

    public async Task<ActionResult> RoleEdit(int id)
    {
        if (id > 0)
        {
            var role = await RoleManager.FindByIdAsync(id);
            if (role == null)
            {
                return HttpNotFound();
            }
            SysAdminVM.RoleViewModel roleModel = new SysAdminVM.RoleViewModel { Id = role.Id, Name = role.Name };
            return View(roleModel);
        }
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> RoleEdit([Bind(Include = "Name,Id")] SysAdminVM.RoleViewModel roleModel)
    {
        if (ModelState.IsValid)
        {
            var role = await RoleManager.FindByIdAsync(roleModel.Id);
            role.Name = roleModel.Name;
            await RoleManager.UpdateAsync(role);
            return RedirectToAction("RoleIndex");
        }
        return View();
    }

    [AllowAnonymous]
    public async Task<ActionResult> Initialize()
    {
        if (db.App.Where(x => x.Name.Contains("Initialize")).FirstOrDefault() == null)
        {
            await InitRoleCreate();
            await InitUser();
            db.App.Add(
                new App { Name = "Initialize", Val = "true" }
            );
            db.SaveChanges();
            return View();
        }
        return HttpNotFound();
    }

    private async Task InitRoleCreate()
    {
        var model = new List<string>()
    {
        "SysAdmin",
        "Admin",
        "User",
    };
        foreach (var item in model)
        {
            var role = new ApplicationRole(item);
            await RoleManager.CreateAsync(role);
        }
    }

    private async Task InitUser()
    {
        var user = new ApplicationUser
        {
            UserName = "HerGiz",
            Email = "hergiz@outlook.com",
            Name = "Igor Hermanović",
            Contact = "098 185 3131",
            TwoFactorEnabled = false,
            LockoutEnabled = true,
            EmailConfirmed = true
        };
        var adminResult = await UserManager.CreateAsync(user, "W7xtc2ywfb");
        await UserManager.AddToRolesAsync(user.Id, "SysAdmin");
    }
}

我需要的整个 API 部分 - 控制器和开箱即用的登录(在某处被 berried):

[Authorize]
public class ApiKeysController : ApiController
{
    [Authorize]
    [Route("api/getkey/{term}")]
    public ShowFullKeyVM Get(string term)
    {
        if (User.Identity.IsAuthenticated == true)
        {
            if (!string.IsNullOrWhiteSpace(term) && (term.Length == 15 || term.Length == 16))
            {
                var lKey = new LKey();
                var vm = lKey.Search(term);
                if (vm != null)
                {
                    return vm;
                }
            }
            return new ShowFullKeyVM() { Error = "IMEI either is not valid :(", SearchIMEI = term };
        }
        return new ShowFullKeyVM() { Error = "Not Authenticated!!!", SearchIMEI = term };
    }
}

全球.asax

protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        GlobalConfiguration.Configure(WebApiConfig.Register);

        MvcHandler.DisableMvcResponseHeader = true;

        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        App_Start.AutoMapperConfig.DefineMaps();

        ModelBinders.Binders.Add(typeof(decimal), new Extensions.DecimalModelBinder());
        ModelBinders.Binders.Add(typeof(decimal?), new Extensions.DecimalModelBinder());
    }

Web.config

<appSettings>
    <add key="UserLockoutEnabledByDefault" value="true" />
    <add key="DefaultAccountLockoutTimeSpan" value="30" />
    <add key="MaxFailedAccessAttemptsBeforeLockout" value="4" />
 </appSettings>

【讨论】:

  • 这似乎是一个奇怪的解决方案,你想要的; API,数据库和应用程序,可能出于某种原因将它们分开?最终你决定合并 API 和网站?这意味着当您的 API 遭到 DDOSS 攻击时,您的网站就会关闭,反之亦然?
猜你喜欢
  • 2018-10-07
  • 1970-01-01
  • 1970-01-01
  • 2010-09-23
  • 2016-07-06
  • 1970-01-01
  • 1970-01-01
  • 2019-09-18
  • 2021-04-25
相关资源
最近更新 更多