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

之前讲了投票模块的功能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已经完成,写得不是很好,仅供参考

猜你喜欢