zoukankan      html  css  js  c++  java
  • jwt 无状态分布式授权

    基于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 类代码

    /// /// 自定义 jwt token 的格式 
        /// 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类代码

    /// /// 自定义 jwt oauth 的授权验证
        /// 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>
        <addkey="issuer"value="test"/>
        <addkey="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保存的信息有限,且都是字符串。
  • 相关阅读:
    PAT (Advanced Level) Practice 1100 Mars Numbers (20分)
    PAT (Advanced Level) Practice 1107 Social Clusters (30分) (并查集)
    PAT (Advanced Level) Practice 1105 Spiral Matrix (25分)
    PAT (Advanced Level) Practice 1104 Sum of Number Segments (20分)
    PAT (Advanced Level) Practice 1111 Online Map (30分) (两次迪杰斯特拉混合)
    PAT (Advanced Level) Practice 1110 Complete Binary Tree (25分) (完全二叉树的判断+分享致命婴幼儿错误)
    PAT (Advanced Level) Practice 1109 Group Photo (25分)
    PAT (Advanced Level) Practice 1108 Finding Average (20分)
    P6225 [eJOI2019]异或橙子 树状数组 异或 位运算
    P4124 [CQOI2016]手机号码 数位DP
  • 原文地址:https://www.cnblogs.com/zxh1919/p/7670036.html
Copyright © 2011-2022 走看看