因为正在开发的系统是内部类型,希望只是简单建立server-side的验证码机制就好,所以就不考虑使用Google reCaptcha。网路上.NET Core文章有点乱,版本又杂(微软根本版本之鬼...),顺手整理一下
.NET 版本: .NET Core 5
.NET Core的所有物件皆须已注入的方式使用,而我们这次要将验证码储存在Session
中。
需要先在startup.cs
中增加以下项目:
app.UseSession()
,告诉.NET Core需要使用Sessionservices.AddDistributedMemoryCache()
,注入分散式记忆体快取物件,Session会用到services.AddSession()
,注入Seesionservices.AddHttpContextAccessor()
,後面会说明为什麽有这行。startup.cs
public void ConfigureServices(IServiceCollection services)
{
// 注入分散式记忆体快取
services.AddDistributedMemoryCache();
// 注入Session
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(10);//You can set Time
});
// 注入 HttpContextAccessor
services.AddHttpContextAccessor();
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TestValidation", Version = "v1" });
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TestValidation v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
// 使用Session
app.UseSession();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
接下来我们建立一个.NET Core 类别库专案,并建立CodeValidation.cs
,interface
、class
放在同一个档案,方便使用。
这里要注意一点,.NET Core很多东西是要自己额外安装:
IHttpContextAccessor
需要从nuget
安装套件: Microsoft.AspNetCore.Http
public interface ICodeValidator
{
/// <summary>
/// 验证
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
bool Validate(string code);
/// <summary>
/// 产生验证码
/// </summary>
/// <returns></returns>
string Generate();
}
public class CodeValidator : ICodeValidator
{
private const string KEY = "ValidationCode";
private HttpContext _httpContext { get; set; }
// 注入 IHttpContextAccessor ,因为我们要使用HttpContext取得Session
public CodeValidator(IHttpContextAccessor httpContextAccessor)
{
_httpContext = httpContextAccessor.HttpContext;
}
public string Generate()
{
string code = CreateRandomWord(5);
// session只能储存byte[],将字串转为byte[]
byte[] codeBytes = Encoding.ASCII.GetBytes(code);
_httpContext.Session.Set(KEY, codeBytes);
return code;
}
public bool Validate(string code)
{
bool isOk = false;
byte[] codeBytes = null;
if(_httpContext.Session.TryGetValue(KEY,out codeBytes))
{
// 从Session取出来的byte[] 转成字串
string serverCode = Encoding.ASCII.GetString(codeBytes);
// 忽略大小写比对
if (serverCode.Equals(code, StringComparison.InvariantCultureIgnoreCase))
{
isOk = true;
}
}
// 无论成功失败,都清除Session。(依情境,非必要)
_httpContext.Session.Remove(KEY);
return isOk;
}
/// <summary>
/// 产生随机字串
/// </summary>
/// <param name="length"></param>
/// <returns></returns>
private string CreateRandomWord(int length = 5)
{
string code = "";
var letters = "ABCDEFGHJKMPQRSTUVWXYZ23456789abcdefghjkmpqrstuvwxyz".ToArray();
Random r = new Random();
for (int i = 0; i < length; i++)
{
int index = r.Next(0, letters.Length);
code += letters[index];
}
return code;
}
}
这就是为什麽我们会注入HttpContextAccessor
的原因,因为实际专案基本上都是分层架构,所以要在不同层级取得Session,就必须注入此物件,而不是从Controller传入HttpContext。
接下来我们需要将CodeValidator
注入给Controller使用,所以startup.cs
要增加程序
public void ConfigureServices(IServiceCollection services)
{
// 注入分散式记忆体快取
services.AddDistributedMemoryCache();
// 注入Session
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(10);//You can set Time
});
// 注入验证物件
services.AddScoped(typeof(ICodeValidator), typeof(CodeValidator));
// 注入 HttpContextAccessor
services.AddHttpContextAccessor();
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TestValidation", Version = "v1" });
});
}
建立CodeController
制作验证码API。
[Route("api/[controller]")]
[ApiController]
public class CodeController : ControllerBase
{
private ICodeValidator _codeValidator { get; set; }
public CodeController(ICodeValidator codeValidator)
{
_codeValidator = codeValidator;
}
[HttpGet]
public ActionResult<string> Generate()
{
string code = _codeValidator.Generate();
return Ok(code);
}
[HttpGet("{code}")]
public ActionResult Validate(string code)
{
bool isOk = _codeValidator.Validate(code);
return isOk ? Ok() : BadRequest();
}
}
测试结果
取得验证码,反回CDXPM
输入验证码验证,返回200 OK
输入错误测试
从新取得验证码 psBw9
输入错误的验证码12345
,返回400 BadRequest
对於大多数 iPhone 用户来说,管理 iPhone 存储空间往往是一件令人头疼的事情。存储空间太...
透过 audio tag 设定背景音乐 class BGM { constructor() { th...
主体资料 (Body) 在传输资料时经常会使用到主体资料,比如说:POST、PUT、PATCH等操作...
在上一章,我们提到了如何用一般方法实作 PRNG 乱数生成器,本章将介绍 State Monad 以...
谈到扩充性,JUCE 以 Modules 为基础,开发者可提供自制 Module,供其他人使用。如下...