Невозможно заполнить объект пользователя при использовании хранилища токенов Spring OAuth2 Jdbc

Я обновил сервис Spring REST Роя Кларксона (https://github.com/royclarkson/spring-rest-service-oauth) с хранилищем токенов на основе Jdbc. Первоначальная реализация использует хранилище токенов в памяти. Я смог увидеть информацию о пользователе в объекте User. С другой стороны, после переключения на хранилище токенов на основе Jdbc все поля в объекте User были пустыми. Похоже, что безопасность Spring не смогла связать токен доступа с пользователем, под которым я получил токен, когда использовал хранилище токенов на основе Jdbc.

Реализация хранилища токенов в памяти:

@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
        AuthorizationServerConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    private TokenStore tokenStore = new InMemoryTokenStore();

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

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Bean
    public ClientDetailsService clientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
            throws Exception {
        // @formatter:off
        endpoints
            .tokenStore(this.tokenStore)
            .authenticationManager(this.authenticationManager)
            .userDetailsService(userDetailsService);
        // @formatter:on
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .withClientDetails(clientDetailsService);
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setTokenStore(this.tokenStore);
        return tokenServices;
    }
}

Конечная точка REST:

@RequestMapping("/greeting")
public Greeting greeting(@AuthenticationPrincipal User user) {
    return new Greeting(counter.incrementAndGet(), String.format(template, user.getName()));
}

user.getName() возвращает имя пользователя, под которым я получил токен доступа.

Реализация хранилища токенов jdbc:

@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
        AuthorizationServerConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private TokenStore tokenStore;

    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

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

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Bean
    public ClientDetailsService clientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
            throws Exception {
        // @formatter:off
        endpoints
            .tokenStore(this.tokenStore)
            .authenticationManager(this.authenticationManager)
            .userDetailsService(userDetailsService);
        // @formatter:on
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .withClientDetails(clientDetailsService);
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setTokenStore(this.tokenStore);
        return tokenServices;
    }
}

Конечная точка REST:

@RequestMapping("/greeting")
public Greeting greeting(@AuthenticationPrincipal User user) {
    return new Greeting(counter.incrementAndGet(), String.format(template, user.getName()));
}

user.getName() возвращает ноль.

CustomUserDetailsService

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    @Autowired
    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByLogin(username);
        if (user == null) {
            throw new UsernameNotFoundException(String.format("User %s does not exist!", username));
        }
        return new UserRepositoryUserDetails(user);
    }

    private final static class UserRepositoryUserDetails extends User implements UserDetails {

        private static final long serialVersionUID = 1L;

        private UserRepositoryUserDetails(User user) {
            super(user);
        }

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

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

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

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

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

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

Пользователь

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @NotEmpty
    private String name;

    @NotEmpty
    @Column(unique = true, nullable = false)
    private String login;

    @NotEmpty
    private String password;

    @NotEmpty
    private String privilege;

    @JsonIgnore
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "role_id") })
    private Set<Role> roles = new HashSet<Role>();

    public User() {
    }

    public User(User user) {
        super();
        this.id = user.getId();
        this.name = user.getName();
        this.login = user.getLogin();
        this.password = user.getPassword();
        this.roles = user.getRoles();
        this.privilege = user.getPrivilege();
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPrivilege() {return privilege; }

    public void setPrivilege(String privilege) {this.privilege = privilege; }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }
}

person GomezW    schedule 04.06.2015    source источник
comment
Разве это не зависит от реализации CustomUserDetailsService (которую вы не показали)?   -  person Dave Syer    schedule 05.06.2015
comment
Привет @DaveSyer, я обновил сообщение, включив в него реализацию «CustomUserDetailsService» и «User». 'user.getName()' вернет имя, хранящееся в таблице _User_table. Я не знаю, почему он может только прочитать значение, когда я использую хранилище токенов в памяти.   -  person GomezW    schedule 05.06.2015
comment
Я тоже не знаю, но @Autowired поля, которое определено как @Bean в том же классе, вызывает проблемы, поэтому я предлагаю вам не делать этого для начала.   -  person Dave Syer    schedule 06.06.2015
comment
+1 столкнулся с точно такой же проблемой. Переключился с tokenStore в памяти на jdbcTokenStore и получил ту же проблему.   -  person Harshit Syal    schedule 30.06.2015
comment
Отладил все весенние фильтры и обнаружил, что при сохранении большого двоичного объекта аутентификации у принципала были значения userDetail. Но при чтении обратно он оказывается нулевым.   -  person Harshit Syal    schedule 30.06.2015


Ответы (2)


Проблема в том, что создаваемый вами «UserRepositoryUserDetails» не сериализуем.

UserRepositoryUserDetails реализует «UserDetails», который является сериализуемым, но класс, который он расширяет, «Пользователь» не является сериализуемым.

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

Решение

Сделайте свои UserRepositoryUserDetails сериализуемыми.

person Harshit Syal    schedule 29.06.2015

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

person George    schedule 17.07.2015
comment
Спасибо за этот ответ. Это мне очень помогло - person Naveen; 27.02.2020