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

ASP .NET + Angular前后端分离实现简单投票系统(JWT登录用户认证)

程序员文章站 2022-07-10 12:44:38
...

之前讲了投票模块的功能api,要实现用户登录后才可以进行投票,对登录用户进行认证,我采用的是JWT(json web token),在请求头加入Authorization,并加上Bearer标注。
大概流程如下:
1 用户请求登录服务器
2 服务器接到请求生成一个token,返回给浏览器
3 之后浏览器的每一次请求都带着这个token
4 服务器收到后验证是否正确,是否被更改过,认证通过就可以返回对应的responseASP .NET + Angular前后端分离实现简单投票系统(JWT登录用户认证)
首先编写一个创建Token的工具类
JwtUtil.cs

using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Vote_Api.Models;

namespace Vote_Api.Utility
{
    public class JwtUtil
    {
        private IConfiguration _configuration { get; }

        public JwtUtil(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public string getToken()
        {
            var claims = new[]
            {
                new Claim(JwtRegisteredClaimNames.NameId, "zxx"),
                new Claim(ClaimTypes.Role, "admin")
            };
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["SecurityKey"])); // 获取**
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); //凭证 ,根据**生成

            var token = new JwtSecurityToken(
               issuer: "Lola",   //签发者
               audience: "Voter",  //接受者
               claims: claims,
               expires: DateTime.Now.AddMinutes(10), //设置过期时间为10分钟
               signingCredentials: creds
           );

            return new JwtSecurityTokenHandler().WriteToken(token);

        }
    }
}

其次还要在Startup.cs的ConfigureServices中添加配置

 public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,//是否验证Issuer
                    ValidateAudience = true,//是否验证Audience
                    ValidateLifetime = true,//是否验证失效时间
                    ValidateIssuerSigningKey = true,//是否验证SecurityKey
                    ValidIssuer = "Lola",
                    ValidAudience = "Voter",
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["SecurityKey"])),
                    RequireExpirationTime = true
                };
                options.Events = new JwtBearerEvents()
                {
                    //验证失败
                    OnAuthenticationFailed = context =>
                    {
                        context.NoResult();

                        context.Response.StatusCode = 401;
                        context.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = context.Exception.Message;
                        Debug.WriteLine("OnAuthenticationFailed: " + context.Exception.Message);
                        return Task.CompletedTask;
                    },
                };
            });
          }

在Startup.cs的Congiure方法中添加一行

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseAuthentication();
        }

编写登录模块的业务逻辑
ILoginService.cs

using Vote_Api.Models;

namespace Vote_Api.Service.IService
{
    public interface ILoginService
    {
        string CheckLogin(string username, string pwd);

        bool UpdatePwd(string oldPwd, string newPwd);
    }
}

实现类LoginService.cs

using System;
using Vote_Api.Models;
using Vote_Api.Service.IService;
using Vote_Api.Utility;

namespace Vote_Api.Service.ServiceImpl
{
    public class LoginService : ILoginService
    {
        private readonly VoteContext _context;
        private readonly JwtTokenUtil _tokenUtil;
       
        public LoginService(VoteContext context, JwtTokenUtil tokenUtil)
        {
            _context = context;
            _tokenUtil = tokenUtil;
        }
        public string CheckLogin(string userId, string pwd)
        {
            string result = "";

            try
            {
                User user = _context.User_Info.Find(userId); //查找用户id,返回用户(一直觉得这个方法不好,但能力有限,将就看吧)

                if (user.Passwd == pwd)  //判断密码是否正确
                {
                    result = "{\"token\":\"";
                    result += _tokenUtil.getToken();  //用户密码正确,发放一个token
                    result += "\"}";
                }
                else
                {
                    result = "error";
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            return result;

        }

        public bool UpdatePwd(string oldPwd, string newPwd)
        {
            throw new NotImplementedException();
        }

    }
}

编写LoginController

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Vote_Api.Service.IService;

namespace Vote_Api.Controllers
{
    [Route("api/login")]
    [ApiController]
    public class LoginController : Controller
    {
        private readonly ILoginService _service;
        public LoginController(ILoginService service)
        {
            _service = service;
        }
        [HttpGet("CheckLogin")]
        public string CheckAccount(string username, string pwd)
        {
            var token = _service.CheckLogin(username, pwd);
            if(token != "error")
            {
                Response.Cookies.Append("token",token);
            }
            return _service.CheckLogin(username, pwd);
        }

        [HttpPost("UpdatePwd")]
        [Authorize]  //增加权限验证,访问该api的请求头必须带着发放的token
        public bool UpdatePwd(string oldPwd, string newPwd)
        {
            return _service.UpdatePwd(oldPwd, newPwd);
        }
        
    }

}

同样在之前的VoteController中的需要验证的api方法前添加[Anthorize]

用postman测试
ASP .NET + Angular前后端分离实现简单投票系统(JWT登录用户认证)
发现登陆成功后,得到一个token,之后我们请求头都带上这个token,在Authorization中添加 Bearer Token,将刚刚的token复制过去即可
ASP .NET + Angular前后端分离实现简单投票系统(JWT登录用户认证)
成功得到数据
ASP .NET + Angular前后端分离实现简单投票系统(JWT登录用户认证)
如果请求头不加token,就没有结果
另外之后前端访问api还会涉及到一个跨域请求的问题,这里先写好
直接在Startup.cs的ConfigfureService方法中添加Cors

 public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options => options.AddPolicy("CorsPolicy",
            builder =>
            {
                builder.AllowAnyMethod()
                    .SetIsOriginAllowed(_ => true)
                    .AllowAnyHeader()
                    .AllowCredentials();
            }));
        }

在Configure方法中添加,需注意位置,必须在app.UseMvc()前面

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseCors("CorsPolicy");
        }
    }

这样就可以跨域访问啦
到此,登录模块的api已经完成,写得不是很好,仅供参考