2016-04-08 4 views
1

Я создаю API для отдыха с использованием Spring Boot v1.3.3. API защищен Spring Security. Я внедрил пользовательскую информацию о пользователях, чтобы иметь пользовательский принцип в контексте аутентификации.Принцип аутентификации пуст во время использования Spring Session Redis

Мне нужно было обмениваться сеансами API с другим приложением Spring, поэтому я решил реализовать Spring Session с сервером Redis в своем приложении, используя этот учебник docs.spring.io/spring-session/docs/current/reference/html5/ руководства/security.html. К сожалению, он заставил Принципа аутентификации прекратить работу. Когда я пытаюсь получить текущего Принципала либо по аннотации @AuthenticationPrincipal CustomUserDetails user, либо по SecurityContextHolder.getContext().getAuthentication().getPrincipal(), он возвращает мои пользовательские данные пользователя, но с Id = 0 и все поля установлены на null (screen from debugging). Я даже не могу получить имя пользователя от SecurityContextHolder.getContext().getAuthentication().getName().

После того как я прокомментировал код Redis и зависимость от maven, он работает (see debug screen). Как заставить его работать с Spring Session и сервером Redis?

Вот код из приложения:

Некоторые примеры метод проверки Principal

@RequestMapping(value = "/status", method = RequestMethod.GET) 
public StatusData status(@AuthenticationPrincipal CustomUserDetails user) { 
    User user2 = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 
    if (user != null) { 
     String name = user.getUsername(); 
     return new StatusData(name); 
    } else return new StatusData(null); 
} 

Применение и Redis конфигурации:

@Configuration 
@EnableRedisHttpSession 
public class AppConfig { 

    @Bean 
    public JedisConnectionFactory connectionFactory() { 
     return new JedisConnectionFactory(); 
    } 

    @Bean 
    public CookieSerializer cookieSerializer() { 
     DefaultCookieSerializer serializer = new DefaultCookieSerializer(); 
     serializer.setCookieName("JSESSIONID"); 
     serializer.setCookiePath("/"); 
     serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$"); 
     return serializer; 
    } 

    @Bean 
    public ShaPasswordEncoder shaEncoder() { 
     return new ShaPasswordEncoder(256); 
    } 

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

    @Bean(name = "messageSource") 
    public ResourceBundleMessageSource messageSource() { 
     ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource(); 
     resourceBundleMessageSource.setBasename("messages/messages"); 
     return resourceBundleMessageSource; 
    } 

    @Bean 
    public Validator basicValidator() { 
     LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); 
     validator.setValidationMessageSource(messageSource()); 
     return validator; 
    } 

    public AppConfig() { 
     DateTimeZone.setDefault(DateTimeZone.UTC); 
    } 
} 

Initializer (используется для Redis Session)

public class Initializer extends AbstractHttpSessionApplicationInitializer { 

} 

SecurityInitializer (используется для Redis сессии)

public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer { 

    public SecurityInitializer() { 
     super(WebSecurityConfig.class, AppConfig.class); 
    } 
} 
(конфигурации Spring Security)

WebSecurityConfig

@Configuration 
@EnableWebSecurity 
//@EnableWebMvcSecurity 
@ComponentScan(basePackageClasses = {UserRepository.class, CustomUserDetailsService.class}) 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Autowired 
    private DataSource dataSource; 

    @Autowired 
    private UserDetailsService customUserDetailsService; 

    @Autowired 
    private HttpAuthenticationEntryPoint httpAuthenticationEntryPoint; 

    @Autowired 
    private AuthSuccessHandler authSuccessHandler; 

    @Autowired 
    private AuthFailureHandler authFailureHandler; 

    @Autowired 
    private HttpLogoutSuccessHandler logoutSuccessHandler; 

    @Autowired 
    private BCryptPasswordEncoder bCryptPasswordEncoder; 

    /** 
    * Persistent token repository stored in database. Used for remember me feature. 
    */ 
    @Bean 
    public PersistentTokenRepository tokenRepository() { 
     JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl(); 
     db.setDataSource(dataSource); 
     return db; 
    } 

    /** 
    * Enable always remember feature. 
    */ 
    @Bean 
    public AbstractRememberMeServices rememberMeServices() { 
     CustomTokenPersistentRememberMeServices rememberMeServices = new CustomTokenPersistentRememberMeServices("xxx", customUserDetailsService, tokenRepository()); 
     rememberMeServices.setAlwaysRemember(true); 
     rememberMeServices.setTokenValiditySeconds(1209600); 
     return rememberMeServices; 
    } 

    /** 
    * Configure spring security to use in REST API. 
    * Set handlers to immediately return HTTP status codes. 
    * Enable remember me tokens. 
    */ 
    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 
       .csrf().disable() 
       .exceptionHandling() 
       .authenticationEntryPoint(httpAuthenticationEntryPoint) 
       .and() 
       .authorizeRequests() 
       .antMatchers("/cookie", "/register", "/redirect/**", "/track/**") 
       .permitAll() 
       .anyRequest().authenticated() 
       .and() 
       .formLogin() 
       .loginPage("/login") 
       .permitAll() 
       .successHandler(authSuccessHandler) 
       .failureHandler(authFailureHandler) 
       .and() 
       .logout() 
       .permitAll().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler) 
       .and() 
       .rememberMe().rememberMeServices(rememberMeServices()) 
       .and() 
       .headers() 
       .addHeaderWriter(new HeaderWriter() { 
        /** 
        * Header to allow access from javascript AJAX in chrome extension. 
        */ 
        @Override 
        public void writeHeaders(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { 
         String corsUrl = "https://mail.google.com"; 
         if (httpServletRequest.getHeader("Origin") != null && httpServletRequest.getHeader("Origin").equals(corsUrl)) { 
          httpServletResponse.setHeader("Access-Control-Allow-Origin", "https://mail.google.com"); 
          httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); 
          httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); 
          httpServletResponse.setHeader("Access-Control-Expose-Headers", "Location"); 
         } 
        } 
       }); 
    } 

    /** 
    * Set custom user details service to allow for store custom user details and set password encoder to BCrypt. 
    */ 
    @Autowired 
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 
     auth 
       .userDetailsService(customUserDetailsService).passwordEncoder(bCryptPasswordEncoder); 
    } 
} 

Maven зависимостей

<dependencies> 
    <dependency> 
     <groupId>${project.groupId}</groupId> 
     <artifactId>models</artifactId> 
     <version>${project.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-web</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-data-jpa</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-security</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-validator</artifactId> 
     <version>5.2.3.Final</version> 
    </dependency> 
    <dependency> 
     <groupId>joda-time</groupId> 
     <artifactId>joda-time</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>org.jadira.usertype</groupId> 
     <artifactId>usertype.core</artifactId> 
     <version>3.1.0.CR1</version> 
    </dependency> 
    <dependency> 
     <groupId>com.fasterxml.jackson.jaxrs</groupId> 
     <artifactId>jackson-jaxrs-json-provider</artifactId> 
     <version>2.2.1</version> 
    </dependency> 
    <dependency> 
     <groupId>com.fasterxml.jackson.datatype</groupId> 
     <artifactId>jackson-datatype-joda</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>com.maxmind.geoip2</groupId> 
     <artifactId>geoip2</artifactId> 
     <version>2.6.0</version> 
    </dependency> 
    <dependency> 
     <groupId>com.ganyo</groupId> 
     <artifactId>gcm-server</artifactId> 
     <version>1.0.2</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.session</groupId> 
     <artifactId>spring-session</artifactId> 
     <version>1.1.1.RELEASE</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-redis</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>mysql</groupId> 
     <artifactId>mysql-connector-java</artifactId> 
     <scope>runtime</scope> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-test</artifactId> 
     <scope>test</scope> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.security</groupId> 
     <artifactId>spring-security-test</artifactId> 
     <version>4.0.4.RELEASE</version> 
     <scope>test</scope> 
    </dependency> 
    <dependency> 
     <groupId>com.jayway.jsonpath</groupId> 
     <artifactId>json-path</artifactId> 
     <scope>test</scope> 
    </dependency> 
</dependencies> 

ответ

2

Я решил эту проблему. Оказалось, что Spring-Session сериализует объект Principal. Моя пользовательская реализация UserDetails была подклассом класса Hibernate Model User. Я решил это, реализовав интерфейс Serializable в моей модели UserDetails, User и все классы, используемые в этой модели.

+0

У меня также есть пользовательские UserDetails, и я изменил его для реализации Serializable (открытый класс MyUserDetails расширяет org.springframework.security.core.userdetails.User реализует Serializable).Я использую Spring Session с бэкэнд JDBC, но имя_столбца columnNULL. Странный. – yglodt

+0

Я согласен с @yglodt, это может быть не лучший ответ. Если вы проверяете класс 'org.springframework.security.core.userdetails.UserDetails', он уже расширяет интерфейс' java.io.Serializable' – BigDong

1

Чтобы это работало в моем случае, я также должен был убедиться, что фильтры Servlet настроены в правильном порядке.

Для меня это было:

... 
<filter-name>CharacterEncodingFilter</filter-name> 
... 
<filter-name>springSessionRepositoryFilter</filter-name> 
... 
<filter-name>springSecurityFilterChain</filter-name> 
... 
<filter-name>csrfFilter</filter-name> 
... 

После этого principal не был пуст больше.

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

  • Нет связанных вопросов^_^