asp.Netmvcはセッションの代わりにJWTを使用し,単一のログインを実現する

55590 ワード

ASP.NET MVCはセッションの代わりにJWTを使用してワンポイントログインを実現
  • 1. Tokenとは?
  • 2. JWTとは?
  • 3. TokenとSessionの比較
  • 4. ASP.NET MVCはどのようにjwtを使って単点登録
  • を実現します
    1.Tokenとは?
    tokenって何?tokenは、コンピュータ認証でよく用いられるトークンと理解できる.サーバとデータ転送を行う前に、認証が行われます.
    2.JWTとは?
    JWTとは?JWTはJson Web Tokenの略称であり、Tokenの仕様である.暗号化された文字列で、構成部分はA.B.Cです.この文字列は、tokenを記録する暗号化方式、文字列長(A部分)、基本的なユーザ情報、荷重、発行者、期限切れ時間など(B部分)、およびAとBの共通の暗号化部分(C部分)から構成される.
    3.TokenとSessionの比較
    従来のSessionで明らかになった問題Session:ユーザーは、コンピュータのアイデンティティ認証のたびに、サーバメモリにsessionを格納し、クライアントにクッキーを保存して、次回のユーザー要求時に認証を行う.しかし、これで2つの問題が明らかになった.1つ目の問題は、sessionがサーバに格納されたメモリであり、要求されたユーザ数が増加すると、サーバの圧力が増大することである.2つ目の問題は、複数のサーバが存在し、sessionが現在の1つのサーバにしか格納できない場合、分散開発には適用されません.
    CSRF:Sessionはcookieに基づいてユーザ識別を行い,cookieがキャプチャされるとユーザはクロスステーション要求偽造攻撃を受けやすくなり,本稿ではcsrf(cross site request forgery)をしばらく考慮しない.
    Tokenの検証メカニズムtokenの検証は、サーバ側にユーザ情報を保持する必要がないため、ユーザ再クライアントが単一のログインを通過した後、複数のサーバにアクセスでき、分散開発に有利である.またtokenは暗号化された文字列で、期限切れを設定でき、模倣されにくい.
    tokenを使用すると、クライアントとサービス側のインタラクティブなプロセスは大体次のようになります.
  • ユーザは、ユーザ名パスワードを使用してサーバ
  • に要求する.
  • サーバは、ユーザの情報
  • を検証する.
  • サーバは、認証によってユーザにtoken
  • を送信する.
  • クライアントはtokenを格納し、要求のたびにこのtoken値
  • を添付する.
  • サービス側はtoken値を検証し、データ
  • を返す.
    tokenはクッキーに保存してもよいし、リクエストヘッダに保存してもよいし、tokenをリクエストヘッダに配置し、macアドレスとマシン名を携帯することを推奨する.
    4. ASP.NET MVCはどのようにjwtを使って単点上陸を実現します
    UserStateクラスの定義
    namespace LYQ.TokenDemo.Models.Infrastructure
    {
        public class UserState
        {
            public string UserName { get; set; }
            public string UserID { get; set; }
            public int Level { get; set; }
        }
    }
    

    AppManagerクラスとTokenInfoクラスの定義
       public static UserState UserState
            {
                get
                {
                    HttpContext httpContext = HttpContext.Current;
                    var cookie = httpContext.Request.Cookies[Key.AuthorizeCookieKey];
                    var tokenInfo = cookie?.Value ?? "";
                    //token   
                    var encodeTokenInfo = TokenHelper.GetDecodingToken(tokenInfo);
                    UserState userState = JsonHelper<UserState>.JsonDeserializeObject(encodeTokenInfo);               
                    return userState;
                }
            }
            
     public class TokenInfo
        {
            public TokenInfo()
            {
                iss = "LYQ";
                iat = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
                exp = iat + 300;
                aud = "";
                sub = "LYQ.VIP";
                jti = "LYQ." + DateTime.Now.ToString("yyyyMMddhhmmss");
            }
    
            public string iss { get; set; }
            public double iat { get; set; }
            public double exp { get; set; }
            public string aud { get; set; }
            public double nbf { get; set; }
            public string sub { get; set; }
            public string jti { get; set; }
    
        }
    

    JsonHelperの定義
     public class JsonHelper<T> where T : class
        {
            public static T JsonDeserializeObject(string json)
            {
                return JsonConvert.DeserializeObject<T>(json);
            }
    
            public static string JsonSerializeObject(object obj)
            {
                return JsonConvert.SerializeObject(obj);
            }
        }
    

    ホームコントローラでLoginを定義する方法
            [HttpGet]
            [LYQ.TokenDemo.Models.CustomAttribute.Authorize(false)]
            public ActionResult Login()
            {
                return View();
            }
    
            [HttpPost]
            [LYQ.TokenDemo.Models.CustomAttribute.Authorize(false)]
            public ActionResult Login(string account, string password)
            {
                if (account == "Tim" && password == "abc123")
                {
                    var cookie = new HttpCookie(Key.AuthorizeCookieKey, TokenHelper.GenerateToken());
                    HttpContext.Response.Cookies.Add(cookie);
                    return Json("y");
                }
                else
                {
                    var cookie = new HttpCookie(Key.AuthorizeCookieKey, "");
                    HttpContext.Response.Cookies.Add(cookie);
                    return Json("n");
                }
            }
    

    生成tokenはNuGetを使用してJWTをダウンロードする.dll
    namespace LYQ.TokenDemo.Models
    {
        public class TokenHelper
        {
            //jwt  ,    
            private const string SecretKey = "LYQ.abcqwe123";
    
            public static string GenerateToken()
            {
                var tokenInfo = new TokenInfo();
                var payload = new Dictionary<string, object>
                {
                    {"iss", tokenInfo.iss},
                    {"iat", tokenInfo.iat},
                    {"exp", tokenInfo.exp},
                    {"aud", tokenInfo.aud},
                    {"sub", tokenInfo.sub},
                    {"jti", tokenInfo.jti},
                    { "userName", "Tim" },
                    { "userID", "001" },
                    { "level",18}
                };
    
                IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
                IJsonSerializer serializer = new JsonNetSerializer();
                IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
                IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
               
                var token = encoder.Encode(payload, SecretKey);
                return token;
            }
    
            public static string GetDecodingToken(string strToken)
            {
                try
                {
                    IJsonSerializer serializer = new JsonNetSerializer();
                    IDateTimeProvider provider = new UtcDateTimeProvider();
                    IJwtValidator validator = new JwtValidator(serializer, provider);
                    IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
                    IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);
    
                    var json = decoder.Decode(strToken, SecretKey, verify: true);
                    return json;
                }
                catch (Exception)
                {
                    return "";
                }
            }
        }
    }
    

    カスタムアイデンティティ認証ここでは、AuthorizeAttributeをカスタマイズするカスタムアイデンティティ認証モードを採用します.
    namespace LYQ.TokenDemo.Models.CustomAttribute
    {
        public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
        {
            public AuthorizeAttribute(bool _isCheck = true)
            {
                this.isCheck = _isCheck;
            }
    
            private bool isCheck { get; }
    
            public void OnAuthorization(AuthorizationContext filterContext)
            {
                var httpContext = filterContext.HttpContext;
                var actionDescription = filterContext.ActionDescriptor;
    
                if (actionDescription.IsDefined(typeof(AllowAnonymousAttribute), false) ||
                    actionDescription.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), false)) { return; }
    
                if (!isCheck) return;
    
                if (AppManager.UserState == null)
                {
                    if (httpContext.Request.IsAjaxRequest())
                    {
                        filterContext.Result = new JsonResult()
                        {
                            Data = new { Status = "Fail", Message = "403 Forbin", StatusCode = "403" },
                            JsonRequestBehavior = JsonRequestBehavior.AllowGet
                        };
                    }
                    else
                    {
                        filterContext.Result = new RedirectResult(("/Home/Login"));
                    }
                }
                else
                {
                    //         ,        token    
                    var cookie = new HttpCookie(Key.AuthorizeCookieKey, TokenHelper.GenerateToken());
                    filterContext.HttpContext.Response.Cookies.Add(cookie);
                }
            }
        }
    }
    

    HTMLページ
    @{
        ViewBag.Title = "Login";
    }
    
    <link href="~/Content/bootstrap.min.css" rel="stylesheet" />
    
    <h2>This is login page.</h2>
    
    <div class="container">
        <form class="box-body" action="/Home/Login" method="post">
            <div class="form-group row">
                <label class="col-sm-1 col-md-1">Account:</label>
                <div class="col-sm-5 col-md-5">
                    <input type="text" class="form-control" id="account" name="account" />
                </div>
            </div>
            <div class="form-group row">
                <label class="col-sm-1 col-md-1">Password:</label>
                <div class="col-sm-5 col-md-5">
                    <input type="password" class="form-control" id="password" name="password" />
                </div>
            </div>
            <div class="form-group row">
                <div class="col-sm-1 col-md-1"></div>
                <div class="col-sm-5 col-md-5">
                    <button type="button" class="btn btn-info" onclick="Login();">Login</button>
                    <button type="reset" class="btn btn-info">Reset</button>
                </div>
            </div>
            <div class="form-group row">
                <div class="col-sm-1 col-md-1"></div>
                <div class="col-sm-5 col-md-5">
                    <span>account:Tim; password:abc123</span>
                </div>
            </div>
        </form>
        
    </div>
    
    <script src="~/Scripts/jquery-3.3.1.min.js"></script>
    <script src="~/StaticFiles/Frontend/Scripts/Common.js"></script>
    
    <script>
    
        function Login() {
            var paras =
            {
                account: $("#account").val(),
                password: $("#password").val()
            };
    
            LYQ.sendAjaxRequest({
                type: "post",
                url: "/Home/Login",
                param: paras,
                dataType: "json",
                callBack: function (result) {
                    if (result == "y") {
                        console.log("Login success");
                        alert("Login success");
                        window.location = "/";
                    } else {
                        console.log("Login fail");
                        alert("Login fail");
                    }
                }
            });     
        }
    
    </script>
    

    Common.js
    !(function (window) {
        var functions = {
            sendAjaxRequest: function (opts) {
                var self = this;
                $.ajax({
                    type: opts.type || "post",
                    url: opts.url,
                    data: opts.param || {},
                    contentType: opts.contentType === null ? true : opts.contentType,
                    cache: opts.cache === null ? true : opts.cache,
                    processData: opts.processData === null ? true : opts.processData,
                    beforeSend: function (XMLHttpRequest) {
                        XMLHttpRequest.setRequestHeader(LYQ.getAuthorizationKey(), "");
                    },
                    dataType: opts.dataType || "json",
                    success: function (result) {
                        if (Object.prototype.toString.call(opts.callBack) === "[object Function]") {   //  callback     function               
                            opts.callBack(result);
                        } else {
                            console.log("CallBack is not a function");
                        }
                    }
                });
            },
            getRequestHeaderAuthorizationToken: function () {
                var document_cookie = document.cookie;
                //var reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
                //if (document_cookie = document.cookie.match(reg))
                //    return unescape(arr[2]);
                //else
                //    return null;
                console.log(document_cookie);
                return document_cookie;
            },
            getAuthorizationKey: function () {
                return 'Authorization';
            }
        };
    
        window.LYQ = functions;
    })(this);
    

    ソースアドレス:
  • Github:https://github.com/Lyq1454759684/Token-Demo
  • コードクラウド:https://gitee.com/Tim_Lee_ZhongShan/Token-Demo