نظرات مطالب
ASP.NET MVC #17

عنوان خطا: The anti-forgery token could not be decrypted

جزئیات خطا

The anti-forgery token could not be decrypted. 
If this application is hosted by a Web Farm or cluster,
ensure that all machines are running the same version of ASP.NET Web Pages and
that the <machineKey> configuration specifies explicit encryption and validation keys. 
AutoGenerate cannot be used in a cluster

Description: An unhandled exception occurred during the execution of the current web request. 
Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Web.Mvc.HttpAntiForgeryException: 
The anti-forgery token could not be decrypted.
If this application is hosted by a Web Farm or cluster, 
ensure that all machines are running the same version of ASP.NET Web Pages and that the <machineKey> configuration 
specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.
شرایط  برنامه:  در زمان ذخیره سازی اطلاعات.(در Mvc دات نت فریم ورک 4.5 و Vs2013)

موارد مشابه در سایر انجمن‌ها : اینجا و اینجا و ...

نتیجه حاصله: درج MachinKey در WebConfig برای مثال:

<system.web>
  <machineKey validationKey="mykey" decryptionKey="myotherkey" validation="SHA1" decryption="AES" />
</system.web>
سوال: علت بروز خطا بر روی بعضی از سیستم‌ها چیست؟ چرا همیشه این خطا نمایش داده نمی‌شود. چرا پیغام نمی‌توانم Decrypt کنم؟ پس چرا در همین برنامه ولی دربخش دیگری که از anti-forgery token استفاده شده مشکل وجود ندارد؟
اشتراک‌ها
0.Visual Studio 2017 15.9 منتشر شد

Summary of Notable New Features in 15.9

Top Issues Fixed in 15.9

0.Visual Studio 2017 15.9 منتشر شد
اشتراک‌ها
نگاهی به مشخصات SQL Server 2019

SQL Server 2019 is designed to solve challenges of the modern data professional including:

  • Store enterprise data in a data lake and offer SQL and Spark query capability overall data
  • Reduce the need for Extract, Transform, and Load (ETL) applications by eliminating data movement
  • Integrate and secure machine learning applications with scalable performance
  • Reduce the need for application and query changes to gain a boost in performance
  • Increase confidential computing of data through hardware enclaves
  • Increase application and database uptime and availability through features like ADR (Advanced Database Recovery)
  • Extend the power of the T-SQL language in a secure and robust fashion
  • Run applications and deploy databases across multiple operating systems and platforms with compatibility
  • Reduce the risk of upgrades while using new SQL capabilities when you are ready though inbuilt database compatibility levels 
نگاهی به مشخصات SQL Server 2019
نظرات مطالب
معرفی JSON Web Token
- بله. کلاس نمونه User مطلب «پیاده سازی JSON Web Token با ASP.NET Web API 2.x» را با کلاس User مربوط به پیاده سازی خاص خودتان جایگزین کنید (در قسمت «حداقل‌های بانک اطلاعاتی مورد نیاز جهت ذخیره سازی وضعیت کاربران و توکن‌های آن‌ها» مطلب یاد شده).
- برای سفارشی سازی ASP.NET Identity از مطلب «اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity» ایده بگیرید.
مطالب
React 16x - قسمت 27 - احراز هویت و اعتبارسنجی کاربران - بخش 2 - استخراج و نمایش اطلاعات JWT و خروج از سیستم
در قسمت قبل، در هر دو حالت ثبت نام یک کاربر جدید و همچنین ورود به سیستم، یک JSON Web Token را از سرور دریافت کرده و در local storage مرورگر، ذخیره کردیم. اکنون قصد داریم محتوای این توکن را استخراج کرده و از آن جهت نمایش اطلاعات کاربر وارد شده‌ی به سیستم، استفاده کنیم. همچنین کار بهبود کیفیت کدهایی را هم که تاکنون پیاده سازی کردیم، انجام خواهیم داد.


نگاهی به محتوای JSON Web Token تولیدی

اگر مطلب قسمت قبل را پیگیری کرده باشید، پس از لاگین، یک چنین خروجی را در کنسول توسعه دهندگان مرورگر می‌توان مشاهده کرد که همان return Ok(new { access_token = jwt }) دریافتی از سمت سرور است:


اکنون این رشته‌ی طولانی را در حافظه کپی کرده و سپس به سایت https://jwt.io/#debugger-io مراجعه و در قسمت دیباگر آن، این رشته‌ی طولانی را paste می‌کنیم تا آن‌را decode کند:


برای نمونه payload آن حاوی یک چنین اطلاعاتی است:
{
  "jti": "b2921057-32a4-fbb2-0c18-5889c1ab8e70",
  "iss": "https://localhost:5001/",
  "iat": 1576402824,
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "1",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Vahid N.",
  "DisplayName": "Vahid N.",
  "http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata": "1",
  "http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Admin",
  "nbf": 1576402824,
  "exp": 1576402944,
  "aud": "Any"
}
در اینجا یک‌سری از اطلاعات کاربر، مانند id ، name ، DisplayName و یا حتی role او درج شده‌است؛ به همراه تاریخ صدور (iat) و انقضای (exp) این token که به صورت Unix time format بیان می‌شوند. به هر کدام از این خواصی که در اینجا ذکر شده‌اند، یک user claim گفته می‌شود. به عبارتی، این token ادعا می‌کند (claims) که نقش کاربر وارد شده‌ی به سیستم، Admin است. برای بررسی صحت این ادعا نیز یک امضای دیجیتال (مشخص شده‌ی با رنگ آبی) را به همراه این توکن سه قسمتی (قسمت‌های مختلف آن، با 2 نقطه از هم جدا شده‌اند که در تصویر نیز با سه رنگ متمایز، مشخص است)، ارائه کرده‌است. به این معنا که اگر قسمتی از اطلاعات این توکن، در سمت کاربر دستکاری شود، دیگر در سمت سرور تعیین اعتبار مجدد نخواهد شد؛ چون نیاز به یک امضای دیجیتال جدید را دارد که کلیدهای خصوصی تولید آن، تنها در سمت سرور مهیا هستند و به سمت کلاینت ارسال نمی‌شوند.


استخراج اطلاعات کاربر وارد شده‌ی به سیستم، از JSON Web Token دریافتی

همانطور که در payload توکن دریافتی از سرور نیز مشخص است، اطلاعات ارزشمندی از کاربر، به همراه آن ارائه شده‌اند و مزیت کار با آن، عدم نیاز به کوئری گرفتن مداوم از سرور و بانک اطلاعاتی، جهت دریافت مجدد این اطلاعات است. بنابراین اکنون در برنامه‌ی React خود، قصد داریم مشابه کاری را که سایت jwt.io انجام می‌دهد، پیاده سازی کرده و به این اطلاعات دسترسی پیدا کنیم و برای مثال DisplayName را در Navbar نمایش دهیم. برای این منظور فایل app.js را گشوده و تغییرات زیر را به آن اعمال می‌کنیم:
- می‌خواهیم اطلاعات کاربر جاری را در state کامپوننت مرکزی App قرار دهیم. سپس زمانیکه کار رندر کامپوننت NavBar درج شده‌ی در متد رندر آن فرا می‌رسد، می‌توان این اطلاعات کاربر را به صورت props به آن ارسال کرد؛ و یا به هر کامپوننت دیگری در component tree برنامه.
- بنابراین ابتدا کامپوننت تابعی بدون حالت App را تبدیل به یک کلاس کامپوننت استاندارد مشتق شده‌ی از کلاس پایه‌ی Component می‌کنیم. اکنون می‌توان state را نیز به آن اضافه کرد:
class App extends Component {
  state = {};
- سپس متد componentDidMount را به این کامپوننت اضافه می‌کنیم؛ در آن ابتدا token ذخیره شده‌ی در local storage را دریافت کرده و سپس decode می‌کنیم تا payload اطلاعات کاربر وارد شده‌ی به سیستم را استخراج کنیم. در آخر state را توسط این اطلاعات به روز می‌کنیم.
- برای decode کردن توکن، نیاز به نصب کتابخانه‌ی زیر را داریم:
> npm install --save jwt-decode
- پس از نصب آن، ابتدا امکانات آن‌را import کرده و سپس از آن در متد componentDidMount استفاده می‌کنیم:
import jwtDecode from "jwt-decode";
// ...

class App extends Component {
  state = {};

  componentDidMount() {
    try {
      const jwt = localStorage.getItem("token");
      const currentUser = jwtDecode(jwt);
      console.log("currentUser", currentUser);
      this.setState({ currentUser });
    } catch (ex) {
      console.log(ex);
    }
  }
ابتدا آیتمی با کلید token از localStorage استخراج می‌شود. سپس توسط متد jwtDecode، تبدیل به یک شیء حاوی اطلاعات کاربر جاری وارد شده‌ی به سیستم گشته و در آخر در state درج می‌شود. در اینجا درج try/catch ضروری است؛ از این جهت که متد jwtDecode، در صورت برخورد به توکنی غیرمعتبر، یک استثناء را صادر می‌کند و این استثناء نباید بارگذاری برنامه را با اخلال مواجه کند. از این جهت که اگر توکنی غیرمعتبر است (و یا حتی در localStorage وجود خارجی ندارد؛ برای کاربران لاگین نشده)، کاربر باید مجددا برای دریافت نمونه‌ی معتبر آن، لاگین کند.

- اکنون می‌توان شیء currentUser را به صورت props، به کامپوننت NavBar ارسال کرد:
  render() {
    return (
      <React.Fragment>
        <ToastContainer />
        <NavBar user={this.state.currentUser} />
        <main className="container">


نمایش اطلاعات کاربر وارد شده‌ی به سیستم در NavBar

پس از ارسال شیء کاربر به صورت props به کامپوننت src\components\navBar.jsx، کدهای این کامپوننت را به صورت زیر جهت نمایش نام کاربر جاری وارد شده‌ی به سیستم تغییر می‌دهیم:
const NavBar = ({ user }) => {
چون این کامپوننت به صورت یک کامپوننت تابعی بدون حالت تعریف شده، برای دریافت props می‌توان یا آن‌را به صورت مستقیم به عنوان پارامتر تعریف کرد و یا خواص مدنظر را با استفاده از Object Destructuring به عنوان پارامتر دریافت نمود.
سپس می‌توان لینک‌های Login و Register را به صورت شرطی رندر کرد و نمایش داد:
{!user && (
  <React.Fragment>
    <NavLink className="nav-item nav-link" to="/login">
      Login
    </NavLink>
    <NavLink className="nav-item nav-link" to="/register">
      Register
    </NavLink>
  </React.Fragment>
)}
در اینجا اگر شیء user تعریف شده باشد (یعنی کاربر، توکن ذخیره شده‌ای در local storage داشته باشد)، دیگر لینک‌های login و register نمایش داده نمی‌شوند. به علاوه برای اعمال && به چند المان React، نیاز است آن‌ها را داخل یک والد، مانند React.Fragment محصور کرد.

شبیه به همین حالت را برای هنگامیکه کاربر، تعریف شده‌است، جهت نمایش نام او و لینک به Logout، نیاز داریم:
{user && (
  <React.Fragment>
    <NavLink className="nav-item nav-link" to="/logout">
      Logout
    </NavLink>
    <NavLink className="nav-item nav-link" to="/profile">
      {user.DisplayName}
    </NavLink>
  </React.Fragment>
)}
user.DisplayName درج شده‌ی در اینجا، اطلاعات خودش را از payload توکن decode شده‌ی دریافتی از سرور، تامین می‌کند؛ با این خروجی:


فعلا تا پیش از پیاده سازی Logout، برای آزمایش آن، به کنسول توسعه دهندگان مرورگر مراجعه کرده و توکن ذخیره شده‌ی در ذیل قسمت application->storage را دستی حذف کنید. سپس صفحه را ریفرش کنید. اینبار لینک‌های به Login و Register نمایان می‌شوند.
یک مشکل! در این حالت (زمانیکه توکن حذف شده‌است)، از طریق قسمت Login به برنامه وارد شوید. هرچند این قسمت‌ها به درستی کار خود را انجام می‌دهند، اما هنوز در منوی بالای سایت، نام کاربری و لینک به Logout ظاهر نشده‌اند. علت اینجا است که در کامپوننت App، کار دریافت توکن در متد componentDidMount انجام می‌شود و این متد نیز تنها یکبار در طول عمر برنامه فراخوانی می‌شود. برای رفع این مشکل به src\components\loginForm.jsx مراجعه کرده و بجای استفاده از history.push برای هدایت کاربر به صفحه‌ی اصلی برنامه، نیاز خواهیم داشت تا کل برنامه را بارگذاری مجدد کنیم. یعنی بجای:
this.props.history.push("/");
باید از سطر زیر استفاده کرد:
window.location = "/";
این سطر سبب full page reload برنامه شده و در نتیجه متد componentDidMount کامپوننت App، یکبار دیگر فراخوانی خواهد شد. شبیه به همین کار را در کامپوننت src\components\registerForm.jsx نیز باید انجام داد.


پیاده سازی Logout کاربر وارد شده‌ی به سیستم

برای logout کاربر تنها کافی است توکن او را از local storage حذف کنیم. به همین جهت مسیریابی جدید logout را که به صورت لینکی به NavBar اضافه کردیم:
<NavLink className="nav-item nav-link" to="/logout">
   Logout
</NavLink>
به فایل src\App.js اضافه می‌کنیم.
import Logout from "./components/logout";
// ...

class App extends Component {
  render() {
    return (
          // ...
          <Switch>
            // ...
            <Route path="/logout" component={Logout} />
البته برای اینکار نیاز است کامپوننت جدید src\components\logout.jsx را با محتوای زیر ایجاد کنیم:
import { Component } from "react";

class Logout extends Component {
  componentDidMount() {
    localStorage.removeItem("token");
    window.location = "/";
  }

  render() {
    return null;
  }
}

export default Logout;
که در متد componentDidMount آن، کار حذف توکن ذخیره شده‌ی در localStorage انجام شده و سپس کاربر را با یک full page reload، به ریشه‌ی سایت هدایت می‌کنیم.


بهبود کیفیت کدهای نوشته شده

اگر به کامپوننت App دقت کنید، کلید token استفاده شده‌ی در آن، در چندین قسمت برنامه مانند login و logout، تکرار و پراکنده شده‌است. بنابراین بهتر است جزئیات پیاده سازی مرتبط با اعتبارسنجی کاربران، به ماژول مختص به آن‌ها (src\services\authService.js) منتقل شود تا سایر قسمت‌های برنامه، به صورت یک‌دستی از آن استفاده کنند و اگر در این بین نیاز به تغییری بود، فقط یک ماژول نیاز به تغییر، داشته باشد.
برای این منظور، ابتدا متد login قبلی را طوری تغییر می‌دهیم که کار ذخیره سازی توکن را نیز در authService.js انجام دهد:
const tokenKey = "token";

export async function login(email, password) {
  const {
    data: { access_token }
  } = await http.post(apiEndpoint + "/login", { email, password });
  console.log("JWT", access_token);
  localStorage.setItem(tokenKey, access_token);
}
سپس متد doSumbit کامپوننت src\components\loginForm.jsx، به صورت زیر ساده می‌شود:
const { data } = this.state;
await auth.login(data.username, data.password);
window.location = "/";

همین‌کار را برای logout نیز در authService انجام داده:
export function logout() {
  localStorage.removeItem(tokenKey);
}
و در ادامه متد componentDidMount کامپوننت Logout را برای استفاده‌ی از آن، اصلاح می‌کنیم:
import * as auth from "../services/authService";

class Logout extends Component {
  componentDidMount() {
    auth.logout();

منطق دریافت اطلاعات کاربر جاری نیز باید به authService منتقل شود؛ چون مسئولیت دریافت توکن و سپس decode آن، نباید به کامپوننت App واگذار شود:
import jwtDecode from "jwt-decode";
//...

export function getCurrentUser() {
  try {
    const jwt = localStorage.getItem(tokenKey);
    const currentUser = jwtDecode(jwt);
    console.log("currentUser", currentUser);
    return currentUser;
  } catch (ex) {
    console.log(ex);
    return null;
  }
}
سپس متد componentDidMount کامپوننت App، به صورت زیر خلاصه خواهد شد:
import * as auth from "./services/authService";

class App extends Component {
  state = {};

  componentDidMount() {
    const currentUser = auth.getCurrentUser();
    this.setState({ currentUser });
  }

جای دیگری که از localStorage استفاده شده، متد doSumbit کامپوننت ثبت نام کاربران است. این قسمت را نیز به صورت زیر به authService اضافه می‌کنیم:
export function loginWithJwt(jwt) {
   localStorage.setItem(tokenKey, jwt);
}
سپس ابتدای متد doSumbit را برای استفاده‌ی از آن به صورت زیر تغییر می‌دهیم:
import * as auth from "../services/authService";
// ...
const response = await userService.register(this.state.data);
auth.loginWithJwt(response.headers["x-auth-token"]);


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: sample-27-backend.zip و sample-27-frontend.zip
اشتراک‌ها
کتاب رایگان JWT Handbook

Ever wondered how JWT came to be and what problems it was designed to tackle? Are you curious about the plethora of algorithms available for signing and encrypting JWTs? Or are you interested in getting up-to-speed with JWTs as soon as possible? Then this handbook is for you. 

کتاب رایگان JWT Handbook
اشتراک‌ها
پروژه ConfuserEx

ConfuserEx is an free, open-source protector for .NET applications. It is the successor of Confuser project. 

پروژه ConfuserEx
مطالب
زیر نویس فارسی ویدیوهای ساخت برنامه‌های مترو توسط سی شارپ و XAML - قسمت چهارم

زیرنویس‌های فارسی قسمت چهارم «Building Windows 8 Metro Apps in C# and XAML» را از اینجا و یا اینجا می‌تونید دریافت کنید.

لیست سرفصل‌های قسمت چهارم به شرح زیر است:

List Controls  00:31:14 
This module shows the Metro controls available to XAML applications for working with collections of items.
This includes the new GridView and ListView controls,
which are optimized for handling collections in a touch-based user interface.

Introduction
Items Controls
Demo: ListBox vs ListView
Demo: GridView
Demo: FlipView
Common ItemsControl
Semantic Zoom
Demo: JumpViewer
Summary

این قسمت به بررسی یک سری کنترل لیستی جدید ویندوز 8 اختصاص دارد شامل ListView بازنویسی شده و همچنین GridView به همراه دو کنترل FlipView و JumpViewer که تمام این‌ها جهت کار با صفحات لمسی بهینه سازی شده‌اند.

اشتراک‌ها
معرفی Relay و GraphQL

Data fetching for React applications

What is Relay? 

Relay is a new framework from Facebook that provides data-fetching functionality for React applications. It was announced at React.js Conf (January 2015).

What is GraphQL? 

GraphQL is a data querying language designed to describe the complex, nested data dependencies of modern applications. It's been in production use in Facebook's native apps for several years. 

معرفی Relay و GraphQL
اشتراک‌ها
مصاحبه ای خواندنی با یکی از بنیانگذاران شرکت Telerik
نکات مهم از داخل متن:
Telerik introduced the Telerik Platform
In the European Union alone, income from the development of mobile applications is estimated at 17.5 billion euros for 2013, according to a report published by the European Commission
Three weeks after releasing the first version[Telerik Platform], it achieved over 61 thousand registrations. Eight thousand people joined the online presentation of the platform 
و در نهایت پارگراف آخر
مصاحبه ای خواندنی با یکی از بنیانگذاران شرکت Telerik