OAuth2 с ответом Spring Boot Unauthorized (401) - PullRequest
1 голос
/ 02 августа 2020

Я реализовал приложение весенней загрузки с помощью oauth2. когда я пытаюсь получить доступ к токену, указав clientId и Secret, возвращается неавторизованный (401) ответ.

Таблица oauth_client_detals создана в базе данных oracle со следующей схемой, а значение секретного столбца сохраняется в формате BCrypt .

insert into oauth_client_details(client_id,client_secret,web_server_redirect_uri,
scope,accsess_token_validity,refresh_token_validity,resource_id,authorized_grant_types,authorities,
  additional_information,autoapprove) values ('web','{bcrypt}$2y$12$FCIQkEmh7ai/6oP99yNOEuWnKt9OjrGEczCxnEnFGDRSOHumOChQO',
  '','READ,WRITE','900','3600','','password,authorization_code,refresh_token,implicit','ROLE_ADMIN,ROLE_USER,ROLE_MANAGER','','');

AuthorizationConfig.class

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer  extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private DataSource dataSource;


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

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

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }
}

SecurityConfig.class

@Configuration
@EnableWebSecurity
public class SecurityConfig  extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AuthEntryPoint authEntryPoint;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider1());
    }

    private AuthenticationProvider authenticationProvider1()
    {
        DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(new BCryptPasswordEncoder());
        return provider;
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().exceptionHandling().authenticationEntryPoint(authEntryPoint)
                .and().authorizeRequests().anyRequest().authenticated().and().httpBasic();
    }
}

UserDetailsServiceImpl.class

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserDAO userDAO;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user= userDAO.findByUserName(username)
                .orElseThrow(()->new UsernameNotFoundException("data not found with "+username));

        return AuthUserDetails.builder(user);
    }

}

AuthUserDetails.class

public class AuthUserDetails implements UserDetails{

    private String userName;
    private String password;
    private List<GrantedAuthority> authorities;
    private boolean accNonExpired;
    private boolean accNonLocked;
    private boolean credentialNonExpired;
    private boolean active;

    public AuthUserDetails()
    {

    }

    public AuthUserDetails(boolean active, List<GrantedAuthority> authorities, String userName, String password,
            boolean accNonExpired, boolean credentialNonExpired, boolean accNonLocked) {

        this.active = active;
        this.authorities = authorities;
        this.userName = userName;
        this.password = password;
        this.accNonExpired = accNonExpired;
        this.credentialNonExpired = credentialNonExpired;
        this.accNonLocked = accNonLocked;
    }

    public static UserDetails builder(User user)
    {

        List<GrantedAuthority> grantedAuthorities=new ArrayList<>();

         user.getRoles().forEach(role-> {

                    grantedAuthorities.add(new SimpleGrantedAuthority(role.getName().name()));

                    role.getPermissions().forEach(perm->{
                            grantedAuthorities.add(new SimpleGrantedAuthority(perm.getName().name()));
                    });

                });

         return new AuthUserDetails((user.getActive()==1),grantedAuthorities,user.getUserName(),user.getPassword(),
                 (user.getAccNonExpired()==1), (user.getCredentialNonExpired()==1),(user.getAccNonLocked()==1));

      
    }

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

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

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

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

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

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

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

User.class

@Entity
@Table(name="user56",schema = Schema.OAUTH2,uniqueConstraints = @UniqueConstraint(
        columnNames = "username"
))
@Getter
@Setter
public class User {

    @Id
    @SequenceGenerator(name="user_id_gen",sequenceName = Schema.OAUTH2+".user_id_seq",initialValue = 1003,allocationSize = 1)
    @GeneratedValue(generator = "user_id_gen",strategy = GenerationType.SEQUENCE)
    @Column(name = "user_id")
    private int userId;
    @Column(name = "username")
    private String userName;
    @Column(name = "password")
    private String password;
    @Column(name = "email")
    private String email;
    @Column(name = "active")
    private int active;
    @Column(name = "acc_non_expired")
    private int accNonExpired;
    @Column(name = "credential_non_expired")
    private int credentialNonExpired;
    @Column(name = "acc_non_locked")
    private int accNonLocked;

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

}
             

OAuthClient.class

@Entity
@Table(name = "oauth_client_details",schema = Schema.OAUTH2)
@Getter
@Setter
public class OAuthClient {

    @Id
    @Column(name = "client_id")
    private String clientId;
    @Column(name="client_secret")
    private String clientSecret;
    @Column(name = "web_server_redirect_uri")
    private String webServerRedirectUri;
    @Column(name = "scope")
    private String scope;
    @Column(name = "accsess_token_validity")
    private String accessTokenValidity;
    @Column(name = "refresh_token_validity")
    private String refreshTokenValidity;
    @Column(name = "resource_id")
    private String resourceId;
    @Column(name="authorized_grant_types")
    private String authorizedGrantType;
    @Column(name = "authorities")
    private String authorities;
    @Column(name = "additional_information")
    private String additionalInformation;
    @Column(name = "autoapprove")
    private String autoApprove;

}

ответ неавторизован (401) через почтальона

enter image description here

Updated

AuthEntryPoint.class

@Component
public class AuthEntryPoint implements AuthenticationEntryPoint {


        Logger ERROR_LOGGER= LoggerFactory.getLogger(AuthEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {

        ERROR_LOGGER.error("Unauthorized error : {}",authException.getMessage());

        response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Error : Unauthorized");

    }
}

Updated 2

when I tried to run the application under the debugging mode then the following error will occur

FileNotFoundException@769

введите описание изображения здесь

Ответы [ 2 ]

1 голос
/ 18 августа 2020

Наконец, я обнаружил, где возникает проблема. Когда я отправляю запрос на сервер, возникает несанкционированная (401) ошибка со следующим сообщением:

Закодированный пароль не похож на BCrypt

Итак, я заменил следующий код в SecurityConfig.class

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

  // replace to

  @Bean
  public PasswordEncoder getPasswordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

Он работает нормально, но все же я не мог найти, почему BCrytptPasswordEncoder не работает, даже если секретные значения сохраняются в формате BCrypt. В любом случае большое спасибо @doctore за ответ

1 голос
/ 06 августа 2020

Я скачал ваш проект из здесь , и сначала я обнаружил только опечатку в столбцах: accsess_token_validity и resource_id

После этого я добавил необходимые таблицы вашего проекта и включают некоторую фиктивную информацию. По этой причине я почти уверен, что ваша проблема связана со значениями password в ваших таблицах oauth_client_details и user56, потому что только когда сохраненное значение не совпадает с ожидаемым, я получил 401.

Учтите, что вы определили два разных BCryptPasswordEncoder экземпляра:

  1. provider.setPasswordEncoder(new BCryptPasswordEncoder()) для пользователей, включенных в user56.

  2. clients.jdbc(dataSource).passwordEncoder(new BCryptPasswordEncoder()) для пользователей, включенных в oauth_clients_detail

После того, как я исправил это и сохранил ожидаемые, я добился того, что конечная точка вернула ожидаемый результат:

Basic Authentication Required parameters and result

As I told you in previous comments, there several classes will help you to find the cause of the error:


BasicAuthenticationFilter will get from the request the Basic Auth provided and executes the authentication process.

BasicFilterAuthentication


BasicAuthenticationConverter will extract Basic Auth provided from the request really.

BasicAuthenticationConverter


JbdcClientDetailsService will get from oauth_clients_detail the information related with provided client_id in Basic Auth.

JbdcClientDetailsService


The following pictures are the most important to verify if your passwords match. That method will invoke twice: first for client_id / client_pass and second for username / password.

I have included some useful information in the "debug area"

DaoAuthenticationProviderClientId DaoAuthenticationProviderUsername

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...