2016-04-08 9 views
1

Ich erstelle Rest API mit Spring Boot v1.3.3. API wird von Spring Security gesichert. Ich habe den benutzerdefinierten Benutzerdetaildienst implementiert, um den benutzerdefinierten Prinzipal im Authentifizierungskontext zu haben.Authentifizierung Principal ist leer, während Spring Session Redis

Ich musste Sitzungen der API mit anderen Spring-App teilen, also wählte ich Spring Session mit Redis-Server in meiner App mit diesem Tutorial docs.spring.io/spring-session/docs/current/reference/html5/ guides/security.html. Unglücklicherweise hat es dazu geführt, dass der Authentication Principal nicht mehr funktionierte. Wenn ich versuche, den aktuellen Principal entweder durch die Annotation @AuthenticationPrincipal CustomUserDetails user oder durch SecurityContextHolder.getContext().getAuthentication().getPrincipal() zu erhalten, werden meine benutzerdefinierten Benutzerdetails zurückgegeben, aber mit Id = 0 und allen Feldern null (screen from debugging). Ich kann nicht einmal den Benutzernamen von SecurityContextHolder.getContext().getAuthentication().getName() bekommen.

Nachdem ich Redis Code und Maven Abhängigkeit kommentiert funktioniert es (see debug screen). Wie funktioniert es mit Spring Session und Redis Server?

Hier ist ein Code aus der App: für

@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 (gebraucht:

Einige Beispielverfahren Haupt

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

Anwendung und Redis Config überprüfen Redis Session)

public class Initializer extends AbstractHttpSessionApplicationInitializer { 

} 

SecurityInitializer (für Redis Sitzung)

public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer { 

    public SecurityInitializer() { 
     super(WebSecurityConfig.class, AppConfig.class); 
    } 
} 

WebSecurityConfig (Spring Security Config)

@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 Abhängigkeiten

<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> 

Antwort

2

Ich löste dieses Problem. Es stellte sich heraus, dass Spring-Session das Principal-Objekt serialisiert. Meine benutzerdefinierte Implementierung von war eine Unterklasse von Hibernate Model User Klasse. Ich löste es durch Implementierung Serializable Schnittstelle in meinem benutzerdefinierten UserDetails, User Modell und alle Klassen in diesem Modell verwendet.

+0

Ich habe auch eine benutzerdefinierte UserDetails, und ich habe es geändert, um Serializable zu implementieren (öffentliche Klasse MyUserDetails erweitert org.springframework.security.core.userdetails.User implementiert Serializable).Ich verwende Spring Session mit JDBC-Backend, aber die Spalte Prinzipalname ist NULL. Seltsam. – yglodt

+0

Ich stimme @yglodt zu, das ist vielleicht nicht die gute Antwort. Wenn Sie die 'org.springframework.security.core.userdetails.UserDetails' Klasse überprüfen, wird die' java.io.Serializable' Schnittstelle bereits erweitert – BigDong

1

Damit es in meinem Fall funktioniert, musste ich auch sicherstellen, dass die Servlet-Filter in der richtigen Reihenfolge eingerichtet wurden.

das war für mich:

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

Danach wird die principal nicht mehr leer war.

Verwandte Themen