.Net6基于IdentityServer4搭建认证授权服务
新建一个名为
Ids4.Server
.Net6的空项目,引用包源IdentityServer4
添加Config配置类
using IdentityServer4.Models;
using static IdentityServer4.IdentityServerConstants;namespace Ids4.Server;public class Config
{public static IEnumerable<IdentityResource> IdentityResources =>new List<IdentityResource>{new IdentityResources.OpenId(),new IdentityResources.Profile()};public static IEnumerable<ApiScope> ApiScopes =>new ApiScope[]{new ApiScope("api2", "My Api2")};public static IEnumerable<Client> Clients =>new List<Client>{new Client{ClientId = "client_api2",// 要使用RefreshToken时,必须要把AllowedGrantTypes设置为授权代码、混合和资源所有者密码凭证流AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, //GrantTypes.ClientCredentials,ClientSecrets ={new Secret("secret".Sha256()) //secret加密密钥 Sha256加密方式},AllowedScopes ={"api2",StandardScopes.OfflineAccess,},// 刷新Token时RefreshToken保持不变RefreshTokenUsage = TokenUsage.ReUse,RefreshTokenExpiration = TokenExpiration.Sliding,// RefreshToken过期时间SlidingRefreshTokenLifetime = 3600,// 明确授权请求刷新令牌AllowOfflineAccess = true,// TOken过期时间AccessTokenLifetime = 60,}};
}
添加UserInfoModel
类,模拟数据库用户信息实体
namespace Ids4.Server;public class UserInfoModel
{public string Id { get; set; }public string Username { get; set; }public string Password { get; set; }public string Role { get; set; }
}
添加静态类UserData
,模拟数据库数据
namespace Ids4.Server;public static class UserData
{/// <summary>/// 模拟数据库存储的用户信息/// </summary>/// <returns></returns>public static List<UserInfoModel> GetListUsers(){return new List<UserInfoModel>{new UserInfoModel{Id = "1",Username = "zhangsan",Password = "123456",Role = "admin"},new UserInfoModel{Id = "2",Username = "lisi",Password = "123456",Role = "Test1,Test2"},new UserInfoModel{Id = "2",Username = "lisi",Password = "123456",Role = "Test2"}};}
}
添加ResourceOwnerPasswordValidator
类继承IResourceOwnerPasswordValidator
实现ValidateAsync
。该方法用于客户端请求获取Token时校验用户信息是否存在
using IdentityModel;
using IdentityServer4.Models;
using IdentityServer4.Validation;namespace Ids4.Server;public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context){// 根据username和password查询用户是否存在var user = UserData.GetListUsers().FirstOrDefault(p => p.Username == context.UserName && p.Password == context.Password);if (user != null){// 返回Id,为下一步获取角色权限做准备context.Result = new GrantValidationResult(user.Id, OidcConstants.AuthenticationMethods.Password);}else{context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid credentials");}}
}
添加ProfileService
类继承IProfileService
实现GetProfileDataAsync
。该方法用于将用户的角色信息添加到Token
using System.Security.Claims;
using IdentityModel;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Services;namespace Ids4.Server;public class ProfileService : IProfileService
{/// <summary>/// 根据Id拿到用户所属角色,添加到Token中/// </summary>/// <param name="context"></param>public async Task GetProfileDataAsync(ProfileDataRequestContext context){var user = UserData.GetListUsers().FirstOrDefault(p => p.Id == context.Subject.GetSubjectId());// 存在多个权限时var roleArr = user.Role.Split(",");var claims = new List<Claim>();foreach (var item in roleArr){claims.Add(new Claim(JwtClaimTypes.Role, item));}context.IssuedClaims = claims;}public async Task IsActiveAsync(IsActiveContext context){context.IsActive = true;}
}
添加RefreshTokenService
类继承IRefreshTokenService
实现CreateRefreshTokenAsync
、UpdateRefreshTokenAsync
、ValidateRefreshTokenAsync
三个方法
using System.Security.Claims;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Validation;namespace Ids4.Server;public class RefreshTokenService : IRefreshTokenService
{/// <summary>/// 模拟RefreshToken存在缓存中/// </summary>private readonly static Dictionary<string, RefreshToken> _refreshTokens = new Dictionary<string, RefreshToken>();/// <summary>/// 创建刷新token/// </summary>/// <param name="subject"></param>/// <param name="accessToken"></param>/// <param name="client"></param>/// <returns></returns>/// <exception cref="NotImplementedException"></exception>public async Task<string> CreateRefreshTokenAsync(ClaimsPrincipal subject, Token accessToken, Client client){var handle = Guid.NewGuid().ToString();var refreshToken = new RefreshToken{AccessToken = accessToken,CreationTime = DateTime.UtcNow,Lifetime = client.RefreshTokenExpiration == TokenExpiration.Sliding ? client.SlidingRefreshTokenLifetime : client.AbsoluteRefreshTokenLifetime,//Subject = subject,Version = 1};_refreshTokens[handle] = refreshToken;return await Task.FromResult(handle);}/// <summary>/// 修改刷新token/// </summary>/// <param name="handle"></param>/// <param name="refreshToken"></param>/// <param name="client"></param>/// <returns></returns>/// <exception cref="NotImplementedException"></exception>public async Task<string> UpdateRefreshTokenAsync(string handle, RefreshToken refreshToken, Client client){if (!_refreshTokens.ContainsKey(handle)){throw new ArgumentException("Invalid refresh token handle");}_refreshTokens[handle] = refreshToken;return await Task.FromResult(handle);}/// <summary>/// 验证刷新token/// </summary>/// <param name="token"></param>/// <param name="client"></param>/// <returns></returns>/// <exception cref="NotImplementedException"></exception>public async Task<TokenValidationResult> ValidateRefreshTokenAsync(string token, Client client){if (!_refreshTokens.TryGetValue(token, out var refreshToken)){return await Task.FromResult(new TokenValidationResult{IsError = true,Error = "Invalid refresh token"});}if (refreshToken.AccessToken.ClientId != client.ClientId){return await Task.FromResult(new TokenValidationResult{IsError = true,Error = "Refresh token does not belong to the client"});}if (DateTime.UtcNow > refreshToken.CreationTime.AddSeconds(refreshToken.Lifetime)){return await Task.FromResult(new TokenValidationResult{IsError = true,Error = "Refresh token has expired"});}return await Task.FromResult(new TokenValidationResult{IsError = false,RefreshToken = refreshToken});}
}
Program
注入
builder.Services.AddTransient<IRefreshTokenService, RefreshTokenService>();
builder.Services.AddIdentityServer().AddDeveloperSigningCredential().AddInMemoryApiScopes(Config.ApiScopes).AddInMemoryClients(Config.Clients).AddInMemoryIdentityResources(Config.IdentityResources).AddProfileService<ProfileService>().AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();app.UseIdentityServer();
项目结构
至此,基于IdentityServer4的认证授权服务就搭建完成了
使用postman
请求获取AccessToken
使用RefreshToken刷新AccessToken
解析后的Token
源码地址:.git
更多推荐
.Net6基于IdentityServer4搭建认证授权服务
发布评论