IdentityServer4-HybridAndClientCredentials

1、服务器nginx

Client设置:服务器

 1  new Client
 2                 {
 3                     ClientId = "mvc1",
 4                     ClientName = "后台管理MVC客户端",
 5                     ClientSecrets = { new Secret("mvc1".Sha256()) },
 6 
 7                     AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
 8                     AllowOfflineAccess = true,
 9                     RequireConsent = false,
10                     RedirectUris = { $"{ClientUrl}/signin-oidc",$"{LocalClientUrl}/signin-oidc"},
11                     PostLogoutRedirectUris = { $"{ClientUrl}/signout-callback-oidc",$"{LocalClientUrl}/signout-callback-oidc"},
12 
13                     AllowedScopes =
14                     {
15                         IdentityServerConstants.StandardScopes.OpenId,
16                         IdentityServerConstants.StandardScopes.Profile,
17                         "IdServerAdmin_API"
18                     },
19 
20                     AlwaysIncludeUserClaimsInIdToken = true
21                 }

Startup.cs:cookie

        /// <summary>
        /// 设置认证服务器
        /// </summary>
        /// <param name="services"></param>
        private void SetIdentityServer(IServiceCollection services)
        {
            #region 认证服务器
            var ServerUrl = Configuration.GetSection("AppSetting:ServerUrl").Value;
            var connectionString = Configuration.GetSection("AppSetting:ConnectionString").Value;

            //配置AccessToken的加密证书
            var rsa = new RSACryptoServiceProvider();
            //从配置文件获取加密证书
            rsa.ImportCspBlob(Convert.FromBase64String(Configuration["AppSetting:SigningCredential"]));
            var idServer = services.AddIdentityServer(options => {
                options.IssuerUri = ServerUrl;
                options.PublicOrigin = ServerUrl;

                options.Discovery.ShowApiScopes = true;
                options.Discovery.ShowClaims = true;

                options.Events.RaiseSuccessEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseErrorEvents = true;

            });
            //设置加密证书
            idServer.AddSigningCredential(new RsaSecurityKey(rsa));
            idServer.AddInMemoryApiResources(Config.GetApiResources());
            idServer.AddInMemoryIdentityResources(Config.GetIdentityResources());
            idServer.AddInMemoryClients(Config.GetClients());

            services.AddTransient<IMyUserStore, MyUserStore>();
            services.AddTransient<IProfileService, MyProfile>();
            services.AddTransient<IResourceOwnerPasswordValidator, MyUserValidator>();
            
            #endregion
        }

  

 1 public class MyProfile : IProfileService
 2     {
 3         private readonly IMyUserStore _myUserStore;
 4         public MyProfile(IMyUserStore myUserStore)
 5         {
 6             _myUserStore = myUserStore;
 7         }
 8 
 9         public Task GetProfileDataAsync(ProfileDataRequestContext context)
10         {
11             var subjectId = context.Subject.GetSubjectId();
12             var user = _myUserStore.GetUserById(subjectId);
13 
14             
15 
16             var claims = new List<Claim>
17             {
18                 new Claim("role",  user.Role),
19                 new Claim("userguid", user.SubjectId),
20                 new Claim("abc", "这是自定义的值。……。。…。……。……")
21             };
22 
23             var q = context.RequestedClaimTypes;
24             context.AddRequestedClaims(claims);
25             context.IssuedClaims.AddRange(claims);
26 
27             return Task.FromResult(0);
28         }
29 
30         public Task IsActiveAsync(IsActiveContext context)
31         {
32             var user = _myUserStore.GetUserById(context.Subject.GetSubjectId());
33             context.IsActive = (user != null);
34 
35             return Task.FromResult(0);
36         }
37     }
 1 public interface IMyUserStore
 2     {
 3         JUser Find(string username, string userpass);
 4         JUser GetUserById(string subjectId);
 5     }
 6 
 7     public class MyUserStore : IMyUserStore
 8     {
 9         readonly IOptions<AppSetting> _options;
10         readonly IMemoryCache _memoryCache;
11 
12         private const string CACHENAME = "MyUserStore";
13 
14         public MyUserStore(IOptions<AppSetting> options, IMemoryCache m_memoryCache)
15         {
16             _options = options;
17             _memoryCache = m_memoryCache;
18         }
19 
20         public List<JUser> GetList(bool reload=true)
21         {
22             if (reload)
23             {
24                 _memoryCache.Remove(CACHENAME);
25             }
26 
27             List<JUser> list;
28             if (!_memoryCache.TryGetValue(CACHENAME, out list)){
29                 using(MySqlConnection conn = new MySqlConnection(_options.Value.ConnectionString))
30                 {
31                     list = conn.Query<JUser>("select * from juser").ToList();
32 
33                     //添加超级用户
34                     JUser jc = new JUser()
35                     {
36                         UserName = _options.Value.SuperUserName,
37                         UserPass = StringHelper.GetMd5(_options.Value.SuperPassword),
38                         SubjectId = "a36005e2-5984-41f5-aa91-8e93b479d88e",
39                         Role = "IdServerAdmin"
40                     };
41 
42                     list.Add(jc);
43                 }
44                 _memoryCache.Set(CACHENAME, list);
45             }
46             return list;
47         }
48 
49 
50 
51         public JUser Find(string username, string userpass)
52         {
53             var list = GetList();
54             return list.SingleOrDefault(p => p.UserName == username && p.UserPass == StringHelper.GetMd5(userpass));
55         }
56 
57         public JUser GetUserById(string subjectId)
58         {
59             var list = GetList();
60             return list.SingleOrDefault(p => p.SubjectId == subjectId);
61         }
 1 public class MyUserValidator : IResourceOwnerPasswordValidator
 2     {
 3         readonly IMyUserStore _myUserStore;
 4 
 5         public MyUserValidator(IMyUserStore myUserStore)
 6         {
 7             _myUserStore = myUserStore;
 8         }
 9 
10         public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
11         {
12             var q = _myUserStore.Find(context.UserName, context.Password);
13 
14             if (q != null)
15             {
16                 //验证成功
17                 //使用subject可用于在资源服务器区分用户身份等等
18                 //获取:资源服务器经过User.Claims.Where(l => l.Type == "sub").FirstOrDefault();
19                 var claims = new List<Claim>();
20                 claims.Add(new Claim("role", q.Role));
21                 claims.Add(new Claim("userguid", q.SubjectId));
22 
23                 context.Result = new GrantValidationResult(subject: $"{q.SubjectId}", authenticationMethod: "custom", claims: claims.AsEnumerable());
24             }
25             else
26             {
27                 //验证失败
28                 context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "无效的用户凭证");
29             }
30             return Task.FromResult(0);
31         }
32     }

2、客户端:mvc

 1  /// <summary>
 2         /// 设置认证客户端
 3         /// </summary>
 4         /// <param name="services"></param>
 5         private void SetIdentityClient(IServiceCollection services)
 6         {
 7             var ServerUrl = Configuration.GetSection("AppSetting:ServerUrl").Value;
 8             var client_id = Configuration.GetSection("AppSetting:SuperClientId").Value;
 9             var cient_secret = Configuration.GetSection("AppSetting:SuperClientSecret").Value;
10 
11             //services.Configure<MvcOptions>(options =>
12             //{
13             //    // Set LocalTest:skipSSL to true to skip SSL requrement in 
14             //    // debug mode. This is useful when not using Visual Studio.
15             //    options.Filters.Add(new RequireHttpsAttribute());
16             //});
17 
18             JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
19 
20             var idClient = services.AddAuthentication(options =>
21             {
22                 options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
23                 options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
24             })
25             .AddCookie()
26             .AddOpenIdConnect(options =>
27             {
28                 options.SignOutScheme = OpenIdConnectDefaults.AuthenticationScheme;
29                 options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; // cookie middle setup above
30                 options.Authority = ServerUrl; // 认证服务器
31                 options.RequireHttpsMetadata = true; // SSL Https模式 
32                 options.ClientId = client_id; // 客户端(位于认证服务器)
33                 options.ClientSecret = cient_secret;  // 客户端(位于认证服务器)
34                 options.ResponseType = "code id_token"; // means Hybrid flow (id + access token)
35 
36                 options.GetClaimsFromUserInfoEndpoint = false;
37                 options.SaveTokens = true;
38                 options.TokenValidationParameters = new TokenValidationParameters
39                 {
40                     NameClaimType = "name",
41                     RoleClaimType = "role"
42                 };
43 
44                 options.Scope.Clear();
45                 options.Scope.Add("openid");
46                 options.Scope.Add("profile");
47                 options.Scope.Add("IdServerAdmin_API");
48 
49                 options.Events = new OpenIdConnectEvents()
50                 {
51                     OnMessageReceived = (context) =>
52                     {
53                         return Task.FromResult(0);
54                     },
55 
56                     OnUserInformationReceived = (context) =>
57                     {
58                         return Task.FromResult(0);
59                     },
60                     OnRedirectToIdentityProvider = (context) =>
61                     {
62                         //设置重定向地址,解决生产环境nginx+https访问,仍是有问题。。。。。。。
63                         context.Properties.RedirectUri = $"{ClientUrl}/signin-oidc";
64                         //context.ProtocolMessage.RedirectUri = $"{ClientUrl}/signin-oidc";
65                         return Task.FromResult(0);
66                     },
67 
68                     OnTokenValidated = (context) =>
69                     {
70                         //context.Properties.RedirectUri = $"{ClientUrl}/signin-oidc";
71                         return Task.FromResult(0);
72                     },
73                 };
74             });
75         }