pytania od początkującego - dostęp do rzeczy z tokena

0

Cześć, jestem w tej technologi, ale w programowaniu coś tam amatorsko lata temu się stukało, to mam nadzieje, że nie bede stukał farmazonów czy niezrozumiałych treści.
(pytanie dotyczy tworzenia Web API!)

Pytanie, zakładając - jestem użytkownikiem - moje ID w bazie danych 457, zakładając, że zrobiłem taką ścieżke w API, gdzie 'me' odnosi się do mojego profilu. Dla przykładu, załóżmy, że to przeglądarkowa gra RPG - add/skill gdzie skill to nazwa np. siły - dodaje jeden punkt w siłe mojej postaci(w moim profilu).

api/users/me/add/skill

W controlerze potrzebne mi jest tylko moje ID - i skąd je wziąć po zalogowaniu? Czy to gdzieś po autenykacji i autoryzacji da się to przekazać do kontrolera (gdzieś w claimach może). Czy brać to z JWT tokena jakoś? (z nazywa użytkownika wtedy, bo ID obecnie tam nie przekazuje, i nie wiem czy to bezpieczne)
Bo wiecie, wtedy po prostu znajduje użytkownika w bazie (mnie) i dodaje.

Po prostu chciałbym wrócić na dobry krok rozumowania, bo to jest taki breakpoint, którego nie czaje za bardzo i fajnie by było jakby ktoś mi choć troche wytłumaczył.

1

Możesz zapisać Id albo coś, co pozwoli Ci zidentyfikować Usera w JWT i chyba najlepsza do tego będzie sekcja

4.1.2. "sub" (Subject) Claim

The "sub" (subject) claim identifies the principal that is the
subject of the JWT. The claims in a JWT are normally statements
about the subject. The subject value MUST either be scoped to be
locally unique in the context of the issuer or be globally unique.
The processing of this claim is generally application specific. The
"sub" value is a case-sensitive string containing a StringOrURI
value. Use of this claim is OPTIONAL.


autenykacji

Uwierzytelnienia

Czy brać to z JWT tokena jakoś? (z nazywa użytkownika wtedy, bo ID obecnie tam nie przekazuje, i nie wiem czy to bezpieczne)

Pytanie czy coś się stanie jeżeli id usera ktoś pozna

Pamiętaj, że rzeczy zapisane w JWT są jawne

2

@GuloGulo:

Wiesz, chodzi mi o to czy może jakoś tego tokena rozpakować w kontrolerze (potrzebne mi coś, żeby się do mnie odwołać)

No tak, robisz mniej więcej coś takiego.

string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

A jak nie zadziała to zła nazwa Claima i sobie po prostu podpatrzysz w debuggerze co się znajduje tam w this.User.Claims bodajże.

1

Odpowiedź @WeiXiao nie jest najlepsza moim zdaniem. Tutaj odpytujesz bazę danych na podstawie wartości z JWT. Trochę to bez sensu o ten odczyt, który tak naprawdę zupełnie niczego nie robi, tylko bez sensu odpytuje bazę. Rzeczy w tokenie też nie są jawne. One są zakodowane. Dodatkowo token ma krótki czas życia. Dlatego też nie obawiałbym się, że ktoś pozna id. Co za różnica, czy pozna id, czy login? Ja ładuję normalnie Id do tokena. W kodzie autora jednak jest inna rzecz dużo bardziej niepokojąca. A mianowicie - jeśli będę zalogowany, mogę sam sobie nabić tyle skilla, że w ciągu minuty zostanę mistrzem świata. Wystarczy, że cały czas będę wywoływał tę stronę z przeglądarki :) I to trzeba w jakiś sposób zabezpieczyć. Tu jest też cały wachlarz możliwości.

A teraz krótki kodzik do tokena, bo to nie jest na początku takie oczywiste:

W taki sposób możesz stworzyć JWT:

async Task<JwtSecurityToken> CreateDefaultToken(SystemUser user)
{
    IList<Claim> userClaims = await signInManager.UserManager.GetClaimsAsync(user); //pobierasz claimsy z bazy
    IList<string> userRoles = await signInManager.UserManager.GetRolesAsync(user); //i role

    List<Claim> claims = new Claim[] //dodajesz kilka nowych claimsów
    {
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), //Id samego tokena
            new Claim(JwtRegisteredClaimNames.Email, user.Email), //e-mail usera
            new Claim(ClaimTypes.Name, user.UserName), //nazwa usera
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()) //Id usera
    }.Union(userClaims).ToList(); //łączysz z odczytanymi claimsami

    foreach (string role in userRoles)
        claims.Add(new Claim(ClaimTypes.Role, role)); //dodajesz role


    var key = tokenReader.GetSecureKey(config["Auth:JwtBearer:Key"]);
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    var token = new JwtSecurityToken(
        issuer: config["Auth:JwtBearer:Issuer"],
        audience: config["Auth:JwtBearer:Audience"],
        claims: claims,
        expires: DateTime.Now.AddMinutes(5),
        signingCredentials: creds);

    return token;
}

//GetSecureKey u mnie wygląda tak:
public SymmetricSecurityKey GetSecureKey(string key)
{
	return new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
}

I masz wtedy wszystko w HttpContext.User.

Pamiętaj tylko, że powinieneś tworzyć też RefreshToken, który ma dłuższy czas życia.

Zasada jest taka:

  • tworzysz JWT i refreshtoken.
  • JWT powinien żyć krótki (5 - 15 minut); refresh żyje dłużej (godzinę, dzień...)
  • typ identyfikuje się za pomocą JWT

gdy JWT wygasa:

  • umieszczasz go w specjalnej tabeli, np. blacklisted_tokens
  • nie powinien już być używany
  • posługujesz się refresh tokenem, żeby wydać nowy JWT. Przy okazji wydajesz nowy refresh token
  • jeśli nie możesz wydać nowego JWT, bo np. refresh token też wygasł, no to dupa. User musi się zalogować.
0

Żeby nie tworzyć nowego tematu, mam problemy z ORM Entity Framework. Mianowicie, chciałbym zrobić coś takiego, czy to praktyczne jeszcze nie wiem ale ma to wyglądać takscreenshot-20210301114819.png
Po krótce, nie wiem, jak robię Entities w Entity Framework - czy da się zmapować tak jak zaraz pokaże w query - po prostu więcej FK w wielu kolumnach w jednej tabeli.
Po prostu, tak jak tutaj - chciałem mieć w tabeli Person (powinno być persons), że Item1 ma FK w tabeli Items, kolumna Item2 też ma FK w tabeli Items. Ktoś spyta czemu, mianowicie ktos po prostu może nie posiadać przedmiotu i potrzebuje wartości NULL. I w dodatku w ten tą kolumne łatwiej mi bedzie przydzielić inny przedmiot. Moge go też łatwo zdjąć, po prostu dająć null. Czy jest to bardzo nie praktyczne i mija się z celem pracująć w ORM?
Tu query o co mi chodziło:

CREATE TABLE Person 
(
ID int Primary Key IDENTITY (1,1) NOT NULL,
Username nvarchar(50) NOT NULL,
Item1 int NULL,
Item2 int NULL,
Item3 int NULL,
)

CREATE TABLE Items
(
ID int Primary Key IDENTITY (1,1) NOT NULL,
ItemName nvarchar(50) NOT NULL,
ItemCategory int NULL
)

CREATE TABLE Category
(
ID int Primary Key IDENTITY (1,1) NOT NULL,
CategoryName nvarchar(50) NOT NULL,
CategoryDescription nvarchar(50) NOT NULL
)

ALTER TABLE Person ADD CONSTRAINT FK_Person_Item1_Item FOREIGN KEY (Item1) REFERENCES Items(ID)
ALTER TABLE Person ADD CONSTRAINT FK_Person_Item2_Item FOREIGN KEY (Item2) REFERENCES Items(ID)

Poniekąd znalazłem solucje w tym ORM - zrobiłbym tabele Slots i dopiero miałby on FK do Items.

1

a moze sprobuj code first approach?

https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli

czyli piszesz klase a pozniej EF sam Ci wygeneruje migracje

1

Przede wszystkim zastanów się, czy taka struktura bazy jest dobrym podejściem. Mi się już tutaj zapala żółte światełko, jak widzę w tabeli Persons pola: item1, item2, item3. OK, w tym momencie gracz może mieć 3 przedmioty. A co, jeśli za chwilę uznasz, że 3 to za mało? Albo będziesz chciał dać pewnym użytkownikom (premium) 4 przedmioty? Będzie Ci to ciężko ogarnąć. I będzie potrzebna zmiana struktury bazy danych. I zmiany w modelach. I zmiany w różnych jeszcze miejscach. Także jak bym to uznał za złe rozwiązanie. Tutaj powinieneś zastosować moim zdaniem relację wiele do wielu z jakimś sprawdzeniem, ile przedmiotów może mieć użytkownik. Być może to powinien być top x przy select, a może sprawdzenie przy insert... To już jest szczegół.

I poza tym - to jest zupełnie inny temat i WYMAGA nowego wątku :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1