本文基于.net core webapi进行讲解,使用CSRedis库操作Redis,实现项目的登录模块。登录方式使用手机号、短信验证码进行登录。登录过程包含获取短信验证码、判断用户输入手机号及短信验证码是否正确及刷新登录有效期3个环节。
用户登录第一步先输入手机号码,并获取验证码,后台生成验证码后存储到Redis中,并设置有效期,验证码用字符串格式存储在Redis中,为确保多用户同时发送验证码时验证码唯一,将使用手机号作为Redis的Key。发送手机验证码接口相关代码实现如下:
1、接口Controllers代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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、短信验证码发送服务代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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、用户登录验证服务代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 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实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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开发,有不对的地方欢迎评论区批评交流,后续将不断更新本教程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | 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缓存应用基础讲解(848)
- Redis教程一、Redis基础讲解及windows下安装、配置说明(620)
- .net core WebApi使用Redis教程(一)Redis代替Session实现登录(541)
- .net core WebApi使用Redis教程(五)Redis缓存应用缓存击穿概念及解决办法(480)
- .net core WebApi使用Redis教程(三)Redis缓存应用缓存穿透概念及解决办法(468)
- Redis教程二、Redis数据结构及常用命令详细介绍(389)
- .net core WebApi使用Redis教程(四)Redis缓存应用缓存雪崩讲解(385)
- Redis教程三、一图读懂Redis企业应用场景(358)
- 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支付方式总结(5719)
- 2.Windows 10休眠文件更改存储位置(3224)
- 3.各大搜索网站网站收录提交入口地址(3218)
- 4.ECharts仪表盘实例及参数使用详解(3113)
- 5.windows 10安装myeclipse 10破解补丁cracker.jar、run.bat闪退解决办法(3034)
- 6.HTML5 WebSocket与C#建立Socket连接实现代码(2905)
- 7.华为鸿蒙系统清除微信浏览器缓存方法(2825)
- 8.CERT_HAS_EXPIRED错误如何解决(2336)
- 9.Js异步async、await关键字详细介绍(lambda表达式中使用async和await关键字)(2222)
- 10.HBuilder编辑器格式化代码(2135)