[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);
#endregion5.示例
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;
}
// 如果存在,不做任何操作,继续执行
}
}
}