JWT自定义授权服务商-OAuthAuthorizationServerProvider

 

      上一篇博客中,较多篇幅提及有关JWT和Katana项目,没有看过的朋友,建议先了解下:

https://my.oschina.net/lichaoqiang/blog/867876

       在上一篇的基础之上,我们进一步探索,这里以自定义的授权服务提供上为例,实现在自定义

OAuthAuthorizationServerProvider程序。代码入下:

首先,我们需要创建一个类:CustomOAuthProvider,继承自

    /// <summary>
    /// <![CDATA[自定义 jwt oauth 的授权验证]]>
    /// </summary>
    public class CustomOAuthProvider : OAuthAuthorizationServerProvider
    {}

其次,重写虚方法,实现验证逻辑。

         /// <summary>
        /// <![CDATA[授权证书]]>
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var username = context.UserName;
            var password = context.Password;
            string userid;
            if (!CheckCredential(username, password, out userid))
            {
                context.SetError("invalid_grant", "The user name or password is incorrect");
                context.Rejected();
                return Task.FromResult<object>(null);
            }
            var ticket = new AuthenticationTicket(SetClaimsIdentity(context, userid, username), new AuthenticationProperties());
            context.Validated(ticket);
            return Task.FromResult<object>(context);
        }

        /// <summary>
        /// <![CDATA[验证客户端授权]]>
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
            return Task.FromResult<object>(context);
        }

自定义一个AccessTokenFormat:

 /// <summary> 
    /// 自定义 jwt token 的格式 
    /// </summary>
    public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
    {
        private readonly byte[] _secret;
        private readonly string _issuer;

        public CustomJwtFormat(string issuer, byte[] secret)
        {
            _issuer = issuer;
            _secret = secret;
        }

        /// <summary>
        /// <![CDATA[生成令牌]]>
        /// </summary>
        /// <param name="data"><![CDATA[票据]]></param>
        /// <returns></returns>
        public string Protect(AuthenticationTicket data)
        {

            var secret = TextEncodings.Base64Url.Decode("IxrAjDoa2FqElO7IhrSrUJELhUckePEPVpaePlS_Xaw");//秘钥
            SecurityKey securityKey = new InMemorySymmetricSecurityKey(secret);
            var signingKey = new InMemorySymmetricSecurityKey(_secret);
            var issued = data.Properties.IssuedUtc;
            var expires = data.Properties.ExpiresUtc;

            return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(_issuer, "Any", data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, new SigningCredentials(securityKey, "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256", "http://www.w3.org/2001/04/xmlenc#sha256")));
        }

        /// <summary>
        /// <![CDATA[从token中解码,返回一个票据信息]]>
        /// </summary>
        /// <param name="protectedText"><![CDATA[受保护的文本,也就是token]]></param>
        /// <returns></returns>
        public AuthenticationTicket Unprotect(string protectedText)
        {

            var jst = new JwtSecurityTokenHandler();
            var token = (JwtSecurityToken)jst.ReadToken(protectedText);

            var identity = new ClaimsIdentity(token.Claims);
            var ticket = new AuthenticationTicket(identity, new AuthenticationProperties() { });
            return ticket;

        }
    }

最后,创建一个启动类,配置中间件。

        /// <summary>
        /// <![CDATA[配置授权]]>
        /// </summary>
        /// <param name="app"></param>
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();
            var issuer = "http://localhost:2817/";
            var audience = "fNm0EDIXbfuuDowUpAoq5GTEiywV8eg0TpiIVnV8";//观众
            var secret = TextEncodings.Base64Url.Decode("IxrAjDoa2FqElO7IhrSrUJELhUckePEPVpaePlS_Xaw");//秘钥
            var signingKey = new System.IdentityModel.Tokens.InMemorySymmetricSecurityKey(secret);


            //配置JwtBearer授权中间件
            app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions()
            {
                AuthenticationMode = AuthenticationMode.Active,
                AuthenticationType = "password",
                AllowedAudiences = new[] { audience },
                Provider = new OAuthBearerAuthenticationProvider()
                {
                    OnValidateIdentity = context =>
                    {
                        context.Ticket.Identity.AddClaim(new System.Security.Claims.Claim(ClaimTypes.NameIdentifier, context.Ticket.Identity.Name));
                        return Task.FromResult<object>(context);
                    },
                    OnRequestToken = (context) =>
                    {
                        if (context.Token != null)
                        {
                            context.Response.Headers["WWW-Authorization"] = "Bearer";

                            var jst = new JwtSecurityTokenHandler();
                            var token = (JwtSecurityToken)jst.ReadToken(context.Token);
                            //创建身份
                            var identity = new ClaimsIdentity(token.Claims, "JWT");
                            context.Request.User = new ClaimsPrincipal(identity);

                        }
                        return Task.FromResult<object>(context);
                    }
                },
                IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                {
                     new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
                }

            });

            //配置OAuth中间件
            app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
            {
                //生产环境设为false
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/oauth2/token"),//请求token的路径
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(30),//令牌的过期时间
                //请求获取token时,验证username, password
                Provider = new CustomOAuthProvider(),
                //定义token信息格式 --默认TicketDataFormat
                AccessTokenFormat = new CustomJwtFormat(issuer, secret),
            });

            app.UseWebApi(config);//

        }

模拟示例:

<script type="text/javascript">
    $.ajax({
        beforeSend: function (request) { console.log(request); },
        type: "post",
        data: { client_id: "fNm0EDIXbfuuDowUpAoq5GTEiywV8eg0TpiIVnV8", username: "admin", password: "admin", grant_type: "password", client_secret: "IxrAjDoa2FqElO7IhrSrUJELhUckePEPVpaePlS_Xaw" },
        url: "/oauth2/token",
        headers: { alg: "HS256", typ: "JWT" },
        success: function (data) {

            var access_token = data.access_token;

            testApi(access_token);

        }

    });

    function testApi(token) {
        $.ajax({
            beforeSend: function (request) {  },
            type: "GET",
            url: "/api/values/get",
            headers: { "Authorization": "Bearer " + token },
            success: function (data) {
                
            }

        });

    }
</script>

获取访问令牌:

{"access_token":"BLEx3eE72_P8O__wCRUEhdtMlpPq0pnswmPReBZYAVdueL1p33cfybdcd0mVBFZZFsG8OgK8LBik1ZuDADW-NHul-KM8TXbJzr_dVfs2bw9S-YUNKMkJjY7mHilbF3TEI3o54jGn11R8z2tlp_95oLNVn_D10zyAdKNGs8qtb7gMW_XUCQpkRmVNcEd58h9wREXHYnYjWYyMRYT-rttgvxU5LKU5VsUTpdIFjfx2c17ALqr7d0sqY5LZ3nDh-tNyMvNHgu9ea9G2uHE3CVbulOYb-7EVp4MkpzW8rQ0cTAI","token_type":"bearer","expires_in":2591999}

携带access_token访问资源:

    [Authorize]
    public class ValuesController : ApiController
    {

        // GET api/values
        public IEnumerable<string> Get()
        {

            var owin = HttpContext.Current.GetOwinContext();
            var identity = owin.Request.User;

            return new string[] { "value1", "value2" };
        }
}
["value1","value2"]

 

转载于:https://my.oschina.net/lichaoqiang/blog/867896

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值