2016-03-10 7 views
8

Я пытаюсь создать приложение на основе Spring Boot (1.2.7.RELEASE) и Vaadin (7.6.3). Моя проблема в том, что я не могу интегрировать Spring Security с Vaadin. Я хочу, чтобы пользовательский Vaadin создал LoginScreen и Spring Security. Мой проект установки выглядит следующим образом:Spring Boot Security с Vaadin Войти

@Configuration 
@EnableWebSecurity 
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.csrf().disable(). 
       exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")).accessDeniedPage("/accessDenied") 
       .and().authorizeRequests() 
       .antMatchers("/VAADIN/**", "/PUSH/**", "/UIDL/**", "/login", "/login/**", "/error/**", "/accessDenied/**", "/vaadinServlet/**").permitAll() 
       .antMatchers("/authorized", "/**").fullyAuthenticated(); 
    } 
} 

А вот мой Vaadin Войти UI

@SpringUI(path = "/login") 
    @Title("LoginPage") 
    @Theme("valo") 
    public class LoginUI extends UI { 

     TextField user; 
     PasswordField password; 
     Button loginButton = new Button("Login", this::loginButtonClick); 
     private static final String username = "username"; 
     private static final String passwordValue = "test123"; 

     @Override 
     protected void init(VaadinRequest request) { 
      setSizeFull(); 

      user = new TextField("User:"); 
      user.setWidth("300px"); 
      user.setRequired(true); 
      user.setInputPrompt("Your username"); 

      password = new PasswordField("Password:"); 
      password.setWidth("300px"); 
      password.setRequired(true); 
      password.setValue(""); 
      password.setNullRepresentation(""); 

      VerticalLayout fields = new VerticalLayout(user, password, loginButton); 
      fields.setCaption("Please login to access the application"); 
      fields.setSpacing(true); 
      fields.setMargin(new MarginInfo(true, true, true, false)); 
      fields.setSizeUndefined(); 

      VerticalLayout uiLayout = new VerticalLayout(fields); 
      uiLayout.setSizeFull(); 
      uiLayout.setComponentAlignment(fields, Alignment.MIDDLE_CENTER); 
      setStyleName(Reindeer.LAYOUT_BLUE); 
      setFocusedComponent(user); 

      setContent(uiLayout); 
     } 

     public void loginButtonClick(Button.ClickEvent e) { 
      //authorize/authenticate user 
      //tell spring that my user is authenticated and dispatch to my mainUI 
     } 

    } 

Когда я начинаю моя весна приложение перенаправляет меня к авторизации UI, который отлично.

Но я не знаю, как аутентифицировать пользователя в отношении механизма безопасности пружины и отправлять его в мой основной интерфейс.

Я также сталкиваюсь с проблемой с токенами csrf, если я не отключу csrf. Я получаю токен crfs, является единственным исключением. Я нашел много примеров для решения этих проблем, но нет решения с Vaadin.

Спасибо за помощь.

+1

У меня есть рабочий пример приложения с использованием Vaadin + Spring Boot + Spring Security, но, к сожалению, этот пример идет без какой-либо документации. Но, возможно, вы можете получить вдохновение от кода для своей задачи: https://github.com/rolandkrueger/vaadin-by-example/tree/master/en/architecture/SpringBootSecurity –

+0

Эй, Роланд, я взглянул на ваш проект и это очень помогло. Спасибо, что поделились ею. –

+0

Рад, что я мог бы помочь, не отвечая на ваш вопрос ;-) –

ответ

20

После недели борьбы и исследований я смог получить эту работу. Это было очень утомительно, потому что в Интернете было много информации и решений, большинство из которых использовало конфигурацию на основе xml или вход в систему на основе JSP, до сих пор я не мог найти другое решение без файла конфигурации xml с использованием инфраструктуры Vaadin для создания пользовательская страница входа.

Я не могу гарантировать, что это лучшая практика или простое решение. Более того, я не оценивал каждую его часть, механизм регистрации работает, насколько я могу видеть, но, возможно, могут быть некоторые проблемы, которые я еще не обнаружил.

Возможно, это поможет кому-то, кто сталкивается с такой же проблемой, поэтому я отправлю свой ответ здесь.

Прежде всего моего securityConfig:

@Resource(name = "authService") 
private UserDetailsService userDetailsService; 

@Override 
protected void configure(HttpSecurity http) throws Exception { 
       http.csrf().disable(). 
         exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")).accessDeniedPage("/accessDenied") 
         .and().authorizeRequests() 
         .antMatchers("/VAADIN/**", "/PUSH/**", "/UIDL/**", "/login", "/login/**", "/error/**", "/accessDenied/**", "/vaadinServlet/**").permitAll() 
         .antMatchers("/authorized", "/**").fullyAuthenticated(); 
      } 

      @Bean 
      public DaoAuthenticationProvider createDaoAuthenticationProvider() { 
       DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); 

       provider.setUserDetailsService(userDetailsService); 
       provider.setPasswordEncoder(passwordEncoder()); 
       return provider; 
      } 

      @Bean 
      public BCryptPasswordEncoder passwordEncoder() { 
       return new BCryptPasswordEncoder(); 
      } 

Вы должны отключить CRSF, но это не проблема, так как Vaadin имеет свою собственную защиту CRSF.

Кроме того, вы должны разрешить некоторые идентификаторы URI, так Vaadin могут получить доступ к своим ресурсам: /VAADIN/** является абсолютно необходимым, я бы также рекомендую, чтобы /vaadinServlet/**, /PUSH/** и /HEARTBEAT/**, но это зависит от того, какие части Vaadin вы используете.

Второй мой UserDetailsService:

@Service("authService") 
public class AuthService implements UserDetailsService { 

     @Autowired 
     CustomUserRepository userRepository; 

     @Override 
     public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { 
      CustomUser user = userRepository.findCustomUserByUserName(userName); 

      return user; 
     } 

} 

DaoAuthenticationProvider использует 'loadUserByUserName метод UserDetails, чтобы получить объект класса, который реализует интерфейс UserDetails. Имейте в виду, что каждый атрибут, описанный в UserDetailsInterface, не должен быть нулевым, иначе вы получите NullPointerException, который позже будет отправлен DaoAuthenticationProvider.

Я создал JPA Entity, который реализует интерфейс UserDetails:

@Entity 
public class CustomUser implements UserDetails { 

     @Id 
     @GeneratedValue(strategy = GenerationType.IDENTITY) 
     Long id; 
     @ManyToMany(fetch = FetchType.EAGER) 
     Collection<Authorities> authorities; 
     String password; 
     String userName; 
     Boolean accountNonExpired; 
     Boolean accountNonLocked; 
     Boolean credentialsNonExpired; 
     Boolean enabled; 

     @Autowired 
     @Transient 
     BCryptPasswordEncoder passwordEncoder; 

     @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 accountNonExpired; 
     } 

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

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

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

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

     public void setAuthorities(Collection<Authorities> authorities) { 
      this.authorities = authorities; 
     } 

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

     public void setUserName(String userName) { 
      this.userName = userName; 
     } 

     public void setAccountNonExpired(Boolean accountNonExpired) { 
      this.accountNonExpired = accountNonExpired; 
     } 

     public void setAccountNonLocked(Boolean accountNonLocked) { 
      this.accountNonLocked = accountNonLocked; 
     } 

     public void setCredentialsNonExpired(Boolean credentialsNonExpired) { 
      this.credentialsNonExpired = credentialsNonExpired; 
     } 

     public void setEnabled(Boolean enabled) { 
      this.enabled = enabled; 
     } 

    } 

Plus Власти Entity:

@Entity 
public class Authorities implements GrantedAuthority { 

     @Id 
     @GeneratedValue(strategy = GenerationType.IDENTITY) 
     Long id; 

     String authority; 

     @Override 
     public String getAuthority() { 
      return authority; 
     } 

     public void setAuthority(String authority) { 
      this.authority = authority; 
     } 

} 

Очевидно, что вы будете хранить некоторые пользовательские данные в базе данных первого до аутентификация будет работать.

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

В Vaadin я мог установить путь URI в классе аннотаций:

@SpringUI(path = "/login") 
@Title("LoginPage") 
@Theme("valo") 
public class LoginUI extends UI { 
    //... 
} 

С этой конфигурацией мой экран Логин доступен на localhost:port/login и моего основного приложения на localhost:port/main.

Я войти в систему пользователя программно в методе button.click в моем loginUI:

Authentication auth = new UsernamePasswordAuthenticationToken(userName.getValue(),password.getValue()); 
Authentication authenticated = daoAuthenticationProvider.authenticate(auth); 
      SecurityContextHolder.getContext().setAuthentication(authenticated); 

//redirect to main application 
getPage().setLocation("/main"); 

Я надеюсь, что это помогло некоторым из вас.

+0

У вас есть целая установка в публичном репозитории для использования в качестве ссылки? –

3

В качестве альтернативы данному подходу вы также можете положиться на vaadin4spring, an 'unofficial' set of extensions to further integrate Vaadin and Spring.

В настоящее время он предлагает two ways для интеграции Spring Security, которые, кажется, работают нормально (даже если они не работают для вас, образцы кода должны дать вам достаточно информации для написания специального кода интеграции).

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