Как обрабатывать «TokenEndpoint: ошибка обработки: InvalidScopeException, пустая область ..»? - PullRequest
0 голосов
/ 06 августа 2020

всем! Я попытался повторить это руководство https://www.youtube.com/watch?v=wxebTn_a930 для создания сервера авторизации, и я получил:

«Ошибка обработки: InvalidScopeException, пустая область (либо клиент, либо пользователь не разрешены запрошенные области) "и я не знаю почему?

Итак, я не могу получить токен доступа с моего сервера аутентификации.

Единственное отличие в моем проекте, который я использую postgreSQL вместо MySQL.

Проект имеет в качестве зависимостей: Spring boot starter web, spring boot starter cloud security, spring data jpa, spring cloud starter oauth2, postgresql.

Aplication .yml

server:
  port: 9020

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/oauth
    username: root
    password: root
    driverClassName: org.postgresql.Driver
    platform: postgres
    initialization-mode: never
# JPA config
  jpa:
    database: POSTGRESQL
    hibernate.ddl-auto: validate

check-user-scopes: true

Модели:

@MappedSuperclass
public class BaseIdEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected int id;
}

@Entity(name = "permission")
public class Permission extends BaseIdEntity {
    private String name;

    // Getters and Setters
}

@Entity(name = "role")
public class Role extends  BaseIdEntity {

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "permission_role", joinColumns = {
            @JoinColumn(name = "role_id", referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "permission_id", referencedColumnName = "id")})
    private List<Permission> permissions;

    private String name;
    // Getters and Setters
}

@Entity(name = "users")
public class User extends BaseIdEntity implements UserDetails {

    private static final long serialVersionUID = 1L;
    private String email;
    private String username;
    private String password;
    private boolean enabled;

    @Column(name = "account_locked")
    private boolean accountNonLocked;

    @Column(name = "account_expired")
    private boolean accountNonExpired;

    @Column(name = "credentials_expired")
    private boolean credentialsNonExpired;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "role_user", joinColumns = {
            @JoinColumn(name = "user_id", referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
    private List<Role> roles;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Set<GrantedAuthority> authorities = new HashSet<>();

        roles.forEach(role -> {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
            role.getPermissions().forEach(permission -> {
                authorities.add(new SimpleGrantedAuthority(permission.getName()));
            });
        });
        return null;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

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

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

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

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

    public String getEmail() {
            return email;
    }
}

Репозиторий:

@Repository
@Transactional
public interface UserRepository extends JpaRepository<User, Long> {
    
    User findUserByUsername(String username);
}

Класс CustomUserDetailsService, который реализует UserDetailsService и отменяет все методы.

@Service(value = "userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findUserByUsername(username);

        if (user == null)
            throw new BadCredentialsException("Bad Credentials");
        new AccountStatusUserDetailsChecker().check(user);

        return user;
    }
}

Конфигурации веб-безопасности.

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }

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

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().exceptionHandling()
                .authenticationEntryPoint((httpServletRequest, httpServletResponse, authException) ->
                        httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED))
                .and().authorizeRequests().antMatchers("/**")
                .authenticated().and().httpBasic();
    }
}

Класс CustomTokenEnhancer, который добавляет дополнительную информацию о пользователе, например адрес электронной почты.

public class CustomTokenEnhancer extends JwtAccessTokenConverter {

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        User user = (User) authentication.getPrincipal();

        Map<String, Object> info = new LinkedHashMap<>(accessToken.getAdditionalInformation());
        info.put("email", user.getEmail());

        DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken);
        customAccessToken.setAdditionalInformation(info);

        return super.enhance(customAccessToken, authentication);
    }
}

CustomOauth2RequestFactory представляет фильтр, который создает токен запросы перед любыми запросами пользователей.

public class CustomOauth2RequestFactory extends DefaultOAuth2RequestFactory {

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    UserDetailsService userDetailsService;

    public CustomOauth2RequestFactory(ClientDetailsService clientDetailsService) {
        super(clientDetailsService);
    }

    @Override
    public TokenRequest createTokenRequest(Map<String, String> requestParameters, ClientDetails authenticatedClient) {
        if (requestParameters.get("grant_type").equals("refresh_token")) {
            OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(
                    tokenStore.readRefreshToken(requestParameters.get("refresh_token")));
            SecurityContextHolder.getContext()
                    .setAuthentication(new UsernamePasswordAuthenticationToken(authentication.getName(), null,
                            userDetailsService.loadUserByUsername(authentication.getName()).getAuthorities()));
        }

        return super.createTokenRequest(requestParameters, authenticatedClient);
    }
}

И конфигурации для сервера авторизации.

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Value("${check-user-scopes}")
    private Boolean checkUserScopes;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Bean
    public OAuth2RequestFactory requestFactory() {
        CustomOauth2RequestFactory requestFactory = new CustomOauth2RequestFactory(clientDetailsService);
        requestFactory.setCheckUserScopes(true);
        return requestFactory;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new CustomTokenEnhancer();
        converter.setKeyPair(
                new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"),
                        "password".toCharArray()).getKeyPair("jwt"));
        return converter;

    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
    }

    @Bean
    public TokenEndpointAuthenticationFilter tokenEndpointAuthenticationFilter() {
        return new TokenEndpointAuthenticationFilter(authenticationManager, requestFactory());
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore()).tokenEnhancer(jwtAccessTokenConverter())
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
        if (checkUserScopes)
            endpoints.requestFactory(requestFactory());
    }
}

схема- postgres. sql.

DROP TABLE IF EXISTS oauth_client_details CASCADE;
CREATE TABLE oauth_client_details(
client_id VARCHAR(255) NOT NULL PRIMARY KEY,
client_secret VARCHAR(255) NOT NULL,
resource_ids VARCHAR(255) DEFAULT NULL,
scope VARCHAR(255) DEFAULT NULL,
authorized_grant_types VARCHAR(255) DEFAULT NULL,
web_server_redirect_uri VARCHAR(255) DEFAULT NULL,
authorities VARCHAR(255) DEFAULT NULL,
access_token_validity INT DEFAULT NULL,
refresh_token_validity INT DEFAULT NULL,
additional_information VARCHAR(4096) DEFAULT NULL,
autoapprove VARCHAR(255) DEFAULT NULL);

DROP TABLE IF EXISTS permission CASCADE;
CREATE TABLE permission (
id int PRIMARY KEY,
name VARCHAR(60) UNIQUE);

DROP TABLE IF EXISTS role CASCADE;
CREATE TABLE role
(id int PRIMARY KEY,
name VARCHAR(60) UNIQUE);

DROP TABLE IF EXISTS permission_role CASCADE;
CREATE TABLE permission_role(
permission_id int,
FOREIGN KEY(permission_id) REFERENCES permission(id),
role_id int,
FOREIGN KEY(role_id) REFERENCES role(id));

DROP TABLE IF EXISTS users CASCADE;
CREATE TABLE users (
id int PRIMARY KEY,
username VARCHAR(24) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
enabled boolean NOT NULL,
account_locked boolean NOT NULL,
account_expired boolean NOT NULL,
credentials_expired boolean NOT NULL);

DROP TABLE IF EXISTS role_users CASCADE;
CREATE TABLE role_users (role_id int,FOREIGN KEY(role_id) REFERENCES role(id),
                         users_id int, FOREIGN KEY(users_id) REFERENCES users(id));

данные - postgres. sql

INSERT INTO oauth_client_details (
    client_id,client_secret,
    resource_ids,
    scope,
    authorized_grant_types,
    web_server_redirect_uri,authorities,
    access_token_validity,refresh_token_validity,
    additional_information,autoapprove)
    VALUES(
    'USER_CLIENT_APP','{bcrypt}$2a$10$EOs8VROb14e7ZnydvXECA.4LoIhPOoFHKvVF/iBZ/ker17Eocz4Vi',
    'USER_CLIENT_RESOURCE,USER_ADMIN_RESOURCE',
    'role_admin,role_user',
    'authorization_code,password,refresh_token,implicit',
    NULL,NULL,
    900,3600,
    '{}',NULL);

INSERT INTO permission (name) VALUES
('can_create_user'),
('can_update_user'),
('can_read_user'),
('can_delete_user');

INSERT INTO role (name) VALUES
('role_admin'),('role_user');

INSERT INTO permission_role (permission_id, role_id) VALUES
(1,1), /* can_create_user assigned to role_admin */
(2,1), /* can_update_user assigned to role_admin */
(3,1), /* can_read_user assigned to role_admin */
(4,1), /* can_delete_user assigned to role_admin */
(3,2);  /* can_read_user assigned to role_user */

INSERT INTO users (
username,password,
email,enabled,account_locked, account_expired,credentials_expired) VALUES (
'admin','{bcrypt}$2a$10$EOs8VROb14e7ZnydvXECA.4LoIhPOoFHKvVF/iBZ/ker17Eocz4Vi',
'william@gmail.com',true,true,true,true),
('user','{bcrypt}$2a$10$EOs8VROb14e7ZnydvXECA.4LoIhPOoFHKvVF/iBZ/ker17Eocz4Vi',
'john@gmail.com',true,true,true,true);


INSERT INTO role_users (role_id, users_id)
VALUES
(1, 1) /* role_admin assigned to admin user */,
(2, 2) /* role_user assigned to user user */ ;

1 Ответ

0 голосов
/ 07 августа 2020

Проблема была в модели «Пользователь»:

@Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Set<GrantedAuthority> authorities = new HashSet<>();

        roles.forEach(role -> {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
            role.getPermissions().forEach(permission -> {
                authorities.add(new SimpleGrantedAuthority(permission.getName()));
            });
        });
        return null;
    }

Метод вернул «ноль» вместо «полномочий». Глупая ошибка.

...