Identity Server 4官方文档:https://identityserver4.readthedocs.io/en/latest/

新建2个asp.net core 项目使用空模板

Auth 身份认证服务

Client客户端

Auth项目打开安装相关依赖 

IdentityServer4 和 EF 实体框架

Mysql EF Provider

Nlog日志组件

打开Startup.cs 文件配置 管道

配置 identity server

还是Startup.cs,编辑ConfigureServices方法:

添加Nlog日志

  services.AddLogging(logBuilder =>
{
logBuilder.AddNLog();
});

日志配置文件(右键属性选择始终复制)

日志配置内容

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<extensions>
<add assembly="Nlog.Targets.Splunk"/>
</extensions> <targets async="true">
<target name="asyncFile" xsi:type="File"
layout="[${longdate}] [${level}] [${logger}] [${message}] ${newline} ${exception:format=tostring}"
fileName="${basedir}/log/${shortdate}.txt"
archiveFileName="${basedir}/log/archives/log.{#####}.txt"
archiveAboveSize="102400000"
archiveNumbering="Sequence"
concurrentWrites="true"
keepFileOpen="false"
encoding="utf-8" /> <target name="console" xsi:type="console"/>
</targets> <rules>
<!--Info,Error,Warn,Debug,Fatal-->
<logger name="*" levels="Info,Error,Warn,Debug,Fatal" writeTo="asyncFile" />
<logger name="*" minlevel="Error" writeTo="console" /> </rules>
</nlog>

添加IdentityServer服务

services.AddIdentityServer()

添加身份验证方式

  services.AddIdentityServer()
.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()

ResourceOwnerPasswordValidator 类实现接口 IResourceOwnerPasswordValidator

实现验证ValidateAsync异步方法

 public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
try
{
//根据context.UserName和context.Password与数据库的数据做校验,判断是否合法
if (context.UserName == "wjk" && context.Password == "")
{
context.Result = new GrantValidationResult(
subject: context.UserName,
authenticationMethod: "custom",
claims: GetUserClaims());
}
else
{ //验证失败
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential");
}
}
catch (System.Exception exception)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid username or password");
throw;
}
}

设置Claims

private static IEnumerable<Claim> GetUserClaims()
{
return new[]
{
new Claim("uid", ""),
new Claim(JwtClaimTypes.Name,"wjk"),
new Claim(JwtClaimTypes.GivenName, "GivenName"),
new Claim(JwtClaimTypes.FamilyName, "yyy"),
new Claim(JwtClaimTypes.Email, "977865769@qq.com"),
new Claim(JwtClaimTypes.Role,"admin")
};
}

设置验证方式

 services.AddIdentityServer()
.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
.AddSigningCredential(cert)

使用pfx 证书验证

cert证书读取

 var certAbsolutePath = PathResolver.ResolveCertConfigPath(Configuration["AuthOption:X509Certificate2FileName"], Environment);
var cert = new X509Certificate2(certAbsolutePath, Configuration["AuthOption:X509Certificate2Password"]);
public static class PathResolver
{
public static string ResolveCertConfigPath(string configPath, IHostingEnvironment environment)
{
var start = new[] { "./", ".\\", "~/", "~\\" };
if (start.Any(d => configPath.StartsWith(d)))
{
foreach (var item in start)
{
configPath = configPath.TrimStart(item);
}
return Path.Combine(environment.ContentRootPath, configPath);
}
return configPath;
} public static string TrimStart(this string target, string trimString)
{
if (string.IsNullOrEmpty(trimString)) return target; string result = target;
while (result.StartsWith(trimString))
{
result = result.Substring(trimString.Length);
} return result;
}
}

appsettings.json配置文件

  "AuthOption": {
"X509Certificate2FileName": "./work.pfx",
"X509Certificate2Password": ""
}

证书生成方式:https://www.cnblogs.com/liuxiaoji/p/10790057.html

EF注入

var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
 services.AddIdentityServer()
.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
.AddSigningCredential(cert)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b =>
b.UseMySql(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
}).AddOperationalStore(options =>
{
options.ConfigureDbContext = b =>
b.UseMySql(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
options.EnableTokenCleanup = true;
});

数据库配置初始化

  /// <summary>
/// 配置
/// </summary>
public class Config
{
/// <summary>
/// 定义身份资源
/// </summary>
/// <returns>身份资源</returns>
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new IdentityResource[]
{
//身份资源是用户的用户ID
new IdentityResources.OpenId()
};
} /// <summary>
/// 定义API资源
/// </summary>
/// <returns>API资源</returns>
public static IEnumerable<ApiResource> GetApiResources()
{
return new[]
{
new ApiResource("api1", new[] {"uid", JwtClaimTypes.Name}),
new ApiResource("api2", new[] { "uid", "name"})
};
} /// <summary>
/// 定义客户端
/// </summary>
/// <returns>客户端</returns>
public static IEnumerable<Client> GetClients()
{
return new[]
{
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowOfflineAccess = true,
ClientSecrets = {new Secret("secret".Sha256())},
AllowedScopes = {OidcConstants.StandardScopes.OfflineAccess, "api1", "api2"}
}
};
}
}
 private void InitializeDatabase(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
/*
*切换到程序包管理器控制器,将默认项目设置为Auth,然后输入以下命令
*PersistedGrantDbContext
*Add-Migration -Name InitialIdentityServerConfigurationDbMigration -Context PersistedGrantDbContext -OutputDir Migrations/IdentityServer/PersistedGrantDb -Project Auth
*ConfigurationDbContext
*Add-Migration -Name InitialIdentityServerConfigurationDbMigration -Context ConfigurationDbContext -OutputDir Migrations/IdentityServer/ConfigurationDb -Project Auth
*命令将使用ConfigurationDbContext创建一个名为InitialIdentityServerConfigurationDbMigration 的迁移。迁移文件将输出到将默认项目设置为Auth项目的Migrations/IdentityServer/ConfigurationDb文件夹。
*/
// 必须执行数据库迁移命令才能生成数据库表
serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
context.Database.Migrate();
if (!context.Clients.Any())
{
foreach (var client in Config.GetClients())
{
context.Clients.Add(client.ToEntity());
}
context.SaveChanges();
} if (!context.IdentityResources.Any())
{
foreach (var resource in Config.GetIdentityResources())
{
context.IdentityResources.Add(resource.ToEntity());
}
context.SaveChanges();
} if (!context.ApiResources.Any())
{
foreach (var resource in Config.GetApiResources())
{
context.ApiResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
}
}
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
InitializeDatabase(app);
app.UseIdentityServer();
}

EF数据库迁移

 var connectionString = Configuration.GetConnectionString("Auth");
 "ConnectionStrings": {
"Auth": "Server=localhost;database=auth;uid=root;pwd=123456;charset=UTF8;"
},

执行命令

 切换到程序包管理器控制器,将默认项目设置为Auth,然后输入以下命令
PersistedGrantDbContext
Add-Migration -Name InitialIdentityServerConfigurationDbMigration -Context PersistedGrantDbContext -OutputDir Migrations/IdentityServer/PersistedGrantDb -Project Auth
ConfigurationDbContext
Add-Migration -Name InitialIdentityServerConfigurationDbMigration -Context ConfigurationDbContext -OutputDir Migrations/IdentityServer/ConfigurationDb -Project Auth
命令将使用ConfigurationDbContext创建一个名为InitialIdentityServerConfigurationDbMigration 的迁移。迁移文件将输出到将默认项目设置为Auth项目的Migrations/IdentityServer/ConfigurationDb文件夹。

Client打开添加相关配置

IdentityServer4 和 EF 实体框架

Nlog日志组件

类型转化框架

打开Startup.cs 文件配置 管道

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//添加日志
loggerFactory.AddNLog(); //添加权限验证
app.UseAuthentication(); //添加MVC框架
app.UseMvc();
}
  public void ConfigureServices(IServiceCollection services)
{
//加入HttpClient
services.AddHttpClient(); var config = Configuration.GetSection("JwtBearerOptions").Get<JwtBearerOptions>();
services.Configure<JwtBearerOptions>(Configuration.GetSection("JwtBearerOptions"));
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(option =>
{
option.RequireHttpsMetadata = false;
option.Authority = config.Authority;
option.RequireHttpsMetadata = config.RequireHttpsMetadata;
option.Audience = config.Audience;
}); services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(option => { option.UseCamelCasing(true); });
}

配置文件

  "JwtBearerOptions": {
"Authority": "http://localhost:61491",
"RequireHttpsMetadata": false,
"Audience": "api1"
}

添加AccountController 用于登录 和刷新Toekn

    [AllowAnonymous]
[Route("api/[controller]")]
public class AccountController: ControllerBase
{
/// <summary>
/// http客户端
/// </summary>
private readonly HttpClient _client; /// <summary>
/// 日志
/// </summary>
private readonly ILogger<AccountController> _logger; /// <summary>
/// 身份认证配置
/// </summary>
private readonly JwtBearerOptions _authOption; public AccountController(
IHttpClientFactory httpClientFactory,
ILogger<AccountController> logger,
IOptions<JwtBearerOptions> authOptions)
{
_client = httpClientFactory.CreateClient();
_logger = logger;
_authOption = authOptions.Value;
} [NonAction]
private async Task<LoginResult> GenerateTokenAsync(string account, string password, string enterpriseId)
{
var disco = await _client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
{
Address = _authOption.Authority,
Policy =
{
RequireHttps = false
}
});
if (disco.IsError)
{
var msg = $"用户{account}登陆到注册中心出错,错误码{disco.StatusCode},详情{disco.Json}";
_logger.LogError(msg);
throw new InvalidOperationException(msg);
} var parameters = new Dictionary<string, string>
{
{ ClaimsConst.EnterpriseId, enterpriseId }
}; // request token
var tokenResponse = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
UserName = account,
Password = password,
Parameters = parameters
}); if (tokenResponse.IsError)
{
var msg = $"用户{account}获取token出错,错误码{tokenResponse.HttpStatusCode},详情{tokenResponse.Json}";
_logger.LogError(msg);
throw new InvalidOperationException(msg);
} return new LoginResult
{
AccessToken = tokenResponse.AccessToken,
RefreshToken = tokenResponse.RefreshToken,
TokenType = tokenResponse.TokenType,
ExpiresIn = tokenResponse.ExpiresIn
};
} /// <summary>
/// 登录
/// </summary>
/// <param name="login">登录信息</param>
/// <returns>结果</returns>
[HttpPost("login")]
public async Task<ActionResult<LoginResult>> Login([FromBody]Login login)
{
var result = await GenerateTokenAsync(login.Account, login.Password, "自定义参数");
if (result != null)
{
return Ok(result);
}
return Unauthorized();
} /// <summary>
/// 用refresh token获取新的access token
/// </summary>
/// <param name="token">refresh token</param>
/// <returns></returns>
[HttpGet("refresh/{token}")]
public async Task<ActionResult<LoginResult>> Refresh(string token)
{
var disco = await _client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
{
Address = _authOption.Authority,
Policy =
{
RequireHttps = false
}
});
if (disco.IsError)
{
_logger.LogError($"获取刷新token出错,错误码{disco.StatusCode},详情{disco.Json}");
return null;
} var tokenResponse = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
RefreshToken = token,
Scope = OidcConstants.StandardScopes.OfflineAccess,
}); if (tokenResponse.IsError)
{
_logger.LogError($"获取刷新token出错,错误码{tokenResponse.HttpStatusCode},详情{tokenResponse.Json}");
return Unauthorized(new { Code = , Msg = "刷新token已过期" });
} var tokenResult = tokenResponse.MapTo<LoginResult>();
return Ok(tokenResult);
}
}

添加 AuthController 用于测试token 身份验证

    [Authorize]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
public ActionResult<string> Get()
{
return Ok("授权成功");
}
}

相关实体类

  public class ClaimsConst
{
public const string UserId = "uid";
public const string UserName = "uname";
public const string EnterpriseId = "eid";
public const string DepartmentId = "did";
public const string Subject = "sub";
public const string Expires = "exp";
public const string Issuer = "iss";
public const string Audience = "aud";
public const string IssuedAt = "iat";
public const string ClientId = "client_id";
public const string LoginStrategy = "lgs";
public const string AuthTime = "auth_time";
public const string Idp = "idp";
public const string NotBefore = "nbf";
public const string UserData = "udata";
public const string DeviceCode = "dcd";
}
    public class Login
{
/// <summary>
/// 帐号
/// </summary>
[Required(ErrorMessage = "帐号不能为空")]
[MaxLength(, ErrorMessage = "帐号长度最长为20位字符")]
public string Account { get; set; } /// <summary>
/// 密码
/// </summary>
[Required(ErrorMessage = "密码不能为空")]
[MaxLength(, ErrorMessage = "密码长度最长为40位字符")]
public string Password { get; set; }
}
    public class LoginResult
{
/// <summary>
/// token
/// </summary>
public string AccessToken { get; set; } /// <summary>
/// 刷新token
/// </summary>
public string RefreshToken { get; set; } /// <summary>
/// token过期时间
/// </summary>
public int ExpiresIn { get; set; } /// <summary>
/// token类型
/// </summary>
public string TokenType { get; set; }
}

使用postman 测试

登录

刷新token

访问API

使用正确的Token

IdentitiServser4 + Mysql实现Authorization Server的更多相关文章

  1. Spring Authorization Server(AS)从 Mysql 中读取客户端、用户

    Spring AS 持久化 jdk version: 17 spring boot version: 2.7.0 spring authorization server:0.3.0 mysql ver ...

  2. Spring Authorization Server 实现授权中心

    Spring Authorization Server 实现授权中心 源码地址 当前,Spring Security 对 OAuth 2.0 框架提供了全面的支持.Spring Authorizati ...

  3. MySQL提示:The server quit without updating PID file问题的解决办法(转载)

    MySQL提示:The server quit without updating PID file问题的解决办法 今天网站web页面提交内容到数据库,发现出错了,一直提交不了,数找了下原因,发现数据写 ...

  4. [转]MySQL: Starting MySQL….. ERROR! The server quit without updating PID file

    转自: http://icesquare.com/wordpress/mysql-starting-mysql-error-the-server-quit-without-updating-pid-f ...

  5. mysql 启动错误-server PID file could not be found

    [root@centos var]# service mysqld stop MySQL manager or server PID file could not be found!       [F ...

  6. MySQL: Starting MySQL….. ERROR! The server quit without updating PID file解决办法

    MySQL: Starting MySQL….. ERROR! The server quit without updating PID file解决办法 1 问题 [root@localhost m ...

  7. centos6.6下编译安装mysql5.6之后启动失败:Starting MySQL... ERROR! The server quit without updating PID file (/var/lib/mysql/localhost.localdomain.pid).

    今天在编译安装mysql5.6时候出现Starting MySQL... ERROR! The server quit without updating PID file (/var/lib/mysq ...

  8. MySQL.. ERROR! The server quit without updating PID file问题解决

    不小心将服务器OS给重启了,再启动数据库的时候,出现了很奇怪的问题 [root@dev run]# service mysql restart ERROR! MySQL server PID file ...

  9. Oracle、MySql、Sql Server比对

    1.    价格 MySql:廉价(部分免费):当前,MySQL採用双重授权(DualLicensed),他们是GPL和MySQLAB制定的商业许可协议.假设你在一个遵循GPL的自由(开源)项目中使用 ...

随机推荐

  1. SpringMVC接收请求参数和页面传参

    接收请求参数: 1,使用HttpServletRequest获取 @RequestMapping("/login.do") public String login(HttpServ ...

  2. 安装ubuntu是所需的引导

    title Install Ubuntu root (hd0,0) kernel (hd0,0)/vmlinuz boot=casper iso-scan/filename=/ubuntu-16.04 ...

  3. 解决Vue在IE中报错出现不支持=>等ES6语法和“Promise”未定义等问题

    在做VUE项目中大家可能会发现除了IE内核浏览器之外项目都能正常显示,但是到IE就萎了,这主要是IE不支持ES6的原因. 要解决这个我们要先引入browser.js,这样你可以使用ES2015(jav ...

  4. 微服务一键启动脚本shell带有环境变量的

    etting####################################################### #程序代码数组APPS=(cAssistantbussiness cAssi ...

  5. MYSQL 中 MyISAM与InnoDB两者之间区别与选择,详细总结,性能对比

    1.MyISAM:默认表类型,它是基于传统的ISAM类型,ISAM是Indexed Sequential Access Method (有索引的顺序访问方法) 的缩写,它是存储记录和文件的标准方法.不 ...

  6. 小D课堂 - 零基础入门SpringBoot2.X到实战_第14节 高级篇幅之SpringBoot多环境配置_59、SpringBoot多环境配置介绍和项目实战

    笔记 1.SpringBoot多环境配置介绍和项目实战(核心知识)     简介:SpringBoot介绍多环境配置和使用场景 1.不同环境使用不同配置         例如数据库配置,在开发的时候, ...

  7. kubernetes学习:CKA考试认证

    考点 CKA认证针对考核成为当业界的Kubernetes管理员所需的技能. CKA认证考试包括这些一般领域及其在考试中的权重: 应用程序生命周期管理 -  8% 安装.配置和验证 -  12% 核心概 ...

  8. IDEA 常用插件及快捷键总结

    现在开发中和日常自己开发都统一换成了 IDEA 进行开发了.现在针对自己常用到的插件和快捷键进行总结记录下. 插件 Alibaba Java Coding Guidelines:阿里巴巴编码规约 Gr ...

  9. Linux0.11之进程0创建进程1(1)

    进程0是由linus写在操作系统文件中的,是预先写死了的.那么进程0以后的进程是如何创建的呢?本篇文章主要讲述进程0创建进程1的过程. 在创建之前,操作系统先是进行了一系列的初始化,分别为设备号.块号 ...

  10. EF Code First 学习笔记:约定配置 Data Annotations+Fluent API

    要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就 ...