1

登录后无法获取数据。

我将 IdentityServer4 与 WebApi 一起使用。只要我不发送任何授权书,一切都会正常进行。我正在使用 Damien Bod 的授权拦截器。

当我发送承载时,我得到了这个:

GET https://localhost:44373/api/pages/10 10:1 XMLHttpRequest 无法加载https://localhost:44373/api/pages/10。请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,不允许访问源“ http://localhost:3000 ”。响应的 HTTP 状态代码为 500。

只要我不将 Authorization 标头添加到 http 请求,这就会起作用。

我正在使用此处找到的 IdentityServer 示例: https ://github.com/IdentityServer/IdentityServer4.Samples/tree/dev/MVC%20and%20API/src

安全服务和授权拦截器都在这里找到: https ://github.com/damienbod/AspNet5IdentityServerAngularImplicitFlow

所以问题是在我登录并尝试从 API 检索数据后,我收到上述错误。

API - Startup.cs

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

        if (env.IsEnvironment("Development"))
        {
            // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
            builder.AddApplicationInsightsSettings(developerMode: true);
        }

        builder.AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

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

        //services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()
        //                                                            .AllowAnyMethod()
        //                                                             .AllowAnyHeader()));

        //var policy = new Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicy();

        //policy.Headers.Add("*");
        //policy.Methods.Add("*");
        //policy.Origins.Add("*");
        //policy.SupportsCredentials = true;

        //services.AddCors(x => x.AddPolicy("AllowAll", policy));

        services.AddEntityFramework()
            .AddEntityFrameworkSqlServer()
            .AddDbContext<AsklepiosContext>(options =>               
                options.UseSqlServer(@"Server = .\SQLEXPRESS; Trusted_Connection = true; Database = AsklepiosTrunk; MultipleActiveResultSets = true;"));

        // Add framework services.
        services.AddApplicationInsightsTelemetry(Configuration);

        var guestPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .RequireClaim("scope", "api1")
            .Build();

        services.AddMvc(options =>
        {
            options.Filters.Add(new AuthorizeFilter(guestPolicy));
        });

        //DI
        // some services here .. 


    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        app.UseStaticFiles();

        app.UseApplicationInsightsRequestTelemetry();

        app.UseApplicationInsightsExceptionTelemetry();

        //app.UseCors("AllowAll");

        //JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();

        //var jwtBearerOptions = new JwtBearerOptions()
        //{
        //    Authority = "https://localhost:44301",
        //    Audience = "https://localhost:44301/resources",
        //    AutomaticAuthenticate = true,

        //    // required if you want to return a 403 and not a 401 for forbidden responses
        //    AutomaticChallenge = true
        //};

        //app.UseJwtBearerAuthentication(jwtBearerOptions);

        //JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();

        //var jwtBearerOptions = new JwtBearerOptions()
        //{
        //    Authority = "https://localhost:44302",
        //    Audience = "https://localhost:44302/resources",
        //    AutomaticAuthenticate = true,

        //    // required if you want to return a 403 and not a 401 for forbidden responses
        //    AutomaticChallenge = true,
        //};

        //app.UseJwtBearerAuthentication(jwtBearerOptions);

        //JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

        app.UseCors("AllowAll");

        app.UseCors(policy =>
        {
            policy.WithOrigins(
                "http://localhost:3000");

            policy.AllowAnyHeader();
            policy.AllowAnyMethod();
        });


        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

        app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
        {
            Authority = "https://localhost:44301",
            RequireHttpsMetadata = false,

            ScopeName = "api1",
            AutomaticAuthenticate = true
        });

        //app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
        //{
        //    Authority = "https://localhost:44301",
        //    RequireHttpsMetadata = false,

        //    ScopeName = "dataEventRecords",
        //    AutomaticAuthenticate = true
        //});

        app.UseMvc();
    }
}

授权拦截器.ts

import { urls } from "../constants";

class AuthorizationInterceptor {
private static _instance: AuthorizationInterceptor;
private baseUrl: string;

public static Factory($q: angular.IQService, localStorageService, $window) {
    AuthorizationInterceptor._instance = new AuthorizationInterceptor($q, localStorageService, $window);
    return AuthorizationInterceptor._instance;
}

constructor(private $q: angular.IQService, private localStorageService, private $window) {
    console.log("AuthorizationInterceptor created");
}

public request(requestSuccess) {
    const self = AuthorizationInterceptor._instance;
    requestSuccess.headers = requestSuccess.headers || {};

        if (self.localStorageService.get("authorizationData") !== null &&
            self.localStorageService.get("authorizationData") !== "") {
            // when it adds this header i get CORS Error
            requestSuccess.headers.Authorization = "Bearer " + self.localStorageService.get("authorizationData");
        }

        return requestSuccess || self.$q.when(requestSuccess);
}


public responseError(responseFailure) {
    const self = AuthorizationInterceptor._instance;
    console.log("console.log(responseFailure);");
    console.log(responseFailure);

    if (responseFailure.status === 403) {
        alert("forbidden");
        self.$window.location = "https://localhost:44389/forbidden";
        self.$window.href = "forbidden";

    } else if (responseFailure.status === 401) {

        alert("unauthorized");
        self.localStorageService.set("authorizationData", "");
    }

    return self.$q.reject(responseFailure);
}
}

export default AuthorizationInterceptor;

安全服务.ts

import DataService from "./dataservice";
import { urls } from "../constants";

class SecurityService {
private baseUrl: string;
private isAuthorized: boolean = false;
private hasAdminRole: boolean = false;

constructor(private $http, private $log, private $q, private $rootScope, private $window, private $state,
    private localStorageService, private $stateParams) {
    this.baseUrl = `${urls.baseApiUrl}`;
    console.log("return url: " + this.$stateParams.returnurl);
    $log.info("SecurityService called");

    // what to put this ?
    this.$rootScope.IsAuthorized = false;
    this.$rootScope.HasAdminRole = false;
}

public resetAuthorizationData() {
    this.localStorageService.set("authorizationData", "");
    this.localStorageService.set("authorizationDataIdToken", "");
    this.$rootScope.IsAuthorized = false;
    this.$rootScope.HasAdminRole = false;
}

public setAuthorizationData(token, id_token) {
    if (this.localStorageService.get("authorizationData") !== "") {
        this.localStorageService.set("authorizationData", "");
    }

    this.localStorageService.set("authorizationData", token);
    this.localStorageService.set("authorizationDataIdToken", id_token);
    this.$rootScope.IsAuthorized = true;

    let data: any = this.getDataFromToken(token);
    if (data.role !== undefined) {
        for (let i = 0; i < data.role.length; i++) {
            if (data.role[i] === "dataEventRecords.admin") {
                this.$rootScope.HasAdminRole = true;
            }
        }
    }
}

 public authorize() {
    console.log("AuthorizedController time to log on");

    // GET /authorize?
    // response_type=code%20id_token
    // &client_id=s6BhdRkqt3
    // &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
    // &scope=openid%20profile%data
    // &nonce=n-0S6_WzA2Mj
    // &state=af0ifjsldkj HTTP/1.1

    const authorizationUrl = "https://localhost:44302/connect/authorize";
    const client_id = "angularclient";
    const redirect_uri = "http://localhost:3000/authorized";
    const response_type = "id_token token";
    const scope = "openid profile api1";
    const nonce = "N" + Math.random() + "" + Date.now();
    const state = Date.now() + "" + Math.random();

    this.localStorageService.set("authNonce", nonce);
    this.localStorageService.set("authStateControl", state);
    console.log("AuthorizedController created. adding myautostate: " + this.localStorageService.get("authStateControl"));

    const url =
        authorizationUrl + "?" +
        "response_type=" + encodeURI(response_type) + "&" +
        "client_id=" + encodeURI(client_id) + "&" +
        "redirect_uri=" + encodeURI(redirect_uri) + "&" +
        "scope=" + encodeURI(scope) + "&" +
        "nonce=" + encodeURI(nonce) + "&" +
        "state=" + encodeURI(state);

    this.$window.location = url;
}

public doAuthorization() {
    this.resetAuthorizationData();
    if (this.$window.location.hash) {
        this.authorizeCallback();
    } else {
        this.authorize();
    }
}

public logoff() {
    // var id_token = localStorageService.get("authorizationDataIdToken");
    // var authorizationUrl = 'https://localhost:44330/connect/endsession';
    // var id_token_hint = id_token;
    // var post_logout_redirect_uri = 'https://localhost:44347/unauthorized.html';
    // var state = Date.now() + "" + Math.random();

    // var url =
    //    authorizationUrl + "?" +
    //    "id_token_hint=" + id_token_hint + "&" +
    //    "post_logout_redirect_uri=" + encodeURI(post_logout_redirect_uri) + "&" +
    //    "state=" + encodeURI(state);

    // ResetAuthorizationData();
    // $window.location = url;

    // 19.02.2106: temp until connect/endsession is implemented in IdentityServer4 NOT A PROPER SOLUTION!
    this.resetAuthorizationData();
    this.$window.location = "https://localhost:44389/unauthorized.html";
}

private urlBase64Decode(str) {
    let output = str.replace("-", "+").replace("_", "/");
        switch (output.length % 4) {
            case 0:
                break;
            case 2:
                output += "==";
                break;
            case 3:
                output += "=";
                break;
            default:
                throw "Illegal base64url string!";
        }
        return window.atob(output);
}

private getDataFromToken(token) {
    let data = {};
    if (typeof token !== "undefined") {
        let encoded = token.split(".")[1];
        data = JSON.parse(this.urlBase64Decode(encoded));
    }
    return data;
}

private authorizeCallback() {
    console.log("AuthorizedController created, has hash");
    let hash = window.location.hash.substr(1);

    let result: any = hash.split("&").reduce(function (result2, item) {
        let parts = item.split("=");
        result2[parts[0]] = parts[1];
        return result2;
    }, {});

    let token = "";
    let id_token = "";
    let authResponseIsValid = false;
    if (!result.error) {

        if (result.state !== this.localStorageService.get("authStateControl")) {
            console.log("AuthorizedCallback incorrect state");
        } else {

            token = result.access_token;
            id_token = result.id_token;

            let dataIdToken: any = this.getDataFromToken(id_token);
            console.log(dataIdToken);

            // validate nonce
            if (dataIdToken.nonce !== this.localStorageService.get("authNonce")) {
                console.log("AuthorizedCallback incorrect nonce");
            } else {
                this.localStorageService.set("authNonce", "");
                this.localStorageService.set("authStateControl", "");

                authResponseIsValid = true;
                console.log("AuthorizedCallback state and nonce validated, returning access token");
            }
        }
    }

    if (authResponseIsValid) {
        this.setAuthorizationData(token, id_token);
        console.log(this.localStorageService.get("authorizationData"));

        this.$state.go("overviewindex");
    } else {
        this.resetAuthorizationData();
        this.$state.go("unauthorized");
    }
}
}

SecurityService.$inject = ["$http", "$log", "$q", "$rootScope", "$window",      "$state", "localStorageService", "$stateParams"];
export default SecurityService;
4

3 回答 3

0

这似乎是CORS问题。在您的服务中启用 CORS,您将一切顺利。当您添加授权标头时,这使得请求在 MDN 术语中并不简单。这可能触发了飞行前 OPTIONS 请求。您可以在此处阅读有关 CORS 的更多信息 - https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

更新 - 您有在代码中启用 CORS 的代码,您已将其注释掉。而不是给予完全许可(*),为您的域提供特定的

于 2016-08-27T15:34:56.580 回答
0

我认为您需要自定义 imp 一个 ICorsPolicyService 接口并对其进行 DI。

public class SwaggerCorsPolicyService : DefaultCorsPolicyService, ICorsPolicyService
{
    private readonly IClientService _clientService;
    private readonly ILogger _logger;

    public SwaggerCorsPolicyService(ILoggerFactory logger, IClientService clientService) : base(logger.CreateLogger<DefaultCorsPolicyService>())
    {
        this._clientService = clientService;
        this._logger = logger.CreateLogger<SwaggerCorsPolicyService>();
    }

    async Task<bool> ICorsPolicyService.IsOriginAllowedAsync(string origin)
    {
        if (AllowAll)
        {
            _logger.LogDebug("AllowAll true, so origin: {0} is allowed", origin);
            return true;
        }

        AllowedOrigins = await _clientService.**Get All Client Uris**;
        if (AllowedOrigins != null)
        {
            if (AllowedOrigins.Contains(origin, StringComparer.OrdinalIgnoreCase))
            {
                _logger.LogDebug("AllowedOrigins configured and origin {0} is allowed", origin);
                return true;
            }
            else
            {
                _logger.LogDebug("AllowedOrigins configured and origin {0} is not allowed", origin);
            }
        }
        _logger.LogDebug("Exiting; origin {0} is not allowed", origin);
        return false;
    }

}

在配置服务中

var icps = services.Where(p => p.ServiceType == typeof(ICorsPolicyService)).FirstOrDefault();
if (icps != null) services.Remove(icps);
services.AddTransient<ICorsPolicyService, SwaggerCorsPolicyService>();
于 2016-08-29T02:36:22.070 回答
0

我也遇到了同样的问题,经过一些麻烦后,我发现该错误与 CORS 无关。

您应该检查 API 的日志。在我的情况下,错误是不记名令牌已过期或其他一些身份验证机制已崩溃,但由于缺少正确的标头,浏览器将其检测为 CORS 问题。

幸运的是,IDSRV4 有一个很好的日志系统,所以你应该检查 API 输出日志。

于 2017-05-29T13:02:51.460 回答