之前讲了投票模块的功能api,要实现用户登录后才可以进行投票,对登录用户进行认证,我采用的是JWT(json web token),在请求头加入Authorization,并加上Bearer标注。
大概流程如下:
1 用户请求登录服务器
2 服务器接到请求生成一个token,返回给浏览器
3 之后浏览器的每一次请求都带着这个token
4 服务器收到后验证是否正确,是否被更改过,认证通过就可以返回对应的response
首先编写一个创建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测试
发现登陆成功后,得到一个token,之后我们请求头都带上这个token,在Authorization中添加 Bearer Token,将刚刚的token复制过去即可
成功得到数据
如果请求头不加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已经完成,写得不是很好,仅供参考