本文基于.net core webapi进行讲解,使用CSRedis库操作Redis,实现项目的登录模块。登录方式使用手机号、短信验证码进行登录。登录过程包含获取短信验证码、判断用户输入手机号及短信验证码是否正确及刷新登录有效期3个环节。

用户登录第一步先输入手机号码,并获取验证码,后台生成验证码后存储到Redis中,并设置有效期,验证码用字符串格式存储在Redis中,为确保多用户同时发送验证码时验证码唯一,将使用手机号作为Redis的Key。发送手机验证码接口相关代码实现如下:
1、接口Controllers代码
namespace RedisTest.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
[TypeFilter(typeof(CustomAuthorizeFilter))]
public class LoginController : ControllerBase
{
private readonly IRedisDomainService redisService;
public LoginController(IRedisDomainService redisService)
{
this.redisService = redisService;
}
/// <summary>
/// 发送手机验证码
/// </summary>
/// <param name="phone"></param>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
public ResponseContent SendCode(string phone)
{
//HttpContext.Session.GetString("key");
return redisService.SendCode(phone);
}
}
}
2、短信验证码发送服务代码
using Common.CacheManager;
using Common.Commons;
using Common.Enum;
using Domain.Entities;
using Domain.Repository;
using Microsoft.AspNetCore.Http;
using Common.Const;
using Common.Extensions;
using System.Security.Policy;
namespace Domain.Service
{
public class RedisDomainService : IRedisDomainService
{
private readonly IRedisDomainRepostory redisRepostory;
private readonly ICacheService cacheService;
public RedisDomainService(IRedisDomainRepostory redisRepostory, ICacheService cacheService)
{
this.redisRepostory = redisRepostory;
this.cacheService = cacheService;
}
/// <summary>
/// 发送短信验证码
/// </summary>
/// <param name="phone"></param>
/// <returns></returns>
public ResponseContent SendCode(string phone)
{
ResponseContent response = new ResponseContent();
//1.校验手机号
if (!RegexUtils.IsMobile(phone))
{
//2.如果不符合,返回错误信息
return response.Error(ResponseType.ParameterVerificationError);
}
//3.符合生成验证码
Random random = new Random();
string code = random.Next(100000, 999999).ToString();
//4.保存验证码到Redis
cacheService.Add(RedisConstants.LOGIN_CODE_KEY + phone, code, RedisConstants.LOGIN_CODE_TTL);
//5.发送验证码(模拟发送)
Console.WriteLine("短信验证码已经发送,验证码:" + code);
//返回ok
return response.Ok(code);
}
}
}
用户输入手机号及短信验证码,后端代码将于Redis中存储的短信验证码进行对比,如果一致在进行判断数据库中是否存在用户信息,不存在将进行用户创建,最后将用户信息存储到Redis中,供其他模块使用,存储用户信息将使用Hash类型进行存储,随机生成Guid作为Key值,并将Guid作为Token返回给前端,前端在访问其他接口时需要在Header的Authorization中携带token。关键代码如下:
1、接口Controllers代码
using Common.Commons;
using Domain.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
namespace RedisTest.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
[TypeFilter(typeof(CustomAuthorizeFilter))]
public class LoginController : ControllerBase
{
private readonly IRedisDomainService redisService;
public LoginController(IRedisDomainService redisService)
{
this.redisService = redisService;
}
/// 登录
/// </summary>
/// <param name="phone"></param>
/// <param name="code"></param>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
public async Task<ResponseContent> Login(string phone, string code)
{
return await redisService.Login(phone, code);
}
}
}
2、用户登录验证服务代码
using Common.CacheManager;
using Common.Commons;
using Common.Enum;
using Domain.Entities;
using Domain.Repository;
using Microsoft.AspNetCore.Http;
using Common.Const;
using Common.Extensions;
using System.Security.Policy;
namespace Domain.Service
{
public class RedisDomainService : IRedisDomainService
{
private readonly IRedisDomainRepostory redisRepostory;
private readonly ICacheService cacheService;
public RedisDomainService(IRedisDomainRepostory redisRepostory, ICacheService cacheService)
{
this.redisRepostory = redisRepostory;
this.cacheService = cacheService;
}
/// <summary>
/// 登录
/// </summary>
/// <param name="phone"></param>
/// <param name="code"></param>
/// <returns></returns>
public async Task<ResponseContent> Login(string phone, string code)
{
ResponseContent response = new ResponseContent();
try
{
//1.校验手机号
if (!RegexUtils.IsMobile(phone))
{
//2.如果不符合,返回错误信息
return response.Error(ResponseType.ParameterVerificationError);
}
//2.校验验证码
//string cacheCode = _session.GetString("code");
string cacheCode = cacheService.Get(RedisConstants.LOGIN_CODE_KEY + phone);
if (cacheCode == null || !cacheCode.Equals(code))
{
//3.不一致,报错
return response.Error(ResponseType.PINError);
}
//4.一致,根据手机号查询用户
User user = await redisRepostory.FindOneByMobile(phone);
//5.判断用户是否存在
if (user == null)
{
//6.不存在,创建新用户并保存
Random random = new Random();
user = await redisRepostory.Add(phone, "测试用户" + random.Next(100000, 999999).ToString());
}
//7.保证用户信息到session中
//_session.SetString("userMobile", user.Mobile);
//7.保证用户信息到Redis中
//7.1随机生成token,作为登录令牌
string token = Guid.NewGuid().ToString();
//7.2将User对象转为Dictionary存储
Dictionary<string, string> userDic = ObjectExtension.ModelToDic(user);
//7.3保存数据到Resdis
string tokenKey = RedisConstants.LOGIN_TOKEN_KEY + token;
cacheService.AddHash(tokenKey, userDic);
//7.4设置token有效期
cacheService.Expire(tokenKey, RedisConstants.LOGIN_TOKEN_TTL);
//8.返回token
return response.Ok(new { token = token });
}
catch (Exception e) {
return response.Error(404,e.Message);
}
}
}
}
以上实现了登录功能,但是用户名信息存储在Redis中,到达过期时间就会被自动删除。解决此问题的方法在需要登录才能访问的接口中,使用IAuthorizationFilter过滤器,过滤器中根据前台传的Token获取Redis中的用户信息,如果获取成功,则刷新过期时间。
1、IAuthorizationFilter实现代码如下:
using Common.CacheManager;
using Common.Const;
using Common.Dto;
using Common.Enum;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Common.Commons
{
/// <summary>
/// 自定义身份验证过滤器,实现登录及权限的验证
/// </summary>
public class CustomAuthorizeFilter : Attribute, IAuthorizationFilter
{
private readonly ICacheService cacheService;
public CustomAuthorizeFilter(ICacheService cacheService)
{
this.cacheService = cacheService;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//当调用的接口是加了[AllowAnonymous]特性的,绕过权限验证的
if (context.ActionDescriptor.EndpointMetadata.Any(item => item is IAllowAnonymous))
{
return;
}
//未登录处理
if (string.IsNullOrEmpty(context.HttpContext.Request.Headers["Authorization"]))
{
context.Result = new JsonResult(new ResponseContent().Error(ResponseType.LoginExpiration));
return;
}
//1.获取token
string token = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault();
//2.基于Token获取Redis中的用户
string tokenKey = RedisConstants.LOGIN_TOKEN_KEY + token;
UserDto user = cacheService.GetHash<UserDto>(tokenKey);
//3.判断用户是否存在
if (user == null) {
//4.不存在,拦截,返回401状态码
context.Result = new JsonResult(new ResponseContent().Error(ResponseType.LoginError));
return;
}
//5.刷新token有效期
cacheService.Expire(tokenKey, RedisConstants.LOGIN_TOKEN_TTL);
//6.放行
return;
}
}
}
2、接口Controllers代码
using Common.Commons;
using Domain.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
namespace RedisTest.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
[TypeFilter(typeof(CustomAuthorizeFilter))]
public class LoginController : ControllerBase
{
private readonly IRedisDomainService redisService;
public LoginController(IRedisDomainService redisService)
{
this.redisService = redisService;
}
[HttpPost]
public async Task<ResponseContent> GetUserInfo(string phone)
{
ResponseContent response = new ResponseContent();
return response.Ok(new { Message = "获取用户信息成功" });
}
[HttpPost]
[AllowAnonymous]
public async Task<ResponseContent> AllowAnonymousTest()
{
ResponseContent response = new ResponseContent();
return response.Ok(new { Message = "访问成功" });
}
}
}
以上代码保证了,只要用户不断访问需要登录的接口,Redis中存储的用户信息就不会过期。
作者初学.net core开发,有不对的地方欢迎评论区批评交流,后续将不断更新本教程。
using CSRedis;
using Newtonsoft.Json;
using System.Reflection;
namespace Common.CacheManager
{
public class RedisCacheService : ICacheService
{
public RedisCacheService()
{
var csredis = new CSRedisClient("127.0.0.1,Password=123456,SyncTimeout=15000");
RedisHelper.Initialization(csredis);
}
/// <summary>
/// 验证缓存项是否存在
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public bool Exists(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
return RedisHelper.Exists(key);
}
/// <summary>
/// List头部插入一个元素
/// </summary>
/// <param name="key"></param>
/// <param name="val"></param>
public void LPush(string key, string val)
{
RedisHelper.LPush(key, val);
}
/// <summary>
/// List尾部插入一个元素
/// </summary>
/// <param name="key"></param>
/// <param name="val"></param>
public void RPush(string key, string val)
{
RedisHelper.RPush(key, val);
}
/// <summary>
/// 从List尾部删除一个元素
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public T ListDequeue<T>(string key) where T : class
{
string value = RedisHelper.RPop(key);
if (string.IsNullOrEmpty(value))
return null;
return JsonConvert.DeserializeObject<T>(value);
}
/// <summary>
/// 从List尾部删除一个元素
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object ListDequeue(string key)
{
string value = RedisHelper.RPop(key);
if (string.IsNullOrEmpty(value))
return null;
return value;
}
/// <summary>
/// 移除list中的数据,keepIndex为保留的位置到最后一个元素如list 元素为1.2.3.....100
/// 需要移除前3个数,keepindex应该为4
/// </summary>
/// <param name="key"></param>
/// <param name="keepIndex"></param>
public void ListRemove(string key, int keepIndex)
{
RedisHelper.LTrim(key, keepIndex, -1);
}
/// <summary>
/// 添加一个String类型元素
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expireSeconds"></param>
/// <param name="isSliding"></param>
/// <returns></returns>
public bool Add(string key, string value, int expireSeconds = -1, bool isSliding = false)
{
return RedisHelper.Set(key, value, expireSeconds);
}
/// <summary>
/// 添加一个元素
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expireSeconds"></param>
/// <param name="isSliding"></param>
/// <returns></returns>
public bool AddObject(string key, object value, int expireSeconds = -1, bool isSliding = false)
{
return RedisHelper.Set(key, value, expireSeconds);
}
/// <summary>
/// 添加一个Hash类型元素
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expireSeconds"></param>
/// <param name="isSliding"></param>
/// <returns></returns>
public void AddHash(string key, Dictionary<string, string> value)
{
foreach (var item in value)
{
RedisHelper.HSet(key, item.Key, item.Value);
}
}
public T GetHash<T>(string key) where T : class
{
//获取所有键值
RedisScan<(string field, string value)> m_pair = RedisHelper.HScan(key, 0);
(string field, string value)[] items = m_pair.Items;
//构造新实例
T t = Activator.CreateInstance<T>();
try
{
//获得新实例类型
var Types = t.GetType();
//获得遍历类型的属性字段
foreach (PropertyInfo sp in Types.GetProperties())
{
//遍历Hash值
for (int i = 0; i < items.Length; ++i)
{
//判断属性名是否相同
if (items[i].field == sp.Name)
{
string propertyType = sp.PropertyType.ToString();
//获得s对象属性的值复制给d对象的属性
if (propertyType.Equals("System.Guid"))
sp.SetValue(t, Guid.Parse(items[i].value), null);
else
sp.SetValue(t, items[i].value, null);
}
}
}
}
catch (Exception ex)
{
throw ex;
}
return t;
}
public bool Expire(string key, int expireSeconds = -1)
{
return RedisHelper.Expire(key, expireSeconds);
}
/// <summary>
/// 删除缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public bool Remove(string key)
{
RedisHelper.Del(key);
return true;
}
/// <summary>
/// 批量删除缓存
/// </summary>
/// <param name="key">缓存Key集合</param>
/// <returns></returns>
public void RemoveAll(IEnumerable<string> keys)
{
RedisHelper.Del(keys.ToArray());
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public T Get<T>(string key) where T : class
{
return RedisHelper.Get<T>(key);
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public string Get(string key)
{
return RedisHelper.Get(key);
}
public void Dispose()
{
}
}
}
- .net core WebApi使用Redis教程(二)Redis缓存应用基础讲解(951)
- Redis教程一、Redis基础讲解及windows下安装、配置说明(699)
- .net core WebApi使用Redis教程(一)Redis代替Session实现登录(646)
- .net core WebApi使用Redis教程(三)Redis缓存应用缓存穿透概念及解决办法(552)
- .net core WebApi使用Redis教程(五)Redis缓存应用缓存击穿概念及解决办法(541)
- .net core WebApi使用Redis教程(四)Redis缓存应用缓存雪崩讲解(470)
- Redis教程二、Redis数据结构及常用命令详细介绍(449)
- Redis教程三、一图读懂Redis企业应用场景(436)
- 2025年3月 (1)
- 2024年6月 (2)
- 2024年5月 (2)
- 2024年4月 (4)
- 2024年3月 (30)
- 2024年1月 (4)
- 2023年12月 (2)
- 2023年11月 (4)
- 2023年10月 (4)
- 2023年9月 (6)
- 2023年3月 (2)
- 2023年2月 (1)
- 2023年1月 (1)
- 2022年12月 (1)
- 2022年9月 (21)
- 2022年8月 (10)
- 2022年7月 (3)
- 2022年4月 (1)
- 2022年3月 (13)
- 2021年8月 (1)
- 2021年3月 (1)
- 2020年12月 (42)
- 2020年11月 (7)
- 2020年10月 (5)
- 2020年8月 (1)
- 2020年6月 (1)
- 2020年3月 (2)
- 2019年12月 (8)
- 2019年11月 (3)
- 2019年9月 (1)
- 2019年4月 (1)
- 2019年3月 (6)
- 2019年2月 (1)
- 2018年7月 (7)
- 1.asp.net mvc内微信pc端、H5、JsApi支付方式总结(5880)
- 2.Windows 10休眠文件更改存储位置(3847)
- 3.各大搜索网站网站收录提交入口地址(3483)
- 4.ECharts仪表盘实例及参数使用详解(3438)
- 5.windows 10安装myeclipse 10破解补丁cracker.jar、run.bat闪退解决办法(3431)
- 6.HTML5 WebSocket与C#建立Socket连接实现代码(3182)
- 7.华为鸿蒙系统清除微信浏览器缓存方法(3182)
- 8.CERT_HAS_EXPIRED错误如何解决(2975)
- 9.Js异步async、await关键字详细介绍(lambda表达式中使用async和await关键字)(2609)
- 10.HBuilder编辑器格式化代码(2396)
