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

Актуализирах услугата Spring REST на Рой Кларксън (https://github.com/royclarkson/spring-rest-service-oauth) с базирано на Jdbc хранилище на токени. Оригиналното изпълнение използва хранилище на токени в паметта. Успях да видя подробностите за потребителя в потребителския обект. От друга страна, след преминаване към хранилище на токени, базирано на Jdbc, всички полета в потребителския обект бяха празни. Изглежда по някакъв начин сигурността на 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 среща точно същия проблем. Превключих от in-memory tokenStore към jdbcTokenStore и получих същия проблем.   -  person Harshit Syal    schedule 30.06.2015
comment
Отстраних грешки през целия пролетен филтри и открих, че при запазване на blob за удостоверяване Principal има стойности userDetail. Но докато четем обратно, се оказва, че е нула.   -  person Harshit Syal    schedule 30.06.2015


Отговори (2)


Проблемът е, че „UserRepositoryUserDetails“, който създавате, не може да се сериализира.

UserRepositoryUserDetails имплементира „UserDetails“, който е Serializable, но класът, който разширява „User“, не е Serializable.

Трябва също да получавате предупреждение от комилатора, за да добавите serialId.

Решение

Направете своите UserRepositoryUserDetails като сериализируеми.

person Harshit Syal    schedule 29.06.2015

След като направих сериализируем потребителски клас всичко работи според очакванията.

person George    schedule 17.07.2015
comment
Благодаря ви за този отговор. Това много ми помогна - person Naveen; 27.02.2020