Spring Boot + JWT Oauth2: весна 5 против весны <5 - PullRequest
0 голосов
/ 05 сентября 2018

У нас есть проект, в котором мы имеем:

+------------------------------------+
|                                    |    1. gets the token
|  Authorization Server (Auth)       | <------------------+
|   - spring-security-oauth2:2.0.14  |                    |
|                                    |                    |
+------------------------------------+                    +
                                                        user
                                                          +
+------------------------------------+                    |
|                                    |  2. uses the token |
|  Resource Server (RS)              |  to access resourcs|
|    - spring-security-oauth2:5.1.0  | <------------------+
|                                    |
+------------------------------------+

Мы работали в среде без Webflux , и все работало, как ожидалось. Стоит упомянуть, что этот JWT имеет следующие утверждения: exp, user_name, полномочий, jti, client_id, scope.

Все ресурсы имеют дополнительную переменную:

@GetMapping("/{id}/car")
public SomeDto someResourceMethod(@PathVariable("id") CarId carId, Principal principal)
  • В «Весенняя паутина» (без использования Reactor): Principal создается OAuth2Authentication, и там у нас есть операция getName.

  • В "Spring Webflux" (с использованием Reactor): Principal инстанцируется до JwtAuthenticationToken, и теперь у нас есть операция getSubject, но она пуста с момента утверждения sub является нулевым.

Как вы думаете, мы должны подойти к этому? Создать новый JwtDecoder? Мы используем Spring 5.1, а декодер - NimbusReactiveJwtDecoder.

1 Ответ

0 голосов
/ 06 сентября 2018

На этом этапе Reactive Resource Server поддерживает утверждения JWT в соответствии с RFC 7519 , поэтому вы видите изменение поведения.

Да, вы можете создать свой собственный декодер, который, вероятно, является наименее инвазивным способом:

public class CustomDecoder implements ReactiveJwtDecoder {
    private final ReactiveJwtDecoder nimbus;

    // ...

    public Mono<Jwt> decode(String token) {
        return this.nimbus.decode(token)
            .map(this::mapJwt);
    }

    private Jwt mapJwt(Jwt jwt) {
        Map<String, Object> claims = jwt.getClaims();
        // ... map claims accordingly
        return new Jwt(...);
    }
}

Вы также можете настроить менеджер аутентификации, который представлен в RC2 :

public class CustomReactiveAuthenticationManager
    implements ReactiveAuthenticationManager {

    private final ReactiveAuthenticationManager delegate;

    // ...

    public Mono<Authentication> authenticate(Authentication authentication) {
        return this.delegate.authenticate(authentication)
            .map(this::mapAuthentication);
    }

    private Authentication mapAuthentication(Authentication authentication) {
        // ... create a custom authentication where getName does what you need
    }
}

ИЛИ, если вам удастся выполнить рефакторинг сигнатур вашего метода, тогда другим вариантом будет использование @AuthenticatedPrincipal:

@GetMapping("/{id}/car")
public SomeDto someResourceMethod(
    @PathVariable("id") CarId carId,
    @AuthenticatedPrincipal Jwt jwt) {

    String name = jwt.getClaims().get("user_name");
    // ...
}

или, возможно, более кратко

@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@AuthenticationPrincipal(expression = "getClaims().get('user_name')")
public @interface CurrentUsername {}    

@GetMapping("/{id}/car")
public SomeDto someResourceMethod(
    @PathVariable("id") CarId carId,
    @CurrentUsername String name) {

    // ...
}

Вы также можете подумать о том, чтобы зарегистрировать усовершенствование Spring Security , чтобы рассмотреть возможность настройки имени атрибута пользователя.

EDIT : я обновил выражение EL, поскольку по умолчанию @AuthenticatedPrincipal сначала вызывает authentication.getPrincipal().

...