欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

(五)用户管理及登录页面

程序员文章站 2022-05-16 12:00:20
目录 1.创建项目 2.重写用户管理界面 3.用户管理 4.用户注册 5.用户登录 6.用户退出 7.自动数据迁移 8.启动应用 1.创建项目 项目中会多出如下红框内的用户身份管理的文件: 单独启动该项目,已经拥有了用户登录、注册、注销、管理等功能页面。 2.重写用户管理界面 但是,我们并不想使用原 ......

目录

 

1.创建项目

(五)用户管理及登录页面

 

(五)用户管理及登录页面

 

 (五)用户管理及登录页面

 

项目中会多出如下红框内的用户身份管理的文件:

 (五)用户管理及登录页面

单独启动该项目,已经拥有了用户登录、注册、注销、管理等功能页面。

 

2.重写用户管理界面

但是,我们并不想使用原有的登录页面,我们想要自定义用户页面。

项目=》 右键 =》 添加 =》 新搭建基架的项目

(五)用户管理及登录页面

 

 (五)用户管理及登录页面

 

 (五)用户管理及登录页面

 

添加过程中如果报错 “运行所选代码生成器时出错”, 则先清理该项目,尝试重新添加,如果仍然报错,重启vsstudio,再次添加基架即可。

然后就会看到项目中添加了下图中的文件:

 (五)用户管理及登录页面

user.cs 中添加姓名和生日:

public class user : identityuser
    {
        [personaldata]
        public string name { get; set; }
        [personaldata]
        public datetime birthday { get; set; }
    }

3.用户管理

leo.users.areas.identity.pages.account.manage.index.cshtml.cs :

public partial class indexmodel : pagemodel
    {
        private readonly usermanager<user> _usermanager;
        private readonly signinmanager<user> _signinmanager;
        private readonly iemailsender _emailsender;

        public indexmodel(
            usermanager<user> usermanager,
            signinmanager<user> signinmanager,
            iemailsender emailsender)
        {
            _usermanager = usermanager;
            _signinmanager = signinmanager;
            _emailsender = emailsender;
        }

        public string username { get; set; }

        public bool isemailconfirmed { get; set; }

        [tempdata]
        public string statusmessage { get; set; }

        [bindproperty]
        public inputmodel input { get; set; }

        public class inputmodel
        {
            #region 新增
            [required]
            [datatype(datatype.text)]
            [display(name = "姓名")]
            public string name { get; set; }

            [required]
            [display(name = "生日")]
            [datatype(datatype.date)]
            public datetime birthday { get; set; }
            #endregion

            [required]
            [emailaddress]
            public string email { get; set; }

            [phone]
            [display(name = "电话")]
            public string phonenumber { get; set; }
        }

        public async task<iactionresult> ongetasync()
        {
            var user = await _usermanager.getuserasync(user);
            if (user == null)
            {
                return notfound($"unable to load user with id '{_usermanager.getuserid(user)}'.");
            }

            var username = await _usermanager.getusernameasync(user);
            var email = await _usermanager.getemailasync(user);
            var phonenumber = await _usermanager.getphonenumberasync(user);

            username = username;

            input = new inputmodel
            {
                #region 新增
                name = user.name,
                birthday = user.birthday,
                #endregion
                email = email,
                phonenumber = phonenumber
            };

            isemailconfirmed = await _usermanager.isemailconfirmedasync(user);

            return page();
        }

        public async task<iactionresult> onpostasync()
        {
            if (!modelstate.isvalid)
            {
                return page();
            }

            var user = await _usermanager.getuserasync(user);
            if (user == null)
            {
                return notfound($"unable to load user with id '{_usermanager.getuserid(user)}'.");
            }

            var email = await _usermanager.getemailasync(user);
            if (input.email != email)
            {
                var setemailresult = await _usermanager.setemailasync(user, input.email);
                if (!setemailresult.succeeded)
                {
                    var userid = await _usermanager.getuseridasync(user);
                    throw new invalidoperationexception($"unexpected error occurred setting email for user with id '{userid}'.");
                }
            }

            #region 新增
            if (input.name != user.name)
            {
                user.name = input.name;
            }

            if (input.birthday != user.birthday)
            {
                user.birthday = input.birthday;
            }
            #endregion
            
            var phonenumber = await _usermanager.getphonenumberasync(user);
            if (input.phonenumber != phonenumber)
            {
                var setphoneresult = await _usermanager.setphonenumberasync(user, input.phonenumber);
                if (!setphoneresult.succeeded)
                {
                    var userid = await _usermanager.getuseridasync(user);
                    throw new invalidoperationexception($"unexpected error occurred setting phone number for user with id '{userid}'.");
                }
            }

            #region 新增
            await _usermanager.updateasync(user);
            #endregion

            await _signinmanager.refreshsigninasync(user);
            statusmessage = "your profile has been updated";
            return redirecttopage();
        }

        public async task<iactionresult> onpostsendverificationemailasync()
        {
            if (!modelstate.isvalid)
            {
                return page();
            }

            var user = await _usermanager.getuserasync(user);
            if (user == null)
            {
                return notfound($"unable to load user with id '{_usermanager.getuserid(user)}'.");
            }


            var userid = await _usermanager.getuseridasync(user);
            var email = await _usermanager.getemailasync(user);
            var code = await _usermanager.generateemailconfirmationtokenasync(user);
            var callbackurl = url.page(
                "/account/confirmemail",
                pagehandler: null,
                values: new { userid = userid, code = code },
                protocol: request.scheme);
            await _emailsender.sendemailasync(
                email,
                "confirm your email",
                $"please confirm your account by <a href='{htmlencoder.default.encode(callbackurl)}'>clicking here</a>.");

            statusmessage = "verification email sent. please check your email.";
            return redirecttopage();
        }
    }

展示页面  index.cshtml :

@page
@model indexmodel
@{
    viewdata["title"] = "profile";
    viewdata["activepage"] = managenavpages.index;
}

<h4>@viewdata["title"]</h4>
<partial name="_statusmessage" for="statusmessage" />
<div class="row">
    <div class="col-md-6">
        <form id="profile-form" method="post">
            <div asp-validation-summary="all" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="username"></label>
                <input asp-for="username" class="form-control" disabled />
            </div>
            <div class="form-group">
                <label asp-for="input.email"></label>
                @if (model.isemailconfirmed)
                {
                    <div class="input-group">
                        <input asp-for="input.email" class="form-control" />
                        <span class="input-group-addon" aria-hidden="true"><span class="glyphicon glyphicon-ok text-success"></span></span>
                    </div>
                }
                else
                {
                    <input asp-for="input.email" class="form-control" />
                    <button id="email-verification" type="submit" asp-page-handler="sendverificationemail" class="btn btn-link">send verification email</button>
                }
                <span asp-validation-for="input.email" class="text-danger"></span>
            </div>
            <div class="form-group">
                @* 新增 start *@
                <div class="form-group">
                    <label asp-for="input.name"></label>
                    <input asp-for="input.name" class="form-control" />
                </div>
                <div class="form-group">
                    <label asp-for="input.birthday"></label>
                    <input asp-for="input.birthday" class="form-control" />
                </div>
                @* end *@
                <label asp-for="input.phonenumber"></label>
                <input asp-for="input.phonenumber" class="form-control" />
                <span asp-validation-for="input.phonenumber" class="text-danger"></span>
            </div>
            <button id="update-profile-button" type="submit" class="btn btn-primary">save</button>
        </form>
    </div>
</div>

@section scripts {
    <partial name="_validationscriptspartial" />
}

4.用户注册

leo.users.areas.identity.pages.account.register.cshtml.cs :

[allowanonymous]
    public class registermodel : pagemodel
    {
        private readonly signinmanager<user> _signinmanager;
        private readonly usermanager<user> _usermanager;
        private readonly ilogger<registermodel> _logger;
        private readonly iemailsender _emailsender;

        public registermodel(
            usermanager<user> usermanager,
            signinmanager<user> signinmanager,
            ilogger<registermodel> logger,
            iemailsender emailsender)
        {
            _usermanager = usermanager;
            _signinmanager = signinmanager;
            _logger = logger;
            _emailsender = emailsender;
        }

        [bindproperty]
        public inputmodel input { get; set; }

        public string returnurl { get; set; }

        public class inputmodel
        {
            #region 新增
            [required]
            [datatype(datatype.text)]
            [display(name = "姓名")]
            public string name { get; set; }

            [required]
            [display(name = "生日")]
            [datatype(datatype.date)]
            public datetime birthday { get; set; }
            #endregion

            [required]
            [emailaddress]
            [display(name = "email")]
            public string email { get; set; }

            [required]
            [stringlength(100, errormessage = "the {0} must be at least {2} and at max {1} characters long.", minimumlength = 6)]
            [datatype(datatype.password)]
            [display(name = "密码")]
            public string password { get; set; }

            [datatype(datatype.password)]
            [display(name = "确认密码")]
            [compare("password", errormessage = "the password and confirmation password do not match.")]
            public string confirmpassword { get; set; }
        }

        public void onget(string returnurl = null)
        {
            returnurl = returnurl;
        }

        public async task<iactionresult> onpostasync(string returnurl = null)
        {
            returnurl = returnurl ?? url.content("~/");
            if (modelstate.isvalid)
            {
                var user = new user {
                    #region 新增
                    name = input.name,
                    birthday = input.birthday,
                    #endregion
                    username = input.email,
                    email = input.email
                };
                var result = await _usermanager.createasync(user, input.password);
                if (result.succeeded)
                {
                    _logger.loginformation("user created a new account with password.");

                    var code = await _usermanager.generateemailconfirmationtokenasync(user);
                    var callbackurl = url.page(
                        "/account/confirmemail",
                        pagehandler: null,
                        values: new { userid = user.id, code = code },
                        protocol: request.scheme);

                    await _emailsender.sendemailasync(input.email, "confirm your email",
                        $"please confirm your account by <a href='{htmlencoder.default.encode(callbackurl)}'>clicking here</a>.");

                    await _signinmanager.signinasync(user, ispersistent: false);
                    return localredirect(returnurl);
                }
                foreach (var error in result.errors)
                {
                    modelstate.addmodelerror(string.empty, error.description);
                }
            }

            // if we got this far, something failed, redisplay form
            return page();
        }
    }

展示页面  register.cshtml :

@page
@model registermodel
@{
    viewdata["title"] = "register";
}

<h1>@viewdata["title"]</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnurl="@model.returnurl" method="post">
            <h4>create a new account.</h4>
            <hr />
            <div asp-validation-summary="all" class="text-danger"></div>
            @* 新增 start *@
            <div class="form-group">
                <label asp-for="input.name"></label>
                <input asp-for="input.name" class="form-control" />
                <span asp-validation-for="input.name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="input.birthday"></label>
                <input asp-for="input.birthday" class="form-control" />
                <span asp-validation-for="input.birthday" class="text-danger"></span>
            </div>
            @* end *@
            <div class="form-group">
                <label asp-for="input.email"></label>
                <input asp-for="input.email" class="form-control" />
                <span asp-validation-for="input.email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="input.password"></label>
                <input asp-for="input.password" class="form-control" />
                <span asp-validation-for="input.password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="input.confirmpassword"></label>
                <input asp-for="input.confirmpassword" class="form-control" />
                <span asp-validation-for="input.confirmpassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">register</button>
        </form>
    </div>
</div>

@section scripts {
    <partial name="_validationscriptspartial" />
}

5.用户登录

login.cshtml.cs :

[allowanonymous]
    public class loginmodel : pagemodel
    {
        private readonly signinmanager<user> _signinmanager;
        private readonly ilogger<loginmodel> _logger;

        public loginmodel(signinmanager<user> signinmanager, ilogger<loginmodel> logger)
        {
            _signinmanager = signinmanager;
            _logger = logger;
        }

        [bindproperty]
        public inputmodel input { get; set; }

        public ilist<authenticationscheme> externallogins { get; set; }

        public string returnurl { get; set; }

        [tempdata]
        public string errormessage { get; set; }

        public class inputmodel
        {
            [required]
            [emailaddress]
            public string email { get; set; }

            [required]
            [datatype(datatype.password)]
            public string password { get; set; }

            [display(name = "remember me?")]
            public bool rememberme { get; set; }
        }

        public async task ongetasync(string returnurl = null)
        {
            if (!string.isnullorempty(errormessage))
            {
                modelstate.addmodelerror(string.empty, errormessage);
            }

            returnurl = returnurl ?? url.content("~/");

            // clear the existing external cookie to ensure a clean login process
            await httpcontext.signoutasync(identityconstants.externalscheme);

            externallogins = (await _signinmanager.getexternalauthenticationschemesasync()).tolist();

            returnurl = returnurl;
        }

        public async task<iactionresult> onpostasync(string returnurl = null)
        {
            returnurl = returnurl ?? url.content("~/");

            if (modelstate.isvalid)
            {
                // this doesn't count login failures towards account lockout
                // to enable password failures to trigger account lockout, set lockoutonfailure: true
                var result = await _signinmanager.passwordsigninasync(input.email, input.password, input.rememberme, lockoutonfailure: true);
                if (result.succeeded)
                {
                    _logger.loginformation("user logged in.");
                    return localredirect(returnurl);
                }
                if (result.requirestwofactor)
                {
                    return redirecttopage("./loginwith2fa", new { returnurl = returnurl, rememberme = input.rememberme });
                }
                if (result.islockedout)
                {
                    _logger.logwarning("user account locked out.");
                    return redirecttopage("./lockout");
                }
                else
                {
                    modelstate.addmodelerror(string.empty, "invalid login attempt.");
                    return page();
                }
            }

            // if we got this far, something failed, redisplay form
            return page();
        }
    }

login.cshtml :

@page
@model loginmodel

@{
    viewdata["title"] = "log in";
}

<h1>@viewdata["title"]</h1>
<div class="row">
    <div class="col-md-4">
        <section>
            <form id="account" method="post">
                <h4>use a local account to log in.</h4>
                <hr />
                <div asp-validation-summary="all" class="text-danger"></div>
                <div class="form-group">
                    <label asp-for="input.email"></label>
                    <input asp-for="input.email" class="form-control" />
                    <span asp-validation-for="input.email" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="input.password"></label>
                    <input asp-for="input.password" class="form-control" />
                    <span asp-validation-for="input.password" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <div class="checkbox">
                        <label asp-for="input.rememberme">
                            <input asp-for="input.rememberme" />
                            @html.displaynamefor(m => m.input.rememberme)
                        </label>
                    </div>
                </div>
                <div class="form-group">
                    <button type="submit" class="btn btn-primary">log in</button>
                </div>
                <div class="form-group">
                    <p>
                        <a id="forgot-password" asp-page="./forgotpassword">forgot your password?</a>
                    </p>
                    <p>
                        <a asp-page="./register" asp-route-returnurl="@model.returnurl">register as a new user</a>
                    </p>
                </div>
            </form>
        </section>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h4>use another service to log in.</h4>
            <hr />
            @{
                if ((model.externallogins?.count ?? 0) == 0)
                {
                    <div>
                        <p>
                            there are no external authentication services configured. see <a href="https://go.microsoft.com/fwlink/?linkid=532715">this article</a>
                            for details on setting up this asp.net application to support logging in via external services.
                        </p>
                    </div>
                }
                else
                {
                    <form id="external-account" asp-page="./externallogin" asp-route-returnurl="@model.returnurl" method="post" class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in model.externallogins)
                                {
                                    <button type="submit" class="btn btn-primary" name="provider" value="@provider.name" title="log in using your @provider.displayname account">@provider.displayname</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>

@section scripts {
    <partial name="_validationscriptspartial" />
}

6.用户退出

logout.cshtml.cs :

[allowanonymous]
    public class logoutmodel : pagemodel
    {
        private readonly signinmanager<user> _signinmanager;
        private readonly ilogger<logoutmodel> _logger;

        public logoutmodel(signinmanager<user> signinmanager, ilogger<logoutmodel> logger)
        {
            _signinmanager = signinmanager;
            _logger = logger;
        }

        public void onget()
        {
        }

        public async task<iactionresult> onpost(string returnurl = null)
        {
            await _signinmanager.signoutasync();
            _logger.loginformation("user logged out.");
            if (returnurl != null)
            {
                return localredirect(returnurl);
            }
            else
            {
                return page();
            }
        }
    }

logout.cshtml :

@page
@model logoutmodel
@{
    viewdata["title"] = "log out";
}

<header>
    <h1>@viewdata["title"]</h1>
    <p>您已成功退出</p>
</header>

7.自动数据迁移

程序自动完成数据库以及表的构建

(五)用户管理及登录页面

如图,会报错,是因为项目中有两个context 数据上下文:

(五)用户管理及登录页面

删掉下面的包含 applicationdbcontext.cs  的 data文件夹

然后编译时startup.cs 会报错,找不到applicationdbcontext 类,此时我们直接将这一段注释掉即可,因为我们添加基架以后已经有了新的数据模块:

(五)用户管理及登录页面

 

public class startup
    {
        public startup(iconfiguration configuration)
        {
            configuration = configuration;
        }

        public iconfiguration configuration { get; }

        // this method gets called by the runtime. use this method to add services to the container.
        public void configureservices(iservicecollection services)
        {
            services.configure<cookiepolicyoptions>(options =>
            {
                // this lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.checkconsentneeded = context => true;
                options.minimumsamesitepolicy = samesitemode.none;
            });

            #region 身份认证相关,注释掉
            //services.adddbcontext<applicationdbcontext>(options =>
            //    options.usesqlserver(
            //        configuration.getconnectionstring("defaultconnection")));
            //services.adddefaultidentity<identityuser>()
            //    .adddefaultui(uiframework.bootstrap4)
            //    .addentityframeworkstores<applicationdbcontext >();
            #endregion

            services.addmvc().setcompatibilityversion(compatibilityversion.version_2_2);
        }

        // this method gets called by the runtime. use this method to configure the http request pipeline.
        public void configure(iapplicationbuilder app, ihostingenvironment env)
        {
            if (env.isdevelopment())
            {
                app.usedeveloperexceptionpage();
                app.usedatabaseerrorpage();
            }
            else
            {
                app.useexceptionhandler("/home/error");
                // the default hsts value is 30 days. you may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.usehsts();
            }

            app.usehttpsredirection();
            app.usestaticfiles();
            app.usecookiepolicy();

            #region 身份认证相关,保留
            app.useauthentication();
            #endregion

            app.usemvc(routes =>
            {
                routes.maproute(
                    name: "default",
                    template: "{controller=home}/{action=index}/{id?}");
            });
        }
    }

再次添加迁移,不再报错:

add-migration customuserdata

 (五)用户管理及登录页面

更新到数据库:

update-database

(五)用户管理及登录页面

完成。

查看数据库:

(五)用户管理及登录页面

为了防止以后出现问题,我们删掉原来的defaultconnection  ,将 userscontextconnection 改为 defaultconnection, 搜索“userscontextconnection” ,用到该字符串的地方都替换为 “defaultconnection” 。

(五)用户管理及登录页面

 

8.启动应用

 选择用 kestrel 服务器启动,方便监控:

(五)用户管理及登录页面

 启动以后,会抛出异常( invalidoperationexception: no service for type 'microsoft.aspnetcore.identity.usermanager`1[microsoft.aspnetcore.identity.identityuser]' has been registered.):

(五)用户管理及登录页面

意思是说  usermanager<identityuser> 类型的服务没有被注册,事实上,我们将identityuser实现为user ,是对user模型进行管理,搜索 identityuser,替换为 user

(五)用户管理及登录页面

(五)用户管理及登录页面

记得添加命名空间,不然引入的user并不正确:

@inject signinmanager<leo.users.areas.identity.data.user> signinmanager
@inject usermanager<leo.users.areas.identity.data.user> usermanager

 启动成功,注册账号:

(五)用户管理及登录页面

完成:

(五)用户管理及登录页面

接下来,我们检查一下后台的用户数据:

 (五)用户管理及登录页面

完成!