IdentityServer4 Reference Token 刷新token Postman截图

.NET Core OAuth IdentityServer4 Token    
OAuth 身份认证 IdentityServer4  
IdentityServer4 1.0.0 documentation(官网)    
IdentityServer4 中文文档(v1.0.0) 第一部分 简介、第二部分 快速入门、第三部分 主题、第四部分 参考、第五部分 端点、第六部分 其它  

当使用 Reference Token 的时候,服务端会对 Token 进行持久化,当客户端请求资源服务器(API)的时候,资源服务器需要每次都去服务通信去验证 Token 的合法性[/connect/introspect],IdentityServer4.AccessTokenValidation 中间件中可以配置缓存一定的时候去验证,并且 Token 是支持撤销[/connect/revocation]的,登录注销[/connect/endsession]

How can we revoke an access token
1、Open the IdentityServerWithAspIdAndEF project, specify the AccessTokenType Property of the Client:
     指定客户端的AccessTokenType属性,AccessTokenType = AccessTokenType.Reference
2、Specify the ApiSecrets property of the ApiResource.
     指定ApiResource的ApiSecrets属性
3、Open the Api project, update the token validation configuration. Specify the ApiSecret we configured before.
     打开Api项目,更新令牌验证配置。指定我们之前配置的apisecrite。

注意 ApiResource 赋值,ApiResource 推荐使用构造函数赋值,不推荐使用“属性”赋值
推荐

public static IEnumerable<ApiResource> GetApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource("ClientService", "CAS Client Service")
        {
            ApiSecrets = { new Secret("ClientSecret".Sha256()) }
        }
    };
}

不推荐

public static IEnumerable<ApiResource> GetApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource
        {
            Name = "CustomAPI",
            DisplayName = "Custom API",
            Description = "Custom API Access",
            UserClaims = new List<string> {"role"},
            ApiSecrets = new List<Secret> {new Secret("scopeSecret".Sha256())}
        }
    };
}

*
1、Startup.cs

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace AuthService
{
    public class Startup
    {
        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            Configuration = configuration;

            var builder = new ConfigurationBuilder()
               .SetBasePath(env.ContentRootPath)
               .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
               .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
               .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            string dbType = Configuration["DbSql:DbType"];
            switch (dbType)
            {
                case "SqlServer":
                    //services.AddSingleton(Configuration.GetConnectionString("DefaultConnection"));
                    services.AddSingleton(Configuration["DbSql:SqlServerConnection"]);
                    break;
                case "Oracle":
                    services.AddSingleton(Configuration["DbSql:OracleConnection"]);
                    break;
                case "MySql":
                    services.AddSingleton(Configuration["DbSql:MySqlConnection"]);
                    break;
                default:
                    break;
            }
            services.AddSingleton<IDbConnection, SqlConnection>();
            services.AddSingleton<IUserRepository, UserRepository>();

            services.AddIdentityServer()
                 .AddDeveloperSigningCredential()
                 .AddInMemoryClients(Security.Clients.GetClients())
                 .AddInMemoryIdentityResources(Security.IdentityConfig.GetIdentityResources())
                 .AddInMemoryApiResources(Security.IdentityConfig.GetResources())
                 .AddProfileService<Security.ProfileService>()
                 .AddResourceOwnerValidator<Security.ResourceOwnerPasswordValidator>();

            services.AddControllers();           
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else 
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseIdentityServer();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

2、Clients.cs(Client)

using AuthService.CoreLibrary;
using IdentityServer4;
using IdentityServer4.Models;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AuthService.Security
{
    public class Clients
    {
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientId = ConfigManager.Configuration["IdentityAuthentication:ClientId"],
                    ClientName = ConfigManager.Configuration["IdentityAuthentication:ClientName"],
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                    AccessTokenType = AccessTokenType.Reference,
                    AccessTokenLifetime = Convert.ToInt32(ConfigManager.Configuration["IdentityAuthentication:AccessTokenLifetime"]),
                    AbsoluteRefreshTokenLifetime =  Convert.ToInt32(ConfigManager.Configuration["IdentityAuthentication:AbsoluteRefreshTokenLifetime"]),//30*24*60*60
                    SlidingRefreshTokenLifetime = Convert.ToInt32(ConfigManager.Configuration["IdentityAuthentication:AbsoluteRefreshTokenLifetime"]),//5*60,
                    RefreshTokenExpiration = TokenExpiration.Sliding,//Absolute固定周期和Sliding滑动周期,前者到指定时间就会过期,后者则每次刷新Token时会延期
                    UpdateAccessTokenClaimsOnRefresh = true,
                    AllowOfflineAccess = true,
                    //AllowedCorsOrigins = { "http://localhost:5003","http://localhost:5005" },
                    ClientSecrets = { new Secret(ConfigManager.Configuration["IdentityAuthentication:Secret"].Sha256()) },
                    AllowedScopes =
                    {
                        ConfigManager.Configuration["IdentityAuthentication:Scope"],
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        IdentityServerConstants.StandardScopes.OfflineAccess,
                    },
                }
            };
        }
    }
}

*
3、IdentityConfig.cs(ApiResource)

using AuthService.CoreLibrary;
using IdentityServer4.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AuthService.Security
{
    public class IdentityConfig
    {
        public static IEnumerable<ApiResource> GetResources()
        {
            return new List<ApiResource>
            {
                new ApiResource(ConfigManager.Configuration["IdentityAuthentication:Scope"],ConfigManager.Configuration["IdentityAuthentication:ClientName"])
                {
                    ApiSecrets = { new Secret(ConfigManager.Configuration["IdentityAuthentication:Secret"].Sha256()) }
                }
            };
        }

        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new IdentityResource[]
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            };
        }
    }
}

4、ResourceOwnerPasswordValidator.cs()

using IdentityModel;
using IdentityServer4.Models;
using IdentityServer4.Stores;
using IdentityServer4.Validation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace AuthService.Security
{
    public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
    {
        string device_id = string.Empty;
        private readonly IReferenceTokenStore _referenceToken;
        private readonly IPersistedGrantStore _persistedGrant;
        private readonly IUserRepository _userRepo;

        public ResourceOwnerPasswordValidator(IReferenceTokenStore referenceToken, IPersistedGrantStore persistedGrant, IUserRepository userRepo)
        {
            _referenceToken = referenceToken;
            _persistedGrant = persistedGrant;
            _userRepo = userRepo;
        }

        public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
            var validatedRequestDictionary = context.Request.Raw.AllKeys.ToDictionary(s => s, s => context.Request.Raw[s]);

            var user = _userRepo.Login(context.UserName);
            if (user == null || context.Password != user.password)
            {
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential");
            }
            else
            {
                device_id = validatedRequestDictionary["device_id"];
                await Task.Run(() =>
                {
                    context.Result = new GrantValidationResult(
                                       subject: user.user_code.ToString(),
                                       authenticationMethod: "custom",
                                       claims: GetUserClaims(user));
                });
                await _referenceToken.RemoveReferenceTokensAsync(user.user_code.ToString(), context.Request.ClientId);
                await _persistedGrant.RemoveAllAsync(user.user_code.ToString(), context.Request.ClientId);
            }
        }

        private Claim[] GetUserClaims(UserDto user)
        {
            return new Claim[]
            {
                new Claim("user_code", user.user_code.ToString()),
                new Claim("user_name", user.user_name.ToString()),
                new Claim("real_name", user.real_name.ToString()),
                new Claim("device_id", device_id),
                new Claim(JwtClaimTypes.Name,user.user_name),
                new Claim(JwtClaimTypes.Role, user.role == null ? "customer" : user.role)
            };
        }
    }
}

*
5、ProfileService.cs(IProfileService)

using IdentityServer4.Models;
using IdentityServer4.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AuthService.Security
{
    public class ProfileService : IProfileService
    {
        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            try
            {
                if (!string.IsNullOrEmpty(context.Subject.Identity.Name))
                {
                }

                //depending on the scope accessing the user data.
                var claims = context.Subject.Claims.ToList();

                //set issued claims to return
                context.IssuedClaims = claims.ToList();
            }
            catch (Exception ex)
            {
                //log your error
            }
        }

        public async Task IsActiveAsync(IsActiveContext context)
        {
            context.IsActive = true;
        }
    }
}

6、appsettings.json

{
  "AdminSafeList": "127.0.0.1;192.168.1.5;::1",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AppSettings": {
    "SecretKey": "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789",
    "Cores": "https://localhost:44390,http://0.0.0.0:3201",
    "UploadFilesRootPath": "F:\\uploadfiles"
  },
  "IdentityAuthentication": {
    "ClientId": "MobileNurse",
    "ClientName": "MobileNurse App",
    "Scope": "MOBILE_PAD",
    "Secret": "ClientSecret",
    "AccessTokenLifetime": "3600",
    "AbsoluteRefreshTokenLifetime": "2592000",
    "SlidingRefreshTokenLifetime": "300"
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=.;Database=dbMobileNurse;User ID=sa;Password=000000;"
  },
  "DbSql": {
    "DbType": "SqlServer",
    "SqlServerConnection": "Server=.;Database=dbMobileNurse;User ID=sa;Password=000000;",
    "OracleConnection": "",
    "MySqlConnection": "Data Source=192.168.2.100;port=3306;Initial Catalog=dbMobileNurse;user id=root;password=000000;",
    "user": {
      "login": "select user_code,user_name,real_name,password from sys_user where user_name=@UserName ",
      "insert": "",
      "update": "",
      "select": ""
    }
  }
}

API 资源服务器 Startup.cs
NuGet:IdentityServer4.AccessTokenValidation

AddIdentityServerAuthentication、AddOAuth2Introspection

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.IO;
using AutoMapper;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Microsoft.AspNetCore.Mvc;
using IdentityModel;
using log4net;
using log4net.Config;
using log4net.Repository;
using System.Net.Mime;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Versioning;

namespace ***.WebAPI
{
    public class Startup
    {
        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            Configuration = configuration;

            var builder = new ConfigurationBuilder()
               .SetBasePath(env.ContentRootPath)
               .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
               .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
               .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfiguration Configuration { get; }
        public static ILoggerRepository loggerRepository { get; set; }

        readonly string AllowSpecificOrigins = "AllowSpecificOrigins";

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            #region 认证   第 1 步
            services.AddAuthentication(config =>
            {
                config.DefaultScheme = "Bearer";
            })
            .AddIdentityServerAuthentication(options =>
            {
                options.Authority = Configuration["IdentityAuthentication:Authority"];
                options.ApiName = "MOBILE_PAD";
                options.ApiSecret = "ClientSecret";
                options.RequireHttpsMetadata = true;//如果授权认证服务器访问地址是https开头,设置成true,如果授权认证服务器访问地址是http开头,设置成false【注意注意】

                #region 配置一定的时间来缓存结果,以减少通信的频率
                options.EnableCaching = true;
                options.CacheDuration = TimeSpan.FromMinutes(10);
                #endregion
            });
            #endregion

            #region 认证(经测试可以使用 OK)   第 1 步
            services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = Configuration["IdentityAuthentication:Authority"];
                    options.ApiName = Configuration["IdentityAuthentication:ApiName"];
                    options.ApiSecret = Configuration["IdentityAuthentication:ApiSecret"];
                    options.RequireHttpsMetadata = true;//如果授权认证服务器访问地址是https开头,设置成true,如果授权认证服务器访问地址是http开头,设置成false【注意注意】
                });

            /
            services.AddAuthentication(OAuth2IntrospectionDefaults.AuthenticationScheme)
            .AddOAuth2Introspection(options =>
            {
                options.Authority = Configuration["IdentityAuthentication:Authority"];
                options.ClientId = Configuration["IdentityAuthentication:ApiName"];
                options.ClientSecret = Configuration["IdentityAuthentication:ApiSecret"];
                options.Events = new OAuth2IntrospectionEvents 
                {
                    OnAuthenticationFailed = context =>
                    {
                        // 错误访问,401,授权认证失败
                        var tokenType = context.Options.TokenTypeHint;
                        if (context.Error.GetType() == typeof(SecurityTokenExpiredException))
                        {
                            context.Response.Headers.Add("Token-Expired", "true");
                        }
                        context.Response.ContentType = "application/json";
                        //自定义返回状态码,默认为401
                        //context.Response.StatusCode = StatusCodes.Status200OK;
                        //context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                        context.Response.WriteAsync(JsonConvert.SerializeObject(new { code = "401", msg = "Unauthorized" }));
                        return Task.CompletedTask;
                    },
                    OnTokenValidated = context =>
                    {
                        // 正常访问
                        var token = context.SecurityToken;
                        return Task.CompletedTask;
                    },
                };
            });

            #endregion

            services.AddControllers(options =>
            {
                //第 2 步
                options.Filters.Add<QDAuthorizeAttribute>();
                options.UseCentralRoutePrefix(new RouteAttribute($"api"));
                options.MaxModelValidationErrors = 50;
            });

            services.AddAutoMapper(typeof(AutoMapperInit));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseStatusCodePages();
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapHub<Chat>("/chathub");
            });
            app.UseApiVersioning();
        }
    }
}

*、https://localhost:44336/connect/token


*、https://localhost:44336/connect/userinfo


=====================令牌自省端点(Token Introspection Endpoint)=====================
docs(Token Introspection Endpoint)  
Introspection Endpoint  
https://localhost:44336/connect/introspect,introspection_endpoint是RFC 7662的实现。可以用于验证reference tokens(或如果消费者不支持适当的JWT或加密库,则JWT)。
示例1

POST /connect/introspect HTTP/1.1
Host: localhost:44336
Content-Type: application/x-www-form-urlencoded

token=iJy59t8IyjzbyWAN2dPpmwwuIzbDAKDlz7LAce09TXk&client_id=MOBILE_PAD&client_secret=ClientSecret&token_type_hint=access_token

响应数据
{
    "iss": "https://localhost:44336",
    "nbf": 1578532747,
    "exp": 1578536347,
    "aud": "MOBILE_PAD",
    "client_id": "MobileNurse",
    "sub": "9",
    "auth_time": 1578532747,
    "idp": "",
    "amr": "custom",
    "user_code": "9",
    "user_name": "admin",
    "real_name": "admin",
    "device_id": "121aa",
    "name": "admin",
    "role": "customer",
    "active": true,
    "scope": "MOBILE_PAD"
}



示例2

POST /connect/introspect
Authorization: Basic TU9CSUxFX1BBRDpDbGllbnRTZWNyZXQ=

token=<AccessToken>





注意:“TU9CSUxFX1BBRDpDbGllbnRTZWNyZXQ=”是一个BASE64编码字符串,BASE64解码后是“MOBILE_PAD:ClientSecret”,所以BASE64编码的格式是“Scope值:Secret值”

成功的响应将返回 状态代码200 以及 活动或非活动令牌:active:true

{
  "iss": "https://localhost:44336",
  "nbf": 1578462272,
  "exp": 1578465872,
  "aud": "MOBILE_PAD",
  "client_id": "MobileNurse",
  "sub": "9",
  "auth_time": 1578462272,
  "idp": "",
  "amr": "custom",
  "user_code": "9",
  "user_name": "admin",
  "real_name": "admin",
  "device_id": "121aa",
  "name": "admin",
  "role": "customer",
  "active": true,  /
  "scope": "MOBILE_PAD"
}

未知或过期的令牌将被标记为无效:

{
    "active": false,
}

无效请求将返回400,未授权请求401。

==========================令牌撤销端点(Token Revocation Endpoint)==========================
docs(Token Revocation Endpoint)  
Revocation Endpoint  撤销访问令牌(仅access tokens 和reference tokens)。它实现了令牌撤销规范(RFC 7009)。

此端点允许撤消访问令牌(仅限引用令牌)和刷新令牌。
token:要撤销的令牌(必填)
token_type_hint:access_token或refresh_token(可选)

示例

POST /connect/revocation HTTP/1.1
Host: localhost:44336
Authorization: Basic TW9iaWxlTnVyc2U6Q2xpZW50U2VjcmV0
Content-Type: application/x-www-form-urlencoded

token=SnQd0MkxPHB752vJJSwnwvgTDf2JhB6OpL0DoqR_fkk&token_type_hint=refresh_token

注意:“TW9iaWxlTnVyc2U6Q2xpZW50U2VjcmV0”是一个BASE64编码字符串,BASE64解码后是“MobileNurse:ClientSecret”,所以BASE64编码的格式是“ClientId值:Secret值”

==========================结束会话端点(End Session Endpoint)==========================
End Session Endpoint  
EndSession Endpoint  

https://localhost:44336/connect/endsession  登录注销
*
https://localhost:44336/connect/endsession?id_token_hint=eyJhbGciOiJSUzI1NiIsIm&post_logout_redirect_uri=http://www.baidu.com
*
*
*
*
*
*
*
*

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值