Принципал клиента OAuth2 не имеет GrantedAuthorities при аутентификации другим настраиваемым сервером авторизации (SpringBoot2 и OAuth2)

Я использую Spring Boot2 в качестве фреймворка и Thymeleaf в качестве механизма шаблонов.

на моем сервере авторизации я добавил пользователя «admin» как «ROLE_ADMIN».

но в клиентском приложении, когда я вошел в систему как «администратор» и распечатал Authentication Object from SecurityContextHolder.getContext().getAuthentication(), свойство Granted Authorities имеет только «ROLE_USER».

Ниже приведена конфигурация моего сервера авторизации.

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("admin").password(passwordEncoder().encode("123")).roles("USER", "ADMIN");
        auth
                .inMemoryAuthentication()
                .withUser("user").password(passwordEncoder().encode("123")).roles("USER");

    }

а ниже Authentication Объект из кода регистрации SecurityContextHolder.getContext().getAuthentication().

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(auth.isAuthenticated());
        System.out.println(auth.getAuthorities());
        System.out.println(auth.getPrincipal());

и результат

//  isAuthenticated()
true

// getAuthorites()
[ROLE_USER] 

// getPrincipal()
Name: [admin], Granted Authorities: [ROLE_USER], User Attributes: [authorities=[{authority=ROLE_ADMIN}, {authority=ROLE_USER}], ...

Ниже приведен мой код тимелеафа.

            <div sec:authorize = "isAuthenticated()">
                Text visible only to authenticated users.

                <!-- Principal name -->
                Authenticated username:
                <div sec:authentication = "name"></div>

                <div sec:authorize = "hasRole('USER')">Text visible to user.</div>
                <!-- i cant see this message -->
                <div sec:authorize = "hasRole('ADMIN')">Text visible to admin.</div>

                Authenticated user roles:
                <!-- print '[ROLE_USER]' only -->
                <div sec:authentication = "principal.authorities"></div>
            </div>

            <div sec:authorize = "!isAuthenticated()">Text visible only to
                unauthenticated users.
            </div>

Итак, я хочу получить доступ к Principal.UserAttributes.authorities в тимелеафе.

я имею в виду OAuth2AuthenticationToken, OAuth2User.getAttributes() и DefaultOAuth2User.toString()

Как я могу это сделать?

В вашем контроллере добавьте принципала в качестве аргумента, например. public String myController(Principal principal){ ... }, а затем добавьте его на карту вашей модели (или класс) и затем используйте его в тимелеафе.

Max R. 06.05.2019 12:22

@МаксР. спасибо за Ваш ответ. тогда я должен добавить параметр Principal ко всем контроллерам, которые включают тимелеаф, используя тимелеаф-безопасность?

박예찬 06.05.2019 12:25

Да, это был бы один из способов.

Max R. 06.05.2019 14:44

@МаксР. Ok. я понял. но я не знаю, почему у моего Principal объекта еще нет ROLE_ADMIN. в конфигурации сервера авторизации я добавил пользователя «admin» с «ROLE_ADMIN»

박예찬 06.05.2019 15:32

Вам нужно будет извлечь роли из ваших [полномочий] и добавить их в предоставленные полномочия самостоятельно, один из способов — использовать интерфейс AuthoritiesExtractor, вот пример: baeldung.com/… (в 4.1 есть пример реализации AuthoritiesExtractor)

Max R. 06.05.2019 15:48
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Версия Java на основе версии загрузки
Версия Java на основе версии загрузки
Если вы зайдете на официальный сайт Spring Boot , там представлен start.spring.io , который упрощает создание проектов Spring Boot, как показано ниже.
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
1
5
1 372
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете передать Принципал своему контроллеру в качестве аргумента, например

public String myController(Principal principal) { 
   ... 
}

Вам также придется сопоставить полномочия с предоставленными полномочиями самостоятельно, например. используя интерфейс AuthoritiesExtractor из Spring, вот пример: Ссылка из Баелдунга

спасибо за Ваш ответ. я не хочу реализовывать параметризованный контроллер. слишком много контроллеров используют эту функцию. но как вы сказали, я проверю AuthoritiesExtractor. Спасибо.

박예찬 07.05.2019 15:50

ищу AuthoritiesExtractor. мой проект состоит из Client, Authorization Server и Resource Server. эти проекты разделены. какой проект реализовать CustomAuthoritiesExtractor? Я добавил весь проект, но не работает. в Resource Server я регистрирую Principal или Authentication объект, в котором отображается конечная точка «/me». и есть «ROLE_USER», «ROLE_ADMIN» в Granted Authorities правильно. я думаю, что что-то не так, когда получаю Granted Authorities в клиенте

박예찬 08.05.2019 03:01

Использовать объект #authentication

<div th:text = "${#authentication.principal.something}"> The value of the "name" property of the authentication object should appear here. </div>

Пример:

<img th:if = "${#authentication.principal.image}"
class = "img-circle" th:src = "${#authentication.principal.image}"
width = "100" height = "100" alt = "place-holder" />

Но сначала добавьте эту зависимость

 <dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>-latest-version-here-</version>
</dependency>

Поскольку он не поставляется с тимелеаф-стартером в весенней загрузке

спасибо за Ваш ответ. я уже добавил thymeleaf-extras-springsecurity5 зависимость. что я хочу сказать, так это «почему мой основной объект имеет полномочия только« ROLE_USER ».

박예찬 07.05.2019 15:49

См. реализацию DefaultOAuth2UserService. Реализация по умолчанию всегда создает объект Prinicpal с полномочиями USER_ROLE. Вы должны предоставить собственную реализацию OAuth2UserService

Rahil Husain 07.05.2019 16:32
Set<GrantedAuthority> authorities = Collections.singleton(new OAuth2UserAuthority(userAttributes));public OAuth2UserAuthority(Map<String, Object> attributes) { this("ROLE_USER", attributes); }
Rahil Husain 07.05.2019 16:32
Ответ принят как подходящий

Я решил.

На сервере авторизации я настроил так.

  • Конфигурация AuthorizationServer WebSecurityConfigurerAdapter
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
        @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("admin").password(passwordEncoder().encode("123")).roles("USER", "ADMIN").authorities("USER", "ADMIN");
        auth
                .inMemoryAuthentication()
                .withUser("user").password(passwordEncoder().encode("123")).roles("USER");

    }
    ...
}

и ниже приведен контроллер сопоставления /me моего сервера ресурсов

  • ResourceServer /me сопоставленный контроллер
@RestController
public class UserController {

    @RequestMapping("/me")
    public Principal user(Principal principal) {
        return principal;
    }
}

и ниже приведена конфигурация моего клиента WebSecurityConfigurerAdapter

  • Конфигурация клиента WebSecurityConfigurerAdapter
@Configuration
@EnableOAuth2Client
public class WebSecurityConfigurerAdapterImpl extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/home", "/error", "/webjars/**", "/resources/**", "/login**").permitAll()
                .anyRequest().authenticated()
                .and().oauth2Login();
    }

и в клиентском контроллере я зарегистрировался так.

  • ведение журнала Principal в клиентском контроллере
    @GetMapping("")
    public String git1() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(auth.getPrincipal());

        /** Thymeleaf using this **/
        Object authenticationProperty = AuthUtils.getAuthenticationProperty(auth, "principal.attributes['authorities']");
        System.out.println(authenticationProperty.toString());

        return VIEW_PATH + "git1";
    }

и далее результат

Name: [admin], Granted Authorities: [ROLE_USER], User Attributes: [authorities=[{authority=USER}, {authority=ADMIN}], details = {remoteAddress=127.0.0.1, sessionId=null, tokenValue=82a7a532-a31e-4d0a-bd83-f15a9cbea3bc, tokenType=Bearer, decodedDetails=null}, authenticated=true, userAuthentication = {authorities=[{authority=USER}, {authority=ADMIN}], details=null, authenticated=true, principal=admin, credentials=N/A, name=admin}, oauth2Request = {clientId=foo, scope=[read], requestParameters = {client_id=foo}, resourceIds=[], authorities=[], approved=true, refresh=false, redirectUri=null, responseTypes=[], extensions = {}, refreshTokenRequest=null, grantType=null}, clientOnly=false, principal=admin, credentials=, name=admin]
[{authority=USER}, {authority=ADMIN}]

как видите, я добавил полномочия «ROLE_USER» и «ROLE_ADMIN» на сервере авторизации.

в объекте Resource Server Principal предоставлены как «ROLE_ADMIN», так и «ROLE_USER».

но в клиентском Principal объекте не предоставлена ​​«ROLE_ADMIN». есть только 'ROLE_USER'.

и Principal.atttibutes['authorities'] имеет «ПОЛЬЗОВАТЕЛЬ», «АДМИН».

как сказал @Rahil Husain, есть DefaultOAuth2UserService, и эта служба предоставляет «ROLE_USER» только OAuth2User Object.

во-первых, я добавил CustomAuthoritiesExtractor через аннотацию @Componenet (тоже @Bean) в клиент.

но это не работает в моих проектах.

Итак, я реализовал CustomOAuth2User и CustomOAuth2UserService.

так.

  • CustomOAuth2User
public class CustomOAuth2User implements OAuth2User {
    private List<GrantedAuthority> authorities;
    private Map<String, Object> attributes;
    private String name;


    public CustomOAuth2User(List<GrantedAuthority> authorities, Map<String, Object> attributes) {
        this.authorities = authorities;
        this.attributes = attributes;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    @Override
    public Map<String, Object> getAttributes() {
        if (this.attributes == null) {
            this.attributes = new HashMap<>();
            this.attributes.put("name", this.getName());
        }
        return attributes;
    }

    @Override
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

и следующее CustomOAuth2UserService

  • CustomOAuth2UserService
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);

        AuthoritiesExtractor authoritiesExtractor = new CustomAuthoritiesExtractor();
        List<GrantedAuthority> grantedAuthorityList = authoritiesExtractor.extractAuthorities(oAuth2User.getAttributes());
        CustomOAuth2User customOAuth2User = new CustomOAuth2User(grantedAuthorityList, oAuth2User.getAttributes());
        customOAuth2User.setName(oAuth2User.getName());

        return customOAuth2User;
    }
}

и ниже мой CustomAuthoritiesExtractor. этот класс не используется как @Bean или @Component. непосредственно используется в CustomOAuth2Service для сопоставления CustomOAuth2User полномочий объекта

  • CustomAuthoritiesExtractor
public class CustomAuthoritiesExtractor implements AuthoritiesExtractor {

    @Override
    public List<GrantedAuthority> extractAuthorities(Map<String, Object> map) {
        return AuthorityUtils.commaSeparatedStringToAuthorityList(asAuthorities(map));
    }

    private String asAuthorities(Map<String, Object> map) {
        List<String> authorities = new ArrayList<>();
        List<LinkedHashMap<String, String>> authz =
                (List<LinkedHashMap<String, String>>) map.get("authorities");
        for (LinkedHashMap<String, String> entry : authz) {
            authorities.add(entry.get("authority"));
        }
        return String.join(",", authorities);
    }
}

и, наконец, я изменил конечную точку клиента на использование моих CustomOAuth2User и CustomOAuth2UserService.

Итак, я изменил конфигурацию клиента WebSecurityConfigurerAdapter следующим образом.

@Configuration
@EnableOAuth2Client
public class WebSecurityConfigurerAdapterImpl extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/home", "/error", "/webjars/**", "/resources/**", "/login**").permitAll()
                .anyRequest().authenticated()
                .and().oauth2Login()


                /** add this config**/
                            .userInfoEndpoint()
                                    .customUserType(CustomOAuth2User.class, "teemo")
                                    .userService(this.oauth2UserService());
    }

    private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
        return new CustomOAuth2UserService();
    }

и ниже мой тимелеаф.

  • лист тимьяна
    <div sec:authorize = "isAuthenticated()">
        Text visible only to authenticated users.

        Authenticated username:
        <div sec:authentication = "name"></div>

        <div sec:authorize = "hasRole('USER')">hasRole('USER')</div>
        <div sec:authorize = "hasRole('ROLE_USER')">hasRole('ROLE_USER')</div>
        <div sec:authorize = "hasRole('ADMIN')">hasRole('ADMIN')</div>
        <div sec:authorize = "hasRole('ROLE_ADMIN')">hasRole('ROLE_ADMIN')</div>
        <!-- TRUE -->
        <div sec:authorize = "hasAuthority('USER')">hasAuthority('USER')</div>
        <div sec:authorize = "hasAuthority('ROLE_USER')">hasAuthority('ROLE_USER')</div>
        <!-- TRUE -->
        <div sec:authorize = "hasAuthority('ADMIN')">hasAuthority('ADMIN')</div>
        <div sec:authorize = "hasAuthority('ROLE_ADMIN')">hasAuthority('ROLE_ADMIN')</div>
    </div>

    <div sec:authorize = "!isAuthenticated()">Text visible only to
                unauthenticated users.
    </div>

и следующий результат.

Text visible only to authenticated users. Authenticated username:
admin
hasAuthority('USER')
hasAuthority('ADMIN')

всем, кто копает, как я, я надеюсь помочь этот вопрос и ответы.

но я не знаю, что это де-факто стандартный способ.

просто.. работаю сейчас.

вау, это было потрясающе, но мне пришлось заменить метод asAuthorities на String authoritiesString= map.get("authorities").toString(); authoritiesString = authoritiesString.substring(1, authoritiesString.length() - 1); authoritiesString = authoritiesString.trim(); return authoritiesString;, так как приведение к LinkedHashMap не сработало. но спасибо ты спас мой день

Priyamal 03.10.2019 09:24

Другие вопросы по теме