【问题标题】:Authentication in ASP.Net Core 5ASP.Net Core 5 中的身份验证
【发布时间】:2021-05-31 21:16:46
【问题描述】:

我在 VS 2019 中使用 React 模板创建了 asp.net 核心,我需要授权一个控制器方法,所以我首先在 Azure AD 上注册了我的应用程序,然后我使用了这个 Startup.cs 配置:

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
         .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
         .EnableTokenAcquisitionToCallDownstreamApi()
         .AddInMemoryTokenCaches();

            services.AddControllersWithViews().AddMicrosoftIdentityUI();

            // In production, the React files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/build";
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseSpaStaticFiles();

            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller}/{action=Index}/{id?}");
            });

            app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "ClientApp";

                if (env.IsDevelopment())
                {
                    spa.UseReactDevelopmentServer(npmScript: "start");
                }
            });
        }
    }

在控制器中我使用 AuthorizeForScopes 和 ITokenAcquisition 如下

    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {  
        private readonly ITokenAcquisition tokenAcquisition;
        public WeatherForecastController(ITokenAcquisition tokenAcquisition)
        {
            this.tokenAcquisition = tokenAcquisition;
        }

        [AuthorizeForScopes(Scopes = new[] { "https://tenantname.sharepoint.com/AllSites.FullControl" })]
        [HttpGet]
        public async Task<IEnumerable<WeatherForecast>> Get()
        {
            string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new[] { "https://tenantname.sharepoint.com/AllSites.FullControl" });
           
            ......
            ......
        }
    }

但是当我尝试获取数据时出现 CORS 错误

你能帮帮我吗

【问题讨论】:

  • 您是否在 Azure 中设置了 CORS?
  • 是的,使用 * 通配符

标签: c# reactjs asp.net-core azure-active-directory openid-connect


【解决方案1】:

关于问题,请参考以下步骤

  1. 注册客户端应用和服务端应用

  2. 使用React project template with ASP.NET Core

  3. 客户端应用程序

一个。安装 msal

npm install msal

b.定义MsalAuthProvider

import React, { Component } from 'react';
import { UserAgentApplication } from 'msal';

const msalConfig = {
    authority: 'https://login.microsoftonline.com/common',
    clientId: '232a1406-b27b-4667-b8c2-3a865c42b79c',
    redirectUri: document.getElementById('root').baseURI
};
export const msalAuth = new UserAgentApplication({
    auth: msalConfig
});

export function withAuth(HocComponent) {
    return class extends Component {
        constructor(props) {
            super(props);

            this.state = {
                isAuthenticated: false,
                user: {},
                renewIframe: false,
                hasError: false,
                errorMessage: null
            };
        }

        async componentWillMount() {
            msalAuth.handleRedirectCallback(() => {
                let userAccount = msalAuth.getAccount();

                this.setState({
                    isAuthenticated: true,
                    user: userAccount
                });
            }, (authErr, accountState) => {  // on fail
                console.log(authErr);

                this.setState({
                    hasError: true,
                    errorMessage: authErr.errorMessage
                });
            });

            if (msalAuth.isCallback(window.location.hash)) {
                this.setState({
                    auth: {
                        renewIframe: true
                    }
                });
                return;
            }

            let userAccount = msalAuth.getAccount();
            if (!userAccount) {
                msalAuth.loginRedirect({});
                return;
            } else {
                this.setState({
                    isAuthenticated: true,
                    user: userAccount
                });
            }
        }

        onSignIn() {
            msalAuth.loginRedirect({});
        }

        onSignOut() {
            msalAuth.logout();
        }

        render() {
            if (this.state.renewIframe) {
                return <div>hidden renew iframe - not visible</div>;
            }

            if (this.state.isAuthenticated) {
                return <HocComponent auth={this.state} onSignIn={() => this.onSignIn()} onSignOut={() => this.onSignOut()} {...this.props} />;
            }

            if (this.state.hasError) {
                return <div>{this.state.errorMessage}</div>;
            }

            return <div>Login in progress...</div>;
        }
    };
}

c。更新App.js


import { withAuth } from './msal/MsalAuthProvider';
import './custom.css'

class RootApp extends Component {
  static displayName ="Azure AD application";

  render () {
    return (
      <Layout>
        ...
      </Layout>
    );
  }
}


//enable auth when we access the page
const App = withAuth(RootApp)
export default App;
  1. 调用 API
import { msalAuth } from '../msal/MsalAuthProvider'

 async componentDidMount() {
     // get token and call the api
      try {
          const accessTokenRequest = {
              scopes: ["api://872ebcec-c24a-4399-835a-201cdaf7d68b/access_as_user"]
          }
          var authRes
          var accessToken = null;
          try {
              authRes = await msalAuth.acquireTokenSilent(accessTokenRequest);
              accessToken=authRes.accessToken
          }
          catch (error) {
              console.log("AquireTokenSilent failure");
              authRes = await msalAuth.acquireTokenPopup(accessTokenRequest);
              accessToken = authRes.accessToken
          }

          
          console.log(accessToken)
          this.populateWeatherData(accessToken);
      }
      catch (err) {
          var error = {};
          if (typeof (err) === 'string') {
              var errParts = err.split('|');
              error = errParts.length > 1 ?
                  { message: errParts[1], debug: errParts[0] } :
                  { message: err };
          } else {
              error = {
                  message: err.message,
                  debug: JSON.stringify(err)
              };
          }

          this.setState({
              user: {},
              isLoading: false,
              error: error
          });
      }
  }

async populateWeatherData(token) {
      const response = await fetch('weatherforecast', {
          method: 'get',
          headers: new Headers({
              'Authorization': 'Bearer ' + token
          })

      });
    const data = await response.json();
    this.setState({ forecasts: data, loading: false });
  }
  1. 服务器代码

一个。启动.cs

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"))
                    .EnableTokenAcquisitionToCallDownstreamApi()
                     .AddInMemoryTokenCaches();

            services.AddControllersWithViews();
           
                // In production, the React files will be served from this directory
                services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/build";
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseSpaStaticFiles();

            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller}/{action=Index}/{id?}");
            });

            app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "ClientApp";

                if (env.IsDevelopment())
                {
                    spa.UseReactDevelopmentServer(npmScript: "start");
                }
            });
        }
    }

b.控制器

[ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {  
        private readonly ITokenAcquisition tokenAcquisition;
        public WeatherForecastController(ITokenAcquisition tokenAcquisition)
        {
            this.tokenAcquisition = tokenAcquisition;
        }

        [AuthorizeForScopes(Scopes = new[] { "<your scope>" })]
        [HttpGet]
        public async Task<IEnumerable<WeatherForecast>> Get()
        {
            string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new[] { "<your scope>" });
           
            ......
            ......
        }
    }

更多详情请参考heresndhere

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2017-03-30
  • 2018-06-25
  • 2016-05-19
  • 2018-05-13
  • 2017-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多