【问题标题】:Microsoft Edge handling HTTP 401 - ServiceStack Typescript clientMicrosoft Edge 处理 HTTP 401 - ServiceStack Typescript 客户端
【发布时间】:2018-09-23 05:04:17
【问题描述】:

Using the [ServiceStack Typescript client][1] and ServiceStack Auth on the backend I am seeing a failure to call/access-token` 在 Microsoft Edge 中收到 HTTP 401 响应的初始 API 请求之后。

It seems that Microsoft Edge may handle the HTTP Exception,因此 ServiceStack 客户端永远不会收到“通知”(通知是 401 结果),因此永远没有机会处理 401 响应并在尝试 API 请求之前调用 <my_servicestack_api_auth_url>/access-token 以获取不记名令牌再次。

您可以在 Chrome 中看到过滤后的网络堆栈请求:

现在查看 Microsoft Edge 中过滤后的网络堆栈:

这里是 Microsoft Edge 控制台窗口:

您可以看到,在 Microsoft Edge 中,在对初始请求做出 401 响应后,从未发出任何 <my_servicestack_api_auth_url>/access-token 请求。

ServiceStack 是否可能没有向<my_servicestack_api_auth_url>/access-token 发出请求,然后由于 Microsoft Edge 本身处理 401 错误而没有重试初始 API 请求?

【问题讨论】:

  • 这篇文章是否有一些隐含的问题?
  • @R.Richards 对于使用 ServiceStack 的人来说,这个问题更明显,我编辑了我的问题以添加一个问题 :)
  • @BrianOgden 这两张截图一样吗?无论哪种方式,MS Edge 似乎都在劫持 401 错误响应并阻止 ServiceStack 处理它。我假设控制台中没有 JavaScript 错误?
  • 嗨@mythz,是的,很抱歉两个屏幕截图是一样的,我已经更新了我的问题,并将第二个屏幕截图替换为 Microsoft Edge 网络堆栈屏幕截图。您是对的,没有 Javascript 错误,我为您提供了 Microsoft Edge 控制台的屏幕截图
  • 嗨@mythz,你有改变来检查这个问题了吗?

标签: angular typescript servicestack


【解决方案1】:

我扩展了servicestack-client 以总结处理异常、令牌存储和拒绝。我没有在我的异常处理程序中重新抛出异常(尽管这只在 Edge 中产生了副作用,而不是 Chrome 或 IE11),因此隐藏了实际错误是 here

import { JsonServiceClient, IReturn, ErrorResponse } from '@servicestack/client';
//service
import { AuthService } from './api/auth.service';
import { SpinnerService } from '../spinner/spinner.service';
//dtos
import { GetAccessToken, ConvertSessionToToken, ConvertSessionToTokenResponse } from './api/dtos'
import { AppModule } from '../../app.module';
import { Router } from '@angular/router';
import { TokenService } from './token.service';
import { ApiHelper } from '../api/api.helper';
import { AppRoutes } from '../const/routes/app-routes.const';

export class JsonServiceClientAuth extends JsonServiceClient {

    private router: Router;
    private tokenService: TokenService;
    private apiHelper: ApiHelper;
    private spinnerService: SpinnerService;

    constructor(baseUrl: string) {
        super(baseUrl);

        //Router, TokenService, ApiHelper are not injected via the contructor because clients of JsonServiceClientAuth need to create instances of it as simple as possibl
        this.router = AppModule.injector.get(Router);
        this.tokenService = AppModule.injector.get(TokenService);
        this.apiHelper = AppModule.injector.get(ApiHelper);
        this.spinnerService = AppModule.injector.get(SpinnerService);

        //refresh token 
        //http://docs.servicestack.net/jwt-authprovider#using-an-alternative-jwt-server
        this.refreshTokenUri = this.apiHelper.getServiceUrl(this.apiHelper.ServiceNames.auth) + "/access-token";

        this.onAuthenticationRequired = async () => {
            this.redirectToLogin();
        };
    }

    get<T>(request: IReturn<T> | string, args?: any): Promise<T> {
        this.prepareForRequest();

        let promise = new Promise<T>((resolve, reject) => {
            super.get(request)
                .then(res => {
                    this.handleSuccessfulResponse();
                    resolve(res);
                }, msg => {
                    this.handleCompletion();
                    this.handleRejection(msg);
                    reject(msg);
                })
                .catch(ex => this.handleCompletion(ex))
        });

        return promise;
    }

    post<T>(request: IReturn<T>, args?: any): Promise<T> {
        this.prepareForRequest();

        let promise = new Promise<T>((resolve, reject) => {
            super.post(request)
                .then(res => {
                    this.handleSuccessfulResponse();
                    resolve(res);
                }, msg => {
                    this.handleCompletion();
                    this.handleRejection(msg);
                    reject(msg);
                })
                .catch(ex => this.handleCompletion(ex))
        });

        return promise;
    }

    put<T>(request: IReturn<T>, args?: any): Promise<T> {
        this.prepareForRequest();

        let promise = new Promise<T>((resolve, reject) => {
            super.put(request)
                .then(res => {
                    this.handleSuccessfulResponse();
                    resolve(res);
                }, msg => {
                    this.handleCompletion();
                    this.handleRejection(msg);
                    reject(msg);
                })
                .catch(ex => this.handleCompletion(ex))
        });

        return promise;
    }

    delete<T>(request: IReturn<T>, args?: any): Promise<T> {
        this.prepareForRequest();

        let promise = new Promise<T>((resolve, reject) => {
            super.delete(request)
                .then(res => {
                    this.handleSuccessfulResponse();
                    resolve(res);
                }, msg => {
                    this.handleCompletion();
                    this.handleRejection(msg);
                    reject(msg);
                })
                .catch(ex => this.handleCompletion(ex))
        });

        return promise;
    }

    private handleRefreshTokenException() {
        this.redirectToLogin();
    }

    private handleCompletion(ex: any = null) {
        //hide spinner in case it was showing
        this.spinnerService.display(false);
        if(ex) {
            console.log('JsonServiceClientAuth.handleCompletion: rethrowing exception', ex);
            throw ex;
        }
    }

    private handleRejection(msg: any) {
        if (msg == "TypeError: Failed to fetch"){
            console.error('Failed to fetch: IT IS QUITE POSSIBLE THAT THE API YOU ARE CALLING IS DOWN');
        } 

        if(msg.responseStatus && msg.responseStatus.errorCode === "401"){
            //an API has rejected the request
            console.log('JsonServiceClientAuth.handleRejection: API returned 401 redirect to not authorized');
            this.router.navigate(['/', AppRoutes.NotAuthorized]);
        }

        if (msg.type === "RefreshTokenException") {
            console.log('JsonServiceClientAuth.handleRejection: there was a token refresh exeception');
            this.handleRefreshTokenException();
        }
    }

    private redirectToLogin() {
        this.router.navigate(['/', AppRoutes.Login], { queryParams: { redirectTo: this.router.url } });
    }

    /**
     * cross domain resources require that we explicity set the token in ServiceStack JsonServiceClients
     * https://stackoverflow.com/questions/47422212/use-the-jwt-tokens-across-multiple-domains-with-typescript-jsonserviceclient-s
     */
    private prepareForRequest() {
        //console.log('JsonServiceClientAuth.prepareForRequest');
        //console.log('this.tokenService.refreshToken', this.tokenService.refreshToken);
        this.refreshToken = this.tokenService.refreshToken;
        this.bearerToken = this.tokenService.bearerToken;
    }

    /**
     * refresh the bearer token with the latest data, every request is passed with the refresh token and the freshest bearerToken will be
     * returned with every response
     */
    private handleSuccessfulResponse() {
        //console.log('JsonServiceClientAuth.handleSuccessfulResponse');
        //console.log('this.bearerToken', this.bearerToken);
        //this will update the client side bearerToken, keeping it fresher - Ogden 4-18-2018
        this.tokenService.bearerToken = this.bearerToken;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-05
    • 1970-01-01
    • 2017-04-08
    • 2023-03-21
    相关资源
    最近更新 更多