WedX - журнал о программировании и компьютерных науках

Успешный вход в Spring OAuth2 с пустыми полномочиями

Я реализовал вход в свое веб-приложение Spring Boot с помощью OAuth2, и все работает нормально. Единственная проблема заключается в том, что вошедший в систему пользователь не имеет информации о полномочиях, сохраненной внутри сеанса, поэтому каждый раз, когда я запрашиваю URL-адрес, а контроллер имеет аннотацию @PreAuthorize ("hasRole ('USER')"), я получаю отказ.

SecurityConfiguration класс:

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@EnableJpaRepositories(basePackageClasses = UserRepository.class)
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomOAuth2UserService customOAuth2UserService;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .formLogin()
                    .loginPage("/login")
                    .failureUrl("/login?error=true")
                    .and()
                .logout()
                    .logoutSuccessUrl("/")
                    .deleteCookies("JSESSIONID")
                    .invalidateHttpSession(true)
                .and()
                .oauth2Login()
                    .loginPage("/login")
                    .failureUrl("/login?error=true")
                .userInfoEndpoint()
                    .userService(customOAuth2UserService)
                .and()
                .failureHandler(oAuth2AuthenticationFailureHandler);
    }


    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

Это класс CustomOAuth2UserService:

@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Autowired
    private UserService userService;

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

        try {
            return processOAuth2User(oAuth2UserRequest, oAuth2User);
        }catch (Exception ex) {
            // Throwing an instance of AuthenticationException will trigger the OAuth2AuthenticationFailureHandler
            throw new InternalAuthenticationServiceException(ex.getMessage(), ex.getCause());
        }
    }

    private OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) {
        OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(oAuth2UserRequest.getClientRegistration().getRegistrationId(), oAuth2User.getAttributes());
        if(StringUtils.isEmpty(oAuth2UserInfo.getEmail())) {
            throw new RuntimeException("Id not found from OAuth2 provider");
        }

        User user;
        try {
            user = userService.getByEmail(oAuth2UserInfo.getEmail());
            if(!user.getProvider().toString().equalsIgnoreCase(oAuth2UserRequest.getClientRegistration().getRegistrationId())) throw new EmailAlreadyTakenException("email-already-taken");
        } catch (UserNotFoundException e) {
            user = registerNewUser(oAuth2UserRequest, oAuth2UserInfo);
        }

        return new CustomUserDetails(user);
    }

    private User registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo oAuth2UserInfo) {
        User user = new User();
        user.setProvider(AuthProvider.valueOf(oAuth2UserRequest.getClientRegistration().getRegistrationId()));
        Identity identity = new Identity(user);
        if(oAuth2UserInfo.getFirstName() != null && !oAuth2UserInfo.getFirstName().equalsIgnoreCase(""))
            identity.setFirstName(oAuth2UserInfo.getFirstName());
        if(oAuth2UserInfo.getLastName() != null && !oAuth2UserInfo.getLastName().equalsIgnoreCase(""))
            identity.setSecondName(oAuth2UserInfo.getLastName());
        user.setIdentity(identity);
        user.setEmail(oAuth2UserInfo.getEmail());
        user.setConfirmedRegistration(true);
        boolean flag = false;
        String username = oAuth2UserInfo.getName().toLowerCase().replaceAll("\\s+", "");
        user.setUsername(username);
        return userService.addFacebookUser(user);

    }

}

Это часть файла application.properties:

spring.security.oauth2.client.registration.facebook.client-id=***
spring.security.oauth2.client.registration.facebook.client-secret=***
spring.security.oauth2.client.registration.facebook.scope=email,public_profile

spring.security.oauth2.client.registration.google.client-id=***
spring.security.oauth2.client.registration.google.client-secret=***
spring.security.oauth2.client.registration.google.scope=email,profile


spring.security.oauth2.client.provider.facebook.authorizationUri = https://www.facebook.com/v3.0/dialog/oauth
spring.security.oauth2.client.provider.facebook.tokenUri = https://graph.facebook.com/v3.0/oauth/access_token
spring.security.oauth2.client.provider.facebook.userInfoUri = https://graph.facebook.com/v3.0/me?fields=id,first_name,middle_name,last_name,name,email,verified,is_verified,picture

После входа в систему пользователь может позвонить по этому URL-адресу / users / {username}, но когда он входит в систему с помощью facebook или google через OAuth2, он получает отказ, потому что список органов власти пуст. Когда он входит в систему со своими учетными данными webapp, список прав доступа содержит USER_ROLE, и ему разрешается продолжить.

@PreAuthorize("hasRole('USER')")
    @GetRequest("users/{username}")
    public String getUser(@PathVariable String username, @PathVariable String subsection, Model model, Principal principal) throws IllegalAccessException, UserNotFoundException {
        User user = userService.getByUsername(principal.getName());
        model.addAttribute("user", user);
        return "user";
    }

Внутри главного объекта находятся:

При входе в систему с помощью OAuth2:

  • Principal: введите CustomUserDetails (информация о пользователе)
  • authorizedClientRegistrationId: введите String ("google", "facebook")
  • органы: введите Collections $ UnmodifiableRandomAccessList (пусто)
  • подробности: null
  • аутентифицирован: тип логический (true)

При входе в систему с локальными учетными данными:

  • Principal: введите CustomUserDetails (информация о пользователе)
  • учетные данные: null
  • authorities: type Collections$UnmodifiableRandomAccessList
    • index:0 type SimpleGrantedAuthority ("USER_ROLE")
  • подробности: введите WebAuthenticationDetails (удаленный адрес, идентификатор сеанса)
  • аутентифицирован: тип логический (true)

Ответы:


1

После некоторого времени отладки я нашел решение! Я неправильно настраивал роли своего пользователя. Внутри метода registerNewUser моей пользовательской службы OAuth2UserService я не устанавливал роль пользователя. Я просто добавил строку:

user.setRoles(new HashSet<>(Collections.singletonList(new Role("ROLE_USER"))));

и все заработало! Итак, теперь, когда у властей OAuth2User спрашивают, он просто вызывает getAuthorities CustomUserDetails (моя реализация OAuth2User) и вызывает метод getRoles пользователя.

CustomUserDetails класс:

public class CustomUserDetails extends User implements UserDetails, OAuth2User {

    public CustomUserDetails() {
    }

    public CustomUserDetails(String username, String email, String password, Set<Role> roles) {
        super(username, email, password, roles);
    }

    public CustomUserDetails(User user) {
        super(user.getUsername(), user.getEmail(), user.getPassword(), user.getRoles());
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return getRoles()
                .stream()
                .map(role -> new SimpleGrantedAuthority(role.getRole()))
                .collect(Collectors.toList());
    }

    @Override
    public Map<String, Object> getAttributes() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public String getName() {
        return null;
    }
}
24.05.2019
Новые материалы

Как создать диаграмму градиентной кисти с помощью D3.js
Резюме: Из этого туториала Вы узнаете, как добавить градиентную кисть к диаграмме с областями в D3.js. Мы добавим градиент к значениям SVG и применим градиент в качестве заливки к диаграмме с..

Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что это выглядит сложно…
Просто начните и учитесь самостоятельно Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что он кажется мне сложным, и я бросил его. Это в основном инструмент..

Лицензии с открытым исходным кодом: руководство для разработчиков и создателей
В динамичном мире разработки программного обеспечения открытый исходный код стал мощной парадигмой, способствующей сотрудничеству, инновациям и прогрессу, движимому сообществом. В основе..

Объяснение документов 02: BERT
BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

Как проанализировать работу вашего классификатора?
Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

Работа с цепями Маркова, часть 4 (Машинное обучение)
Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..


Для любых предложений по сайту: [email protected]