2017-07-09 11 views
1

私はMVCアプリケーションでJWT認証を作成したかったのです。トークンを正しく返すWeb APIのAuthorization Webサービスを作っています。その後、私はクッキーにトークンを保存しようとしています。MVC 5のCookieにJWTトークンを格納しています

[HttpPost] 
    public async Task<ActionResult> Login(LoginDto loginDto) 
    { 
     var token = await loginService.GetToken(loginDto); 

     if (!string.IsNullOrEmpty(token)) 
     { 
      var cookie = new System.Web.HttpCookie("token", token) 
      { 
       HttpOnly = true 
      }; 
      Response.Cookies.Add(cookie); 
      return RedirectToAction("Index", "Product"); 
     } 
     return View("LoginFailed"); 
    } 

しかし、今はすべてのリクエストに対してこのトークンをヘッダーに追加したいと考えました。だから私はアクションフィルタがこれを達成するのに最適だろうと決めました。

public class CustomActionFilter : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     var token = filterContext.HttpContext.Request.Cookies.Get("token"); 

     if (token != null) 
      filterContext.HttpContext.Request.Headers.Add("Authorization", $"Bearer {token}"); 

     base.OnActionExecuting(filterContext); 
    } 
} 

スタートアップ

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     AutofacConfig.Configure(); 
     AreaRegistration.RegisterAllAreas(); 
     RouteConfig.RegisterRoutes(RouteTable.Routes); 
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 

     ConfigureOAuth(app); 
    } 

    public void ConfigureOAuth(IAppBuilder app) 
    { 
     var issuer = System.Configuration.ConfigurationManager.AppSettings["issuer"]; 
     var audience = System.Configuration.ConfigurationManager.AppSettings["appId"]; 
     var secret = TextEncodings.Base64Url.Decode(System.Configuration.ConfigurationManager.AppSettings["secret"]); 

     app.UseJwtBearerAuthentication(
      new JwtBearerAuthenticationOptions 
      { 
       AuthenticationMode = AuthenticationMode.Active, 
       AllowedAudiences = new[] { audience }, 
       IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[] 
       { 
        new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret) 
       }, 

      }); 

    } 
} 

そして私はちょうど属性を認可コントローラをマーク。 POSTMANで呼び出すとうまく動作します。

しかし、MVCのアクションフィルタは、認証フィルタの後に常に起動されます。だから私は質問がある:

  1. どのようにすべての要求にクッキーからトークンを追加するには?いい練習ですか?もし私が何をすべきか?
  2. csrf攻撃などはどうですか? AntiForgeryTokenAttrは作業を行いますか?それでは、ajaxは何を呼びますか?

答えて

0

解決策が見つかりました。これは、どのように、ログインサービスルックスで

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     AutofacConfig.Configure(); 
     AreaRegistration.RegisterAllAreas(); 
     RouteConfig.RegisterRoutes(RouteTable.Routes); 
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 

     ConfigureOAuth(app); 
    } 

    public void ConfigureOAuth(IAppBuilder app) 
    { 
     var issuer = System.Configuration.ConfigurationManager.AppSettings["issuer"]; 
     var audience = System.Configuration.ConfigurationManager.AppSettings["appId"]; 
     var secret = TextEncodings.Base64Url.Decode(System.Configuration.ConfigurationManager.AppSettings["secret"]); 

     app.UseJwtBearerAuthentication(
      new JwtBearerAuthenticationOptions 
      { 
       AuthenticationMode = AuthenticationMode.Active, 
       AllowedAudiences = new[] { audience }, 
       IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[] 
       { 
        new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret) 
       }, 
       Provider = new MvcJwtAuthProvider() // override custom auth 

      }); 

    } 
} 
+0

そのひどい_one-undoumented-MS-auth-API-over-the-other-mess_あなたのアプローチは有望ですね。共有ありがとう!私はパズルの一部を欠落しているだけです: '' loginService.GetToken'''をどこから取得するのですか? – Michael

+0

コメントセクションにこのログインサービスの実装を表示するのが難しいため、別の回答を追加しました。 – TjDillashaw

0

@Michael を私はカスタムOAuthBearerAuthenticationProviderプロバイダを作成し、startup.csの内側に、このクラスの中イムは、クッキーからトークンを取得し、context.Token

public class MvcJwtAuthProvider : OAuthBearerAuthenticationProvider 
{ 
    public override Task RequestToken(OAuthRequestTokenContext context) 
    { 
     var token = context.Request.Cookies.SingleOrDefault(x => x.Key == "token").Value; 

     context.Token = token; 
     return base.RequestToken(context); 
    } 
} 

にこれを割り当て、好き。 authエンドポイントを呼び出すだけです。

public class LoginService : ILoginService 
{ 
    public async Task<string> GetToken(LoginDto loginDto) 
    { 
     var tokenIssuer = ConfigurationManager.AppSettings["issuer"]; 
     using (var httpClient = new HttpClient {BaseAddress = new Uri($"{tokenIssuer}/oauth2/token")}) 
     { 
      using (var response = await httpClient.PostAsync(httpClient.BaseAddress, new FormUrlEncodedContent(
       new List<KeyValuePair<string, string>> 
       { 
        new KeyValuePair<string, string>("username", loginDto.Username), 
        new KeyValuePair<string, string>("password", loginDto.Password), 
        new KeyValuePair<string, string>("grant_type", "password"), 
        new KeyValuePair<string, string>("client_id", ConfigurationManager.AppSettings["appId"]) 
       }))) 
      { 
       var contents = await response.Content.ReadAsStringAsync(); 

       if (response.StatusCode == HttpStatusCode.OK) 
       { 
        var deserializedResponse = 
         new JavaScriptSerializer().Deserialize<Dictionary<string, string>>(contents); 

        var token = deserializedResponse["access_token"]; 

        return token; 
       } 
      } 
      return null; 
     } 
    } 
} 
+0

呼び出しごとに新しいHttpClientを作成しないでください。それは再利用されることを意味します。 [docs](https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=netframework-4.7) –

+0

を参照してください。そんなこと知ってる。しかし、このプロジェクトはテストのためだけのものでした。 – TjDillashaw

関連する問題