Skip to content

[TOC]

Redis缓存实现单点登录

1.安装包

shell
dotnet add package StackExchange.Redis
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package System.IdentityModel.Tokens.Jwt

注意版本

2.配置 appsettings.json

json
,
  "Redis": {
    "ConnectionString": "localhost:6379,password=,defaultDatabase=0"
  }

3. Redis 相关函数

C
private readonly IDatabase _redis;  
private readonly IDatabase _redis; 
public LoginController( IConnectionMultiplexer redis)
{ 
    this._redis = redis.GetDatabase();
}
var oldJwt = await _redis.StringSetAsync($"key"); ///设置/创建key
var oldJwt = await _redis.StringGetAsync($"key"); ///获取key:key 的value   
var oldJwt = await _redis.KeyDeleteAsync($"key"); ///删除key
if (!await _redis.KeyExistsAsync($"key"))  //判断key是否存在

4. 配置

C
      #region redis

      // Redis 注册
      var redisConn = ConnectionMultiplexer.Connect(builder.Configuration["Redis:ConnectionString"]);
      builder.Services.AddSingleton<IConnectionMultiplexer>(redisConn); 
      #endregion

5.示例

C
        [HttpPost]
        public async Task<IActionResult> Login([FromBody] LoginDTO dto,[FromServices] IConfiguration config)
        {
            var users =  DbContext.Set<Users>().SingleOrDefault(a => a.Name == dto.name);
            if(users == null)
            {
                return NotFound("用户不存在");
            }
            if(users.Password != dto.password)
            {
                return Unauthorized("用户名或密码错误");
            }
            #region 生成 JWT  求时间长度ttl,JWTID 
            // 1. 生成 JWT 
            var token =  JwtHelper.GenerateToken(config, users.Id, dto.name );
            var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(token);///ReadJwtToken 将token解析一下成为jwt对象
            var JWTID = jwtToken.Claims.First(c => c.Type == JwtRegisteredClaimNames.Jti).Value;///JWT ID(jti) 之前生成jwt 存储的数据
            var ttl = jwtToken.ValidTo - DateTime.UtcNow;///距离过期还有多长时间  过期时间,时间长度(ValidTo) 
            var sessionData = new { users.Id, dto.name };//, roles };
            var jsonData = System.Text.Json.JsonSerializer.Serialize(sessionData);
            #endregion  

            #region 存储会话到 Redis,key = session:{JWTID} 
            var oldJwtID = await _redis.StringGetAsync($"token:userID:{users.Id}");
            var oldJwt = await _redis.StringGetAsync($"token:session:{oldJwtID}");
            if (!string.IsNullOrEmpty(oldJwt))
            {
                await _redis.KeyDeleteAsync($"token:session:{oldJwtID}");
            }
            await _redis.StringSetAsync($"token:userID:{users.Id}", JWTID, ttl);
            await _redis.StringSetAsync($"token:session:{JWTID}", jsonData, ttl);

            #endregion 
            return Ok(new { token=token, users });  
        }

6. 过滤器

C
using System.IdentityModel.Tokens.Jwt; 
using System.Security.Claims;
using demo1.Uitl.Redis;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;


using System.IdentityModel.Tokens.Jwt;
using AutoMapper;
using Azure.Core;
using demo1.Mode;
using demo1.Uitl;
using demo1.Uitl.JWT;
using demo1.Uitl.Redis;
using DTO.LoginControllerDTO;
using Microsoft.AspNetCore.Mvc;
using StackExchange.Redis;

namespace demo1.Filter
{
    public class RedisSessionFilter : IAsyncAuthorizationFilter
    {

        private readonly IDatabase _redis;
        private readonly IConnectionMultiplexer _redis1;
        public RedisSessionFilter(IRedisCacheService redisCache, IConnectionMultiplexer redis)
        { 
            this._redis = redis.GetDatabase();
            this._redis1 = redis ;
        }


        public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            // 1. 获取当前用户
            var user = context.HttpContext.User; 
            // 1. 先判断 user 对象本身是否存在
            if (user == null)
            {
                return;
            }

            // 2. 再判断 Identity 属性是否存在
            if (user.Identity == null)
            {
                return;
            }

            // 3. 最后判断是否已认证
            if (!user.Identity.IsAuthenticated)
            {
                return;
            }

            // 2. 从 JWT 中提取 jti
            var jti = user.FindFirstValue(JwtRegisteredClaimNames.Jti);
            if (string.IsNullOrEmpty(jti))
            {
                context.Result = new UnauthorizedResult();
                return;
            }
             
            // 3. 检查 Redis 中是否存在该会话
            var sessionKey =  $"token:session:{jti}" ;
            if (!await _redis.KeyExistsAsync(sessionKey))
            { 
                context.Result = new UnauthorizedObjectResult($@"{user.GetUserId()}+{user.GetUserName()}+会话已过期,请重新登录");
                return;
            }
            // 如果存在,不做任何操作,继续执行
        }
    }
}