Пример #1
0
        public static void SetOIDC(IServiceCollection services, IdentityOption identityOption)
        {
            /* 简单来说:OIDC 是OpenID Connect的简称,OIDC=(Identity, Authentication) + OAuth 2.0。它在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议
             * iss = Issuer Identifier:必须。提供认证信息者的唯一标识。一般是一个https的url(不包含querystring和fragment部分)。
             * sub = Subject Identifier:必须。iss提供的EU的标识,在iss范围内唯一。它会被RP用来标识唯一的用户。最长为255个ASCII个字符。
             * aud = Audience(s):必须。标识ID Token的受众。必须包含OAuth2的client_id。
             * exp = Expiration time:必须。过期时间,超过此时间的ID Token会作废不再被验证通过。
             * iat = Issued At Time:必须。JWT的构建的时间。
             * auth_time = AuthenticationTime:EU完成认证的时间。如果RP发送AuthN请求的时候携带max_age的参数,则此Claim是必须的。
             * nonce:RP发送请求的时候提供的随机字符串,用来减缓重放攻击,也可以来关联ID Token和RP本身的Session信息。
             * acr = Authentication Context Class Reference:可选。表示一个认证上下文引用值,可以用来标识认证上下文类。
             * amr = Authentication Methods References:可选。表示一组认证方法。
             * azp = Authorized party:可选。结合aud使用。只有在被认证的一方和受众(aud)不一致时才使用此值,一般情况下很少使用。
             */
            var apiResources = new List <ApiResource>
            {
                //给api资源定义Scopes 必须与 Client 的 AllowedScopes 对应上,不然显示 invalid_scope
                new ApiResource(identityOption.Scope, identityOption.Scope)
            };
            Collection <Secret> clientSecrets = new Collection <Secret>()
            {
                new Secret(identityOption.Secret.Sha256())
            };

            services.AddIdentityServer()
            .AddSigningCredential(GetRsaSecurityKey())
            //.AddDeveloperSigningCredential()//设置RSA的加密证书(注意:默认是使用临时证书的,就是AddTemporarySigningCredential(),无论如何不应该使用临时证书,因为每次重启授权服务,就会重新生成新的临时证书),RSA加密证书长度要2048以上,否则服务运行会抛异常
            .AddInMemoryApiResources(apiResources)   //添加api资源
            .AddInMemoryClients(new List <Client> {
                new Client {
                    ClientId            = identityOption.ClientId,
                    AllowedGrantTypes   = GrantTypes.Implicit,
                    ClientSecrets       = clientSecrets,
                    AccessTokenLifetime = identityOption.ExpiresIn,
                    ClientName          = identityOption.ClientName,
                    // where to redirect to after login
                    RedirectUris = { identityOption.RedirectUri },   //登录成功后定向地址
                    // where to redirect to after logout
                    PostLogoutRedirectUris = { identityOption.LogoutRedirectUri },
                    AllowedScopes          = new List <string>
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile
                    }
                }
            }
                                )
            .AddResourceOwnerValidator <ResourceOwnerPasswordValidator>();
        }
Пример #2
0
        /// <summary>
        /// 生成一个新的 Token
        /// </summary>
        /// <param name="option">身份配置信息,生成Token所需要的信息</param>
        /// <param name="claims">用户信息实体</param>
        /// <returns></returns>
        private JsonResult CreateToken(IdentityOption option, Claim[] claims)
        {
            #region Claim
            // var claims = new Claim[]
            //{

            //     new Claim(JwtRegisteredClaimNames.Aud, _options.Audience),
            //     new Claim(JwtRegisteredClaimNames.Sid, username),
            //     new Claim(JwtRegisteredClaimNames.Sub, username),
            //     new Claim(JwtRegisteredClaimNames.Exp, _options.ExpiresIn.ToString()),
            //     new Claim(JwtRegisteredClaimNames.AuthTime, DateTime.Now.ToString()),
            //     new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            //     new Claim(JwtRegisteredClaimNames.Iat, _options.Issued.ToString(), ClaimValueTypes.Integer64),//发行时间
            //     //用户名
            //     new Claim("UserName","中国人民"),
            //     //角色
            //     new Claim("Role","我是角色"),
            //     new Claim("Country","中国"),
            //     new Claim("Expired",_options.ExpiresIn.ToString()),
            //     new Claim("Mobile","13556891160")
            //};
            #endregion
            var jwt = new JwtSecurityToken(
                issuer: option.Issuer,     // 发行者(颁发机构)
                audience: option.Audience, //订阅人 , 令牌的观众(颁发给谁)
                claims: claims,
                notBefore: DateTime.UtcNow,
                expires: option.ExpiresTime,
                signingCredentials: option.SigningCredentials);
            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

            var token = new
            {
                access_token = encodedJwt,
                token_type   = option.TokenType,
                expires_in   = option.ExpiresIn,                                   //过期时间(秒)
                expires_time = option.ExpiresTime.ToString("yyyy-MM-dd HH:mm:ss"), //过期时间(日期)
                claims       = claims
            };
            return(Json(token));
        }
Пример #3
0
        //private readonly IConnectionMultiplexer _redis;

        public IdentityService(
            IOptions <IdentityOption> options,
            IHttpContextAccessor httpContextAccessor,
            UserManager <User> userManager,
            SignInManager <User> signInManager,
            IPasswordHasher <User> passwordHasher,
            ICoreUnitOfWork uow,
            IEnumerable <IUserInfoService> userInfoServices,
            IDistributedCache cache,
            //IConnectionMultiplexer redis,
            IWorkContext workContext)
        {
            _options             = options.Value;
            _httpContextAccessor = httpContextAccessor;
            _userManager         = userManager;
            _signInManager       = signInManager;
            _passwordHasher      = passwordHasher;
            _uow = uow;
            _userInfoServices = userInfoServices;
            _cache            = cache;
            _workContext      = workContext;
            //_redis = redis;
        }
Пример #4
0
        /// <summary>
        /// 注入服务
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        /// <param name="Configuration">IConfiguration</param>
        public static void AddServiceSingleton(this IServiceCollection services, IConfiguration Configuration)
        {
            services.Configure <IdentityOption>(Configuration.GetSection("IdentityOption"));
            //var identityConfigurationSection = Configuration.GetSection("IdentityOption");
            // 添加服务设置实例配置
            var identity = Configuration.GetSection("IdentityOption");

            #region 【读取配置】
            var            symmetricKeyAsBase64 = Configuration["IdentityOption:Secret"];
            var            keyByteArray         = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
            var            signingKey           = new SymmetricSecurityKey(keyByteArray);
            var            signingCredentials   = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
            IdentityOption identityOption       = new IdentityOption
            {
                Secret             = Configuration["IdentityOption:Secret"],                     //密钥
                Issuer             = Configuration["IdentityOption:Issuer"],                     //发行者
                Audience           = Configuration["IdentityOption:Audience"],                   //令牌的观众
                TokenType          = Configuration["IdentityOption:TokenType"],                  //表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
                Scope              = Configuration["IdentityOption:Scope"],                      //表示权限范围,如果与客户端申请的范围一致,此项可省略
                Subject            = Configuration["IdentityOption:Subject"],                    //主题
                ExpiresIn          = Convert.ToInt32(Configuration["IdentityOption:ExpiresIn"]), //表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
                ClientId           = Configuration["IdentityOption:ClientId"],                   //表示客户端的ID,必选项
                ResponseType       = Configuration["IdentityOption:ResponseType"],               //表示授权类型,必选项,此处的值固定为"code"
                RedirectUri        = Configuration["IdentityOption:RedirectUri"],
                State              = Configuration["IdentityOption:State"],                      //表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。
                SigningCredentials = signingCredentials
            };
            #endregion
            #region 【客户端模式】【密码模式】
            LicensingMode.SetResourceOwnerPasswordAndClientCredentials(services, identityOption);
            #endregion
            #region JWT JwtRegisteredClaimNames 方式 直接读取配置文件信息,初始化Token 需要验证的信息,如果不同在一台服务,则产生的Token与验证的Token的服务器验证信息与产生的信息要一致
            var            jwtKeyAsBase64        = Configuration["JWTTokenOption:Secret"];
            var            jwtKeyByteArray       = Encoding.ASCII.GetBytes(jwtKeyAsBase64);
            var            jwtSigningKey         = new SymmetricSecurityKey(jwtKeyByteArray);
            var            jwtSigningCredentials = new SigningCredentials(jwtSigningKey, SecurityAlgorithms.RsaSha256Signature);
            JWTTokenOption jwtOption             = new JWTTokenOption
            {
                Issuer             = Configuration["JWTTokenOption:Issuer"],                     //发行者
                Audience           = Configuration["JWTTokenOption:Audience"],                   //令牌的观众
                ExpiresIn          = Convert.ToInt32(Configuration["JWTTokenOption:ExpiresIn"]), //表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
                ClientId           = Configuration["JWTTokenOption:ClientId"],                   //表示客户端的ID,必选项
                SigningCredentials = jwtSigningCredentials
            };
            // 从文件读取密钥
            string keyDir = PlatformServices.Default.Application.ApplicationBasePath;
            if (RSAUtils.TryGetKeyParameters(keyDir, false, out RSAParameters keyParams) == false)
            {
                keyParams = RSAUtils.GenerateAndSaveKey(keyDir);
            }
            jwtOption.RsaSecurityKey = new RsaSecurityKey(keyParams);
            // 添加到 IoC 容器
            // services.SigningCredentials(_tokenOptions);


            var tokenValidationParameters = new TokenValidationParameters
            {
                #region  面三个参数是必须
                // 签名秘钥
                ValidateIssuerSigningKey = true,
                IssuerSigningKey         = jwtSigningKey,
                // 发行者(颁发机构)
                ValidateIssuer = true,
                ValidIssuer    = jwtOption.Issuer,
                // 令牌的观众(颁发给谁)
                ValidateAudience = true,
                ValidAudience    = jwtOption.Audience,
                #endregion
                // 是否验证Token有效期
                ValidateLifetime = true,
                ClockSkew        = TimeSpan.Zero //ClockSkew:允许的服务器时间偏移量,默认是5分钟,如果不设置,时间有效期间到了以后,5分钟之内还可以访问资源
                                                 /***********************************TokenValidationParameters的参数默认值***********************************/
                                                 // RequireSignedTokens = true,
                                                 // SaveSigninToken = false,
                                                 // ValidateActor = false,
                                                 // 将下面两个参数设置为false,可以不验证Issuer和Audience,但是不建议这样做。
                                                 // ValidateAudience = true,
                                                 // ValidateIssuer = true,
                                                 // ValidateIssuerSigningKey = false,
                                                 // 是否要求Token的Claims中必须包含Expires
                                                 // RequireExpirationTime = true,
                                                 // 允许的服务器时间偏移量
                                                 // ClockSkew = TimeSpan.FromSeconds(300),//TimeSpan.Zero
                                                 // 是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
                                                 // ValidateLifetime = true
            };
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme    = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(o =>
            {
                //不使用https
                //o.RequireHttpsMetadata = false;
                o.TokenValidationParameters = tokenValidationParameters;
            });
            #endregion

            #region 【密码模式 OIDC】和用户有关,一般用于第三方登录
            //services.AddAuthentication(options =>
            //{
            //    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            //})
            // .AddCookie()
            // .AddOpenIdConnect(o =>
            // {
            //     o.ClientId = "oidc.hybrid";
            //     o.ClientSecret = "secret";

            //    // 若不设置Authority,就必须指定MetadataAddress
            //    o.Authority = "https://oidc.faasx.com/";
            //    // 默认为Authority+".well-known/openid-configuration"
            //    //o.MetadataAddress = "https://oidc.faasx.com/.well-known/openid-configuration";
            //    o.RequireHttpsMetadata = false;

            //    // 使用混合流
            //    o.ResponseType = OpenIdConnectResponseType.CodeIdToken;
            //    // 是否将Tokens保存到AuthenticationProperties中
            //    o.SaveTokens = true;
            //    // 是否从UserInfoEndpoint获取Claims
            //    o.GetClaimsFromUserInfoEndpoint = true;
            //    // 在本示例中,使用的是IdentityServer,而它的ClaimType使用的是JwtClaimTypes。
            //    o.TokenValidationParameters.NameClaimType = "name"; //JwtClaimTypes.Name;

            //    // 以下参数均有对应的默认值,通常无需设置。
            //    //o.CallbackPath = new PathString("/signin-oidc");
            //    //o.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
            //    //o.RemoteSignOutPath = new PathString("/signout-oidc");
            //    //o.Scope.Add("openid");
            //    //o.Scope.Add("profile");
            //    //o.ResponseMode = OpenIdConnectResponseMode.FormPost;

            //    /***********************************相关事件***********************************/
            //    // 未授权时,重定向到OIDC服务器时触发
            //    //o.Events.OnRedirectToIdentityProvider = context => Task.CompletedTask;

            //    // 获取到授权码时触发
            //    //o.Events.OnAuthorizationCodeReceived = context => Task.CompletedTask;
            //    // 接收到OIDC服务器返回的认证信息(包含Code, ID Token等)时触发
            //    //o.Events.OnMessageReceived = context => Task.CompletedTask;
            //    // 接收到TokenEndpoint返回的信息时触发
            //    //o.Events.OnTokenResponseReceived = context => Task.CompletedTask;
            //    // 验证Token时触发
            //    //o.Events.OnTokenValidated = context => Task.CompletedTask;
            //    // 接收到UserInfoEndpoint返回的信息时触发
            //    //o.Events.OnUserInformationReceived = context => Task.CompletedTask;
            //    // 出现异常时触发
            //    //o.Events.OnAuthenticationFailed = context => Task.CompletedTask;

            //    // 退出时,重定向到OIDC服务器时触发
            //    //o.Events.OnRedirectToIdentityProviderForSignOut = context => Task.CompletedTask;
            //    // OIDC服务器退出后,服务端回调时触发
            //    //o.Events.OnRemoteSignOut = context => Task.CompletedTask;
            //    // OIDC服务器退出后,客户端重定向时触发
            //    //o.Events.OnSignedOutCallbackRedirect = context => Task.CompletedTask;

            //});

            #endregion
            //注册简单的定时任务执行
            //services.AddSingleton<Microsoft.Extensions.Hosting.IHostedService, MainService>();
        }
Пример #5
0
        /// <summary>
        /// 【客户端模式】【密码模式】
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        /// <param name="identityOption">IdentityOption</param>
        public static void SetResourceOwnerPasswordAndClientCredentials(IServiceCollection services, IdentityOption identityOption)
        {
            #region 【客户端模式】【密码模式】

            /*
             * 【客户端模式】和用户无关,用于应用程序与 API 资源的直接交互场景,经常运用于服务器对服务器中间通讯使用。
             * 【密码模式】和用户有关,一般用于第三方登录
             */
            var apiResources = new List <ApiResource>
            {
                //给api资源定义Scopes 必须与 Client 的 AllowedScopes 对应上,不然显示 invalid_scope
                new ApiResource(identityOption.Scope, identityOption.Scope)
            };
            Collection <Secret> clientSecrets = new Collection <Secret>()
            {
                new Secret(identityOption.Secret.Sha256())
            };
            services.AddIdentityServer()
            .AddSigningCredential(GetRsaSecurityKey())
            //.AddDeveloperSigningCredential()//设置RSA的加密证书(注意:默认是使用临时证书的,就是AddTemporarySigningCredential(),无论如何不应该使用临时证书,因为每次重启授权服务,就会重新生成新的临时证书),RSA加密证书长度要2048以上,否则服务运行会抛异常
            .AddInMemoryApiResources(apiResources)   //添加api资源
            .AddInMemoryClients(new List <Client> {
                new Client {
                    ClientId            = identityOption.ClientId,
                    AllowedGrantTypes   = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                    ClientSecrets       = clientSecrets,
                    AccessTokenLifetime = identityOption.ExpiresIn,
                    AllowedScopes       = { identityOption.Scope }
                }
            }
                                )
            .AddResourceOwnerValidator <ResourceOwnerPasswordValidator>();
            #endregion
        }