【问题标题】:RXJS map operator returns empty responseRXJS 映射运算符返回空响应
【发布时间】:2018-12-13 00:09:49
【问题描述】:

首先,package.json

"@angular/cli": "^6.0.8",
"rxjs": "6.2.1",

我正在关注带有角度教程的 asp.net core 2。我不得不修改一些代码,因为它被认为是旧版本。

本章是关于创建 JWT 身份验证的。

我得到了不记名令牌,但是当 api controller 返回响应时,auth.service 得到一个空响应,并且 let token = res && res.token; 行对于 if 变为 false,因此它永远不会被识别。

我怀疑 TokenResponse 的演员阵容有问题,但我不知道是什么。

token.response.ts

interface TokenResponse {
    token: string,
    expiration: number
}

auth.service.ts

import { EventEmitter, Inject, Injectable, PLATFORM_ID } from "@angular/core";
import { isPlatformBrowser } from '@angular/common';
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Observable } from "rxjs";
import { map, catchError } from 'rxjs/operators';
import 'rxjs/Rx';

@Injectable()
export class AuthService {
    authKey: string = "auth";
    clientId: string = "NetCoreAngularWeb";

    constructor(private http: HttpClient,
        @Inject(PLATFORM_ID) private platformId: any) {
    }

    // performs the login
    login(username: string, password: string): Observable<boolean> {
        var url = "api/token/auth";
        var data = {
            username: username,
            password: password,
            client_id: this.clientId,
            // required when signing up with username/password
            grant_type: "password",
            // space-separated list of scopes for which the token is 
            // issued
            scope: "offline_access profile email"
        };

        return this.http.post<TokenResponse>(url, data)
            .pipe(
                map(res => {
                    let token = res && res.token;
                    // if the token is there, login has been successful
                    if (token) {
                        // store username and jwt token
                        this.setAuth(res);
                        // successful login
                        return true;
                    }

                    // failed login
                    return Observable.throw('Unauthorized');
                }),
                catchError(error => {
                    return new Observable<any>(error);
                })
            );
        }

    // performs the logout
    logout(): boolean {
        this.setAuth(null);
        return true;
    }

    // Persist auth into localStorage or removes it if a NULL argument is given
    setAuth(auth: TokenResponse | null): boolean {
        if (isPlatformBrowser(this.platformId)) {
            if (auth) {
                localStorage.setItem(
                    this.authKey,
                    JSON.stringify(auth));
            }
            else {
                localStorage.removeItem(this.authKey);
            }
        }
        return true;
    }

    // Retrieves the auth JSON object (or NULL if none)
    getAuth(): TokenResponse | null {
        if (isPlatformBrowser(this.platformId)) {
            var i = localStorage.getItem(this.authKey);
            if (i) {
                return JSON.parse(i);
            }
        }
        return null;
    }

    // Returns TRUE if the user is logged in, FALSE otherwise.
    isLoggedIn(): boolean {
        if (isPlatformBrowser(this.platformId)) {
            return localStorage.getItem(this.authKey) != null;
        }
        return false;
    }
}

TokenController

[HttpPost("Auth")]
public async Task<IActionResult> Auth([FromBody]TokenRequestViewModel model)
{
    // return a generic HTTP Status 500 (server Error)
    // if the client payload is invalid.
    if (model == null) return new StatusCodeResult(500);

    switch (model.grant_type)
    {
        case "password":
            return await GetToken(model);
        default:
            // not supported - return a HTTP 401 (Unauthorized)
            return new UnauthorizedResult();
    }
}

private async Task<IActionResult> GetToken(TokenRequestViewModel model)
{
    try
    {
        // check if there's an user with the given username
        var user = await UserManager.FindByNameAsync(model.username);
        // fallback to support e-mail address instead of username
        if (user == null && model.username.Contains("@"))
            user = await UserManager.FindByEmailAsync(model.username);
        if (user == null || !await UserManager.CheckPasswordAsync(user, model.password))
        {
            // user does not exists or password mismatch
            return new UnauthorizedResult();
        }

        // username & password matches: create and return the Jwt token
        DateTime now = DateTime.UtcNow;

        // add the registered claims for JWT (RFC7519)
        // For more info, see...
        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, user.Id),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(now).ToUnixTimeSeconds().ToString())

            // TODO add additional claims here
        };

        var tokenExpirationMins = Configuration.GetValue<int>("Auth:Jwt:TokenExpirationInMinutes");
        var issuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Auth:Jwt:Key"]));

        var token = new JwtSecurityToken(
                issuer: Configuration["Auth:Jwt:Issuer"],
                audience: Configuration["Auth:Jwt:Audicnete"],
                claims: claims,
                notBefore: now,
                expires:
                    now.Add(TimeSpan.FromMinutes(tokenExpirationMins)),
                signingCredentials: new SigningCredentials(issuerSigningKey, SecurityAlgorithms.HmacSha256)
            );
        var encodedToken = new JwtSecurityTokenHandler().WriteToken(token);

        // build & return the response
        var response = new TokenResponseViewModel()
        {
            token = encodedToken,
            expiration = tokenExpirationMins
        };
        return Json(response);
    }
    catch (Exception ex)
    {
        return new UnauthorizedResult();
    }
}

编辑 - 响应,由 POSTMAN 检查

{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJlODBiNzRkMi0xOGIwLTQ5NzUtOWUyYS0wOGJiOGJlOTUzMDEiLCJqdGkiOiIzNTkzNjVhYS02M2UyLTQ3ZGMtYjY1NC1mMDk3YWQxNjNmMzQiLCJpYXQiOiIxNTMwNzEzMzU2IiwibmJmIjoxNTMwNzEzMzU2LCJleHAiOjE1MzU4OTczNTYsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzE3NzkvIn0.6QlGCK_4ybzYEz8qaRDDotOja6x0FI0fZTxmky94S5Y",
    "expiration": 86400
}

Status 200 OK
Time:11281 ms
Size:671 B

Content-Type →application/json; charset=utf-8
Date →Wed, 04 Jul 2018 14:09:21 GMT
Server →Kestrel
Transfer-Encoding →chunked
X-Powered-By →ASP.NET

【问题讨论】:

  • 你检查过你的实际反应了吗?
  • @AlexRiabov 是的,我得到了一个有效的 json 对象。我编辑了显示我使用邮递员测试的响应的问题
  • 您是否尝试捕获应用程序对请求的响应?例如。在 Chrome 中的 DevTools 中
  • 另外,在token 行之前添加了console.log(res) 以查看res 是否真的为空,但没关系:Object {token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4Z…kvIn0.KXJ_SmrqhJF1oclEUcI05F7-S4DSLHVDz-R6fRVKwt8", expiration: 86400} 这就是为什么我认为它与转换和 TokenResponse 接口,但属性名称和数据类型匹配
  • 您在控制台中看到Unauthorized 吗?

标签: asp.net angular authentication asp.net-core rxjs


【解决方案1】:

嗯,这里有几个问题。 首先,我的 Chrome 版本破坏了 VS Studio 调试器。我必须将其更新为 63+ 版本(在我的情况下为 65) https://social.msdn.microsoft.com/Forums/vstudio/en-US/5cd1aa58-d986-4d03-a10e-bf1fd62080a7/simultaneous-debugging-in-chrome?forum=vsdebug#5cd1aa58-d986-4d03-a10e-bf1fd62080a7

在这之后,我可以更好地调试,我发现了错误。

1- auth.service.ts 。 throwError 函数在 Observable https://www.academind.com/learn/javascript/rxjs-6-what-changed/

return this.http.post<TokenResponse>(url, data)
        .pipe(
            map(res => {
                let token = res && res.token;
                if (token) {
                    this.setAuth(res);
                    return true;
                }
                else {
                    //failed login
                    return Observable.throwError('Unauthorized');
                }

            }),
            catchError(error => {
                //console.log(error);
                return new Observable<any>(error);
            })
        );

2- TokenController Audiecnete 而不是Audience 中的拼写错误

            var token = new JwtSecurityToken(
                    issuer: Configuration["Auth:Jwt:Issuer"],
                    audience: Configuration["Auth:Jwt:Audience"],
                    claims: claims,
                    notBefore: now,
                    expires:
                        now.Add(TimeSpan.FromMinutes(tokenExpirationMins)),
                    signingCredentials: new SigningCredentials(issuerSigningKey, SecurityAlgorithms.HmacSha256)
                );

【讨论】:

    猜你喜欢
    • 2023-03-14
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    • 2021-08-25
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多