2016-01-10 3 views
2

Приложение Spring Boot имеет пользовательские UserDetails. Все работает правильно, когда все приложение выполняет аутентификацию существующих пользователей из базы данных с классом User. Однако возникают проблемы, когда я пытаюсь внести изменения, чтобы люди могли создавать новые учетные записи пользователей через графический интерфейс приложения. В частности, изменение пользовательского UserDetailsService, чтобы стать UserDetailsManager, необходимо изменить класс User на явно implement UserDetails, что, в свою очередь, приводит к тому, что Spring не сможет скомпилировать банку, когда я ввожу java -jar target/modular-0.0.1-SNAPSHOT.jar в терминал из корневого каталога приложения.Невозможно автоподписать пользовательские UserDetails

Сообщение об ошибке ядро:

java.lang.IllegalArgumentException: 
    Not an managed type: interface org.springframework.security.core.userdetails.UserDetails 

Какие конкретные изменения должны быть внесены в код ниже, для того, чтобы дать возможность Spring, чтобы иметь возможность скомпилировать приложение с обычаем UserDetails и UserDetailsManager?

Вот код для базового класса в приложение, которое включает в себя конфигурацию Spring Security:

@SpringBootApplication 
@Controller 
@EnableJpaRepositories(basePackages = "demo", considerNestedRepositories = true) 
public class UiApplication extends WebMvcConfigurerAdapter { 

    @Autowired 
    private WebLeadRepository myrepo; 

    @Autowired 
    private Users users;//duplicate from AuthenticationSecurity internal class below. Remove one? 

    // Match everything without a suffix (so not a static resource) 
    @RequestMapping(value = "/{[path:[^\\.]*}") 
    public String redirect() { 
     // Forward to home page so that route is preserved. 
     return "forward:/"; 
    } 

    @RequestMapping("/user") 
    @ResponseBody 
    public Principal user(Principal user) { 
     return user; 
    } 

//lots of other @RequestMapping @ResponseBody url handling methods 

    public static void main(String[] args) { 
     SpringApplication.run(UiApplication.class, args); 
    } 

    @Order(Ordered.HIGHEST_PRECEDENCE) 
    @Configuration 
    protected static class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter { 

     @Autowired 
     private Users users; 

     @Override 
     public void init(AuthenticationManagerBuilder auth) throws Exception { 
      auth.userDetailsService(users); 
     } 
    } 

    @SuppressWarnings("deprecation") 
    @Configuration 
    @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) 
    @EnableWebMvcSecurity 
    @EnableGlobalMethodSecurity(prePostEnabled = true) 
    protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter { 

     @Override 
     protected void configure(HttpSecurity http) throws Exception { 
      http.httpBasic().and().authorizeRequests() 
       .antMatchers("/sign-up").permitAll() 
       .antMatchers("/index.html", "/", "/login", "/something*") 
       .permitAll().anyRequest().authenticated().and().csrf() 
       .csrfTokenRepository(csrfTokenRepository()).and() 
       .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class); 
     } 

     private Filter csrfHeaderFilter() { 
      return new OncePerRequestFilter() { 
       @Override 
       protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 
        throws ServletException, IOException { 
         CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); 
         if (csrf != null) { 
          Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN"); 
          String token = csrf.getToken(); 
          if (cookie == null || token != null && !token.equals(cookie.getValue())) { 
           cookie = new Cookie("XSRF-TOKEN", token); 
           cookie.setPath("/"); 
           response.addCookie(cookie); 
         } 
        } 
        filterChain.doFilter(request, response); 
       } 
      }; 
     } 

     private CsrfTokenRepository csrfTokenRepository() { 
      HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); 
      repository.setHeaderName("X-XSRF-TOKEN"); 
      return repository; 
     } 
    } 

    @Repository//This repository is what Spring cannot seem to create in the stack trace 
    interface UserRepository extends CrudRepository<UserDetails, Long> { 
     User findByName(String name); 
    } 

} 

Users.java является:

@Service 
class Users implements UserDetailsManager { 

    private UserRepository repo; 

    @Autowired 
    public Users(UserRepository repo) {this.repo = repo;} 

    @Override 
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
     User user = repo.findByName(username); 
     if (user == null) {throw new UsernameNotFoundException("Username was not found. ");} 
     List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"); 
     if (username.equals("admin")) {auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN");} 
     String password = user.getPassword(); 
     return new org.springframework.security.core.userdetails.User(username, password, auth); 
    } 

    @Override 
    public void createUser(UserDetails user) {// TODO Auto-generated method stub 
     repo.save(user); 
    } 

    @Override 
    public void updateUser(UserDetails user) {// TODO Auto-generated method stub 
     repo.save(user); 
    } 

    @Override 
    public void deleteUser(String username) {// TODO Auto-generated method stub 
     User deluser = (User)this.loadUserByUsername(username); 
     repo.delete(deluser); 
    } 

    @Override 
    public void changePassword(String oldPassword, String newPassword) { 
     // TODO Auto-generated method stub 
    } 

    @Override 
    public boolean userExists(String username) { 
     // TODO Auto-generated method stub 
     return false; 
    } 

} 

И User.java является:

@Entity 
class User implements UserDetails{ 

    @GeneratedValue 
    @Id 
    private Long iduser; 
    private String name;//valid email address only 
    private String password; 
//lots of other properties that model all the things the User does in the app 

    //getters and setters 
    public String getName() {return name;}//valid email address 
    public void setName(String name) {this.name = name;}//valid email address 

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

    //LOTS OF OTHER GETTERS AND SETTERS OMITTED HERE, THAT MANAGE MANY CUSTOM PROPERTIES 

    // Also, All the following are for implementing UserDetails 
    @Override 
    public Collection<? extends GrantedAuthority> getAuthorities() {// TODO Auto-generated method stub 
     return null; 
    } 
    @Override 
    public String getUsername() {// TODO Auto-generated method stub 
     return null; 
    } 
    @Override 
    public boolean isAccountNonExpired() {// TODO Auto-generated method stub 
     return false; 
    } 
    @Override 
    public boolean isAccountNonLocked() {// TODO Auto-generated method stub 
     return false; 
    } 
    @Override 
    public boolean isCredentialsNonExpired() {// TODO Auto-generated method stub 
     return false; 
    } 
    @Override 
    public boolean isEnabled() {// TODO Auto-generated method stub 
     return false; 
    } 
} 

WebLeadRepository.java:

public interface WebLeadRepository extends JpaRepository<WebLead, Long> { 

    List<WebLead> findByLastname(String lastName); 
    List<WebLead> findBySessionid(String sid); 
    WebLead findByIdlead(Long idl); 
} 

Полная трассировка стека слишком длинная для этой публикации, но I have uploaded the complete stack trace to a file sharing site, which you can view by clicking on this link. Кроме того, основная причина в трассировке стека приведены ниже следующим образом:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'uiApplication.UserRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not an managed type: interface org.springframework.security.core.userdetails.UserDetails 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1578) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) 
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) 
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1192) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014) 
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813) 
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741) 
    ... 38 more 
Caused by: java.lang.IllegalArgumentException: Not an managed type: interface org.springframework.security.core.userdetails.UserDetails 
    at org.hibernate.jpa.internal.metamodel.MetamodelImpl.managedType(MetamodelImpl.java:219) 
    at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:68) 
    at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getEntityInformation(JpaEntityInformationSupport.java:67) 
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:152) 
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:99) 
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:81) 
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:185) 
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:251) 
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:237) 
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) 
+2

Ваш репозиторий нуждается в 'User', а не' UserDetails' в объявлении типа. Измените 'CrudRepository ' на 'CrudRepository '. –

+0

В ваших пользовательских реализациях 'UserDetailsManager' вам нужно передать' UserDetails' в объект 'User'. –

+0

@ M.Deinum Ваши комментарии предоставили ответ. Если вы хотите написать их в качестве ответа, я был бы рад отметить его как принятый и +1. В частности, решение включало в себя создание объектов различных методов назад и вперед между «User» и «UserDetails», чтобы CrudRepository работал с экземплярами «User» с источником данных, а также позволял Spring Security работать с объектами UserDetails , – CodeMed

ответ

1

Прежде всего вам необходимо исправить отображение для вашего репозитория, объект, который вы пытаетесь сохранить, - это User, а не UserDetails. Spring Data JPA нуждается в этой информации, чтобы она могла сканировать объект и создавать необходимые привязки.

@Repository 
interface UserRepository extends CrudRepository<User, Long> { 
    User findByName(String name); 
} 

Теперь вы будете иметь проблемы компиляции в вашем пользовательских UserDetailsManager класса Users. Поскольку все методы принимают объект UserDetails, а не ваш объект User, который является ожидаемым вашим UserRepository. Чтобы исправить это, просто передайте аргумент User, прежде чем передавать его на UserRepository.

@Override 
public void createUser(UserDetails user) {// TODO Auto-generated method stub 
    repo.save((User) user); 
} 

@Override 
public void updateUser(UserDetails user) {// TODO Auto-generated method stub 
    repo.save((User) user); 
} 
+0

Я снова обратился к другому вопросу, основанному на продолжающихся исследованиях. Я надеюсь, что людям будет легче читать. Вы хотите прокомментировать это? http://stackoverflow.com/questions/34932581/how-do-i-force-spring-security-to-update-xsrf-token-cookie – CodeMed

0

Вам может понадобиться @MappedSuperClass аннотацию на интерфейсе UserDetails иначе спящий режим не будет знать, как сохранить что-либо реализующий этот интерфейс.

+0

Это не решило проблему. – CodeMed

1

Я использовал подход, состоящий из двух классов: пользовательский объект и пользовательский класс данных пользователя. Я написал вспомогательный метод для создания CustomUserDetails из пользователя (Entity). Таким образом, существует разделение/различие между вашим объектом Spring и объектом объекта.

Edit: Код

детали пользовательского пользователя: Entity

public class CustomUser extends User implements UserDetails, CredentialsContainer { 

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

@Entity 
@Table(name="users") 
public class User { 

Это все проводные вверх службы деталей на заказ пользователя:

public class CustomUserDetailsService extends JdbcUserDetailsManager implements UserDetailsService {  

В loadB yUserName (один из методов, которые вы должны реализовать в CustomUserDetailsService, я создаю CustomUserDetails от лица пользователя:

CustomUser customUser = user.getUserDetails(); 
+0

Можете ли вы показать рабочий код? В его нынешнем виде ваш ответ еще не поможет. – CodeMed

+0

Спасибо и +1 за добавление кода и разработку. Я решил сделать шаги, показанные в принятом ответе М.Диенама. – CodeMed

1

См this answer, который объясняет ошибку «Не удалось типа», который выводится в вашем StackTrace.Оливер Гирке, разработчик Spring Data, отмечает, что может возникнуть проблема с пакетом, связанная с объектами JPA, однако это будет вашей проблемой только в том случае, если вашему приложению Spring Boot не удается обнаружить все классы в вашем приложении, поэтому он может не отвечать ваш вопрос.

Другая возможность - this answer, еще раз Оливер Гирке, который отмечает, что в некоторых ситуациях вам нужна конкретная реализация вашего репозитория JPA. Вы не разместили свой WebLeadRepository, так что я не могу быть уверен.

Если ни один из этих ответов не помогает, то я тоже в тупике.

+0

Боже мой! Я рад удалить этот ответ, если это необходимо. Сходство между простым Spring-управляемым приложением с использованием JPA и вашим приложением Spring Boot с использованием JPA является ясным, и оба приложения генерируются в одном и том же исключении. Я не знал, что ты посмотрел на этот вопрос. – ben3000

+0

Это не решило проблему. Обратите внимание, что комментарии M.Deinum ниже OP выше - это то, что на самом деле решило проблему. +1 здесь просто поблагодарить этого пользователя за его время. – CodeMed

Смежные вопросы