zoukankan      html  css  js  c++  java
  • 开箱即用

    基于JWT(Json Web Token)的授权方式

    JWT 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;

    从客户端请求服务器获取token, 用该token 去访问实现了jwt认证的web服务器。 token 可保存自定义信息,如用户基本信息, web服务器用key去解析token,就获取到请求用户的信息了;
    很方便解决跨域授权的问题,因为跨域无法共享cookie,.net平台集成的 FormAuthentication 认证系统是基于Session保存授权信息,拿不到cookie就无法认证,用jwt完美解决了。
    很多时候,web服务器和授权服务器是同一个项目,所以也可以用以下架构:

    实现JWT授权

    1.vs2015 新建一个WebApi,安装下面的库,可用nuget 或 命令安装:

    install-package Thinktecture.IdentityModel.Core
    install-package Microsoft.Owin.Security.Jwt

    2.把Startup.Auth.cs 下的 ConfigureAuth 方法清空掉,改为:

    
        public partial class Startup
        {
            public void ConfigureAuth(IAppBuilder app)
            {
                var issuer = ConfigurationManager.AppSettings["issuer"];
                var secret = TextEncodings.Base64Url.Decode(Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(ConfigurationManager.AppSettings["secret"])));
    
                //用jwt进行身份认证
                app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
                {
                    AuthenticationMode = AuthenticationMode.Active,
                    AllowedAudiences = new[] { "Any" },
                    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]{
            new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
                    }
                });
    
    
                app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
                {
                    //生产环境设为false
                    AllowInsecureHttp = true,
                    //请求token的路径
                    TokenEndpointPath = new PathString("/oauth2/token"),
                    AccessTokenExpireTimeSpan = TimeSpan.FromDays(30),
                    //请求获取token时,验证username, password
                    Provider = new CustomOAuthProvider(),
                    //定义token信息格式 
                    AccessTokenFormat = new CustomJwtFormat(issuer, secret),
                });
    
            }
        }

    3.ConfigureAuth中的 AccessTokenFormat = new CustomJwtFormat(issuer, secret)是自定义token 保存的信息格式, CustomJwtFormat.cs 类代码

    
        /// <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;
            }
    
            public string Protect(AuthenticationTicket data)
            {
                if (data == null)
                {
                    throw new ArgumentNullException(nameof(data));
                }
    
                var signingKey = new HmacSigningCredentials(_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, signingKey));
            }
    
            public AuthenticationTicket Unprotect(string protectedText)
            {
                throw new NotImplementedException();
            }
        }

    4.ConfigureAuth中的 Provider = new CustomOAuthProvider() 是自定义验证username, password 的,可以用它来实现访问数据库的验证业务逻辑,CustomOAuthProvider.cs类代码

        /// <summary>
        /// 自定义 jwt oauth 的授权验证
        /// </summary>
        public class CustomOAuthProvider : OAuthAuthorizationServerProvider
        {
            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>(null);
            }
    
            public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
            {
                context.Validated();
                return Task.FromResult<object>(null);
            }
    
            private static ClaimsIdentity SetClaimsIdentity(OAuthGrantResourceOwnerCredentialsContext context, string userid, string usercode)
            {
                var identity = new ClaimsIdentity("JWT");
                identity.AddClaim(new Claim("userid", userid));
                identity.AddClaim(new Claim("username", usercode));
                return identity;
            }
    
            private static bool CheckCredential(string usernme, string password, out string userid)
            {
                var success = false;
                // 用户名和密码验证
                if (usernme == "admin" && password == "admin")
                {
                    userid = "1";
                    success = true;
                }
                else
                {
                    userid = "";
                }
                return success;
            }
        }

    5.Web.config 添加 issue 和 secret

      <appSettings>
        <add key="issuer" value="test"/>
        <!--32个字符的secret-->
        <add key="secret" value="12345678123456781234567812345678"/>
      </appSettings>

    使用

    强烈建议用 chrome 的 postman 插件来调试

    1. 获取token

    2. 用token请求数据

    header 要添加 Authorization , 值为: Bearer [token], 获取到的 token 替换 [token], 如

    Authorization   Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOiIxIiwidXNlcmNvZGUiOiIxIiwiaXNzIjoidGVzdCIsImF1ZCI6IkFueSIsImV4cCI6MTQ4NzI0MTQ5MCwibmJmIjoxNDg0NjQ5NDkwfQ.RaWlJC3OF0RNz4mLtuW4uQtRKDHF8RXwZwzIcbZoNOo

    JWT缺点

    1. 一旦拿到token, 可用它访问服务器,直到过期,中间服务器无法控制它,如是它失效(有解决方案: 在 token 中保存信息,可添加额外的验证,如加一个 flag, 把数据库对应的flag失效,来控制token有效性)。
    2. token 的过期时间设置很关键,一般把它设到凌晨少人访问时失效,以免用户使用过程中失效而丢失数据。
    3. token保存的信息有限,且都是字符串。

    Demo源码

  • 相关阅读:
    565. Array Nesting
    796. Rotate String
    817. Linked List Components
    696. Count Binary Substrings
    SQL语句优化
    java7增强的try语句关闭资源
    java新手自己实现的计算器,有点乱
    java数组
    java创建不存在路径的文件
    class.forName
  • 原文地址:https://www.cnblogs.com/jjg0519/p/6848073.html
Copyright © 2011-2022 走看看