• 初始token


            为了验证使用者的身份,需要客户端向服务器端提供一个可靠的验证信息,称为Token。

            这个token通常由Json数据格式组成,通过hash散列算法生成一个字符串,所以称为Json Web Token(Json表示令牌的原始值是一个Json格式的数据,web表示是在互联网传播的,token表示令牌,简称JWT)。

    JWT是由 . 分割的三部分组成(例如):

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiQ2xpZW50XzIiLCJleHAiOjE1NjczMzQ2MzMsImlzcyI6IlRva2VuLmNvcmUiLCJhdWQiOiJDbGllbnRfMiJ9.OeamosaolXhsnIMe0so5eYIdAjLrm9w2mG3c5cDu5zQ

    头部(Header):

            JWT的头部承载了两个信息。声明类型(对于Jwt来说就是jwt)和加密算法(通常使用SHA256,HS256)

            将第一部分eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9进行base64就得到了实例中的头部。像这样的一个Json:

    {
        "typ":"JWT",
        "alg":"HS256"
    }

     

    载荷(Payload) :

            这一部分是JWT主要的信息存储部分,其中包含了许多种的声明(claims)。

            这些信息又包含了三个部分:1.标准中注册的声明,2.公共的声明,3.私有的声明。

           标准中注册的声明 (建议但不强制使用):

    iss: jwt签发者

    sub: jwt所面向的用户

    aud: 接收jwt的一方

    exp: jwt的过期时间,这个过期时间必须要大于签发时间

    nbf: 定义在什么时间之前,该jwt都是不可用的.

    iat: jwt的签发时间

    jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

           公共的声明 :公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息。

           私有的声明 :私有声明是提供者和消费者所共同定义的声明。

    注意:事实上我们的Header和Payload都是基于base64加密的,这种密文都是可以对称解密的,因此请不要存放敏感信息。

            将第二部分eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiQ2xpZW50XzIiLCJleHAiOjE1NjczMzQ2MzMsImlzcyI6IlRva2VuLmNvcmUiLCJhdWQiOiJDbGllbnRfMiJ9进行base64解谜就得到了实例中的载荷。像这样的一个Json:

    {
        "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name":"Client_2",
        "exp":1567334633,
        "iss":"Token.core",
        "aud":"Client_2"
    }

     

    签名(Signature):

           使用保存在服务端的秘钥对其签名,用来验证发送者的JWT的同时也能确保在期间不被篡改。

    验证过程:

    1. 服务方的提供一个签发token的接口,验证订阅方信息通过后,生成并返回token。
    2. 客户端存储token,并在每次请求时附送上这个token值。
    3. 服务方验证token值,并返回数据。

    实例:

    .Net core:先创建一个空的API项目作为服务方,在Startup.cs中添加如下代码

    /// <summary>
    /// 向容器中添加服务
    /// </summary>
    /// <param name="services"></param>
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    
        #region 添加验证服务
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options => {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    // 是否验证属性若为false则不验证,默认值为true
                    ValidateIssuer = true,//是否验证服务发布方
                    ValidateAudience = true,//是否验证服务订阅方
                    ValidateLifetime = true,//是否验证失效时间
                    ValidateIssuerSigningKey = true,//是否验证密钥
                    ValidIssuer = "Token.core", // 服务发布方
                    ValidAudience = "test", // 服务订阅方
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("qwertyuiopasdfghjklzxcvbnm1234567890")) // 密钥
                };
            });
        #endregion
    }
    /// <summary>
    /// 配置http请求管道
    /// </summary>
    /// <param name="app"></param>
    /// <param name="env"></param>
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseAuthentication(); // 注意添加这一句,启用jwt验证
        app.UseMvc();
    }

    这部分代码用来添加并启动jwt验证服务。

    注意:HS256加密需要提供足够长度的密钥,否则报错哦。

    [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        /// <summary>
        /// 测试无需验证
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get()
        {
            return "测试无需验证通过";
        }
        /// <summary>
        /// 测试需验证
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Authorize]
        public string GetByAuth()
        {
            return "测试需验证通过";
        }
    }

    在需要验证的接口加上[Authorize]属性,便会在该接口被调用时进行权限验证。

    [Route("api/[controller]/[action]")]
    [ApiController]
    public class RegisterController : ControllerBase
    {
        /// <summary>
        /// 签发Token
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public string Token(RegisterInfo reg)
        {
            return new JwtTokenHelper().GetToken4Client(reg);
        }
    }
    /// <summary>
    /// 生成客户端授权Token
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public string GetToken4Client(RegisterInfo reg)
    {
        // 组装要验证的身份信息
        var claims = new[]
        {
               new Claim(ClaimTypes.Name, reg.Client),
               //new Claim(ClaimTypes.Role, "admin"), //在这可以分配用户角色,比如管理员 、 vip会员 、 普通用户等
        };
        // 签发Token需要用到一个密钥secret key。这个密钥需要共享到服务端与有权限访问的客户端
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(reg.SecretKey));
        //.NET Core的JwtSecurityToken类实际负责生成token.
        var token = new JwtSecurityToken(
            issuer: "Token.core", // 服务发布方
            audience: reg.Client, // 服务订阅方
            claims: claims, // 声明
            expires: DateTime.Now.AddMinutes(100), // 有效时间
            signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256) // 根据密钥生成签名凭证
        );
    
        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    创建一个签发token的接口,如果验证订阅方传入的身份信息无误,即生成并返回该token。

    .Net framework:https://blog.csdn.net/zhanglong_longlong/article/details/75503521

    先创建一个空的API项目作为服务方。

    添加以下引用:

    Install-Package Microsoft.AspNet.Identity.Owin 
    Install-Package Microsoft.Owin.Security.OAuth 
    Install-Package Microsoft.AspNet.WebApi.Owin 
    Install-Package Microsoft.AspNet.WebApi.WebHost 
    Install-Package Microsoft.Owin.Host.SystemWeb

    [assembly: OwinStartup(typeof(Startup))]
    namespace Token.frame.App_Start
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                ConfigureAuth(app);
            }
    
            public void ConfigureAuth(IAppBuilder app)
            {
                /*
                  app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
                 {
                     TokenEndpointPath = new PathString("/token"),
                     Provider = new ApplicationOAuthProvider(),
                     AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
                     AuthenticationMode = AuthenticationMode.Active,
                     //HTTPS is allowed only AllowInsecureHttp = false
                     AllowInsecureHttp = true
                     //ApplicationCanDisplayErrors = false
                 });
                 app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
                 */
                app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
                {
                    TokenEndpointPath = new PathString("/token"),
                    Provider = new ApplicationOAuthProvider(),
                    //RefreshTokenProvider = new ApplicationRefreshTokenProvider(),
                    AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
                    AuthenticationMode = AuthenticationMode.Active,
                    //HTTPS is allowed only AllowInsecureHttp = false
                    AllowInsecureHttp = true
                    //ApplicationCanDisplayErrors = false
                });
            }
        }
    
        public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
        {
            public static RegisterInfo register = JsonConvert.DeserializeObject<RegisterInfo>(File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"/Token.json"));
            /*
             private OAuth2ClientService _oauthClientService;
             public ApplicationOAuthProvider()
             {
                 this.OAuth2ClientService = new OAuth2ClientService();
             }
             */
    
            /// <summary>
            /// 验证客户[client_id与client_secret验证]
            /// </summary>
            /// <param name="context"></param>
            /// <returns></returns>
            public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
            {
                //POST http://localhost:48339/token
                //grant_type=client_credentials&client_id=Client_1&client_secret=qwertyuiopasdfghjklzxcvbnm1234567890
                string client_id;
                string client_secret;
                context.TryGetFormCredentials(out client_id, out client_secret);
                if (client_id == register.Client && client_secret == register.SecretKey)
                {
                    context.Validated(client_id);
                }
                else
                {
                    //context.Response.StatusCode = Convert.ToInt32(HttpStatusCode.OK);
                    context.SetError("invalid_client", "client is not valid");
                }
                return base.ValidateClientAuthentication(context);
            }
    
            /// <summary>
            /// 客户端授权[生成access token]
            /// </summary>
            /// <param name="context"></param>
            /// <returns></returns>
            public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
            {
                /*
                     var client = _oauthClientService.GetClient(context.ClientId);
                     oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName));
                 */
    
    
                var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
                oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, register.Client));
                var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties() { AllowRefresh = true });
                context.Validated(ticket);
                return base.GrantClientCredentials(context);
            }
    
            /// <summary>
            /// 刷新Token[刷新refresh_token]
            /// </summary>
            /// <param name="context"></param>
            /// <returns></returns>
            public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
            {
                //enforce client binding of refresh token
                if (context.Ticket == null || context.Ticket.Identity == null || !context.Ticket.Identity.IsAuthenticated)
                {
                    context.SetError("invalid_grant", "Refresh token is not valid");
                }
                else
                {
                    //Additional claim is needed to separate access token updating from authentication 
                    //requests in RefreshTokenProvider.CreateAsync() method
                }
                return base.GrantRefreshToken(context);
            }
    
            public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
            {
                if (context.ClientId == register.Client)
                {
                    var expectedRootUri = new Uri(context.Request.Uri, "/");
                    if (expectedRootUri.AbsoluteUri == context.RedirectUri)
                    {
                        context.Validated();
                    }
                }
                return Task.FromResult<object>(null);
            }
        }
    }

    新建Startup.cs,添加OAuth 相关配置。

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API 配置和服务
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
            // Web API 路由
            config.MapHttpAttributeRoutes();
    
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }

    在WebApiConfig.cs中添加验证配置。

    [RoutePrefix("api/[controller]/[action]")]
    public class TestController : ApiController
    {
        /// <summary>
        /// 测试无需验证
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get()
        {
            return "测试无需验证通过";
        }
        /// <summary>
        /// 测试需验证
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        public string GetByAuth()
        {
            return "测试需验证通过";
        }
    }
    

    在需要验证的接口加上[Authorize]属性,便会在该接口被调用时进行权限验证。

    测试过程这里就不做说明啦。

    使用OAuth授权包括【客户端模式(Client Credentials Grant)】

    采用Client Credentials方式,即应用公钥、密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open API,并且需要开发者提前向开放平台申请,成功对接后方能使用。认证服务器不提供像用户数据这样的重要资源,仅仅是有限的只读资源或者一些开放的 API。例如使用了第三方的静态文件服务,如Google Storage或Amazon S3。这样,你的应用需要通过外部API调用并以应用本身而不是单个用户的身份来读取或修改这些资源。这样的场景就很适合使用客户端证书授权,通过此授权方式获取Access Token仅可访问平台授权类的接口。

    比如获取App首页最新闻列表,由于这个数据与用户无关,所以不涉及用户登录与授权,但又不想任何人都可以调用这个WebAPI,这样场景就适用[例:比如微信公众平台授权]。

    注:以上内容由网上内容整理而来

     

  • 相关阅读:
    Android Button四种点击事件和长按事件
    Android 简单登陆 涉及 Button CheckBox TextView EditText简单应用
    Android EditText属性
    Android-TextView 控件常用属性以及基本用法
    对象数组空指针异常
    数字反转问题
    遇3问题
    队列解密QQ号
    线程先后执行问题
    小哼买书
  • 原文地址:https://www.cnblogs.com/ariter/p/11444175.html
Copyright © 2020-2023  润新知