Spring REST security - konfiguracja w javie

Odpowiedz Nowy wątek
2015-06-24 12:13

Rejestracja: 10 lat temu

Ostatnio: 5 godzin temu

0

Robię projekt w Spring 4.1.6, Spring Security 4.0.1, mam Resta który działa, teraz chcę zrobić jego autoryzację, tak sobie pomyślałem że będzie ona oparta o tokeny. Poniżej przedstawiam moje klasy konfiguracyjne:
AppConfig

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "pl.wrweb.springrest")
public class AppConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean(name = "messageSource")
    public ReloadableResourceBundleMessageSource getMessageSource() {
        ReloadableResourceBundleMessageSource resource = new ReloadableResourceBundleMessageSource();
        resource.setBasename("classpath:messages");
        resource.setDefaultEncoding("UTF-8");
        return resource;
    }

} 

AppInitializer

public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    public void onStartup(ServletContext container) throws ServletException {

        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(AppConfig.class);
        ctx.setServletContext(container);
        ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(ctx));
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/");

        container.addFilter("customFilter",  new DelegatingFilterProxy(new CustomFilter())).addMappingForUrlPatterns(null, true, "/*");

    }

    @Override
    protected String[] getServletMappings() {
        return new String[0];
    }

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[0];
    }
} 

HibernateConfiguration

 @Configuration
@EnableTransactionManagement
@ComponentScan({ "pl.wrweb.springrest.configuration" })
@PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration {

    @Autowired
    private Environment environment;

    @Bean
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setPackagesToScan(new String[] {"pl.wrweb.springrest.entity" });
        sessionFactory.setHibernateProperties(hibernateProperties());
        return sessionFactory;
     }

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
        dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
        dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
        dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
        return dataSource;
    }

    private Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
        properties.put("hibernate.hbm2ddl.auto", environment.getRequiredProperty("update"));
        properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
        return properties;
    }

    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory s) {
       HibernateTransactionManager txManager = new HibernateTransactionManager();
       txManager.setSessionFactory(s);
       return txManager;
    }
}

Dodałem też Filter który sprawdza z jakiego urla przychodzi request i czy ma token, jeśli ma sprawdza czy token poprawny jeśli poprawny generuje kolejny token i dokleja do header responsa.

@Configuration
@EnableWebSecurity
public class CustomFilter implements  Filter {

    @Autowired
    UserService userService;

    public void init(FilterConfig fc) throws ServletException {

    }

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException {

        System.out.println("doFilter");
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        if (httpServletRequest.getRequestURI().contains("/employee/")) {
            String token = httpServletRequest.getHeader("x-token");
            System.out.println("token " + token);
            Long userId = TokenUtils.getUserNameFromToken(token);
            System.out.println("ObjectStore.token " + ObjectStore.token);

            if (token!=null) {
                if (token.equals(String.valueOf(ObjectStore.token))) {
                    int index = ObjectStore.token++;
                    ObjectStore.token = index+1;
                    System.out.println("index " +ObjectStore.token + "token " + token);
                    response.setHeader("x-token", String.valueOf(ObjectStore.token));
                    fc.doFilter(req, response);
                } else {
                    response.sendError(HttpStatus.FORBIDDEN.value());
                    fc.doFilter(req, response);
                    return;
                }
            } else {
                response.sendError(HttpStatus.FORBIDDEN.value());
                fc.doFilter(req, response);
                return;
            }
        } else {
            fc.doFilter(req, res);
        }
    }

    public void destroy() {

    }

}

To tez działa, ale chcę zrobić tak żeby genrowany token był dodawany do bazy i sprawdzany z tym w bazie, w tym celu próbuje wstrzyknąć:

@Autowired
    UserService userService 

ale jest nullem. Jak można rozwiązać ten problem, czy to co robię jest prawidłowe czy może powinno się to inaczej zrobić?Dodam że zależy mi na konfiguracji w javie.

Pozostało 580 znaków

2015-06-24 12:37

Rejestracja: 11 lat temu

Ostatnio: 1 dzień temu

0

Na szybko to brakuje Ci @ComponentScan(twoj userservice package) w konfiguracji filtra.


Hate the sin, love the sinner

Pozostało 580 znaków

2015-06-24 12:44

Rejestracja: 10 lat temu

Ostatnio: 5 godzin temu

0

Zmieniłem na:
@ComponentScan("pl.wrweb.springrest.service")
public class CustomFilter implements Filter {

dalej userService jest null.

Pozostało 580 znaków

2015-06-24 13:35

Rejestracja: 5 lat temu

Ostatnio: 4 lata temu

0

Jaka może być tego przyczyna?

Na początku wykonujesz następujący kod:

 @Autowired
    UserService userService; 

Po czym już dalej nie używasz swojej zmiennej userService. Innymi słowy, z nią się nic nie dzieje. Więc po wszystkim jest wciąż nullem.

o czym Ty mówisz !?!? - NoZi 2015-06-24 13:44

Pozostało 580 znaków

2015-06-24 14:21

Rejestracja: 11 lat temu

Ostatnio: 1 dzień temu

0

Generalnie ja to robiłem mniej więcej w taki sposób:

@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private TokenUtils tokenUtils;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(authenticationFilter(), LogoutFilter.class)
                .addFilterBefore(authenticationTokenProcessingFilter(), LogoutFilter.class)
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/register").permitAll()
                .anyRequest().authenticated()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPoint())
                .and()
                .formLogin()
                .usernameParameter("email")
                .passwordParameter("password")
                .failureHandler(authenticationFailureHandler())
                .successHandler(authenticationSuccessHandler())
                .and()
                .httpBasic();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .eraseCredentials(false)
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

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

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return new AuthFailureHandler();
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return new AuthSuccessHandler();
    }

    private Filter authenticationTokenProcessingFilter() {
        return new AuthenticationTokenProcessingFilter(userDetailsService, tokenUtils);
    }

    private Filter authenticationFilter() throws Exception {
        AuthenticationFilter authenticationFilter = new AuthenticationFilter();
        authenticationFilter.setAuthenticationManager(authenticationManagerBean());
        authenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
        authenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
        return authenticationFilter;
    }
}

Przykładowy filtr

public class AuthenticationTokenProcessingFilter extends GenericFilterBean {

    private TokenUtils tokenUtils;
    private UserDetailsService userDetailsService;

    public AuthenticationTokenProcessingFilter(UserDetailsService userDetailsService, TokenUtils tokenUtils) {
        this.userDetailsService = userDetailsService;
        this.tokenUtils = tokenUtils;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;

        String authToken = tokenUtils.extractAuthTokenFromRequest(httpRequest);
        String userName = tokenUtils.getUserNameFromToken(EncryptionUtils.decode(authToken));

        if (userName != null && !isUrlForAuthentication(httpRequest)) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
            if (tokenUtils.validateToken(authToken, userDetails)) {
                UsernamePasswordAuthenticationToken authentication = getUsernamePasswordAuthenticationToken(userDetails);
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(UserDetails userDetails) {
        return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    }
}

Hate the sin, love the sinner
edytowany 2x, ostatnio: NoZi, 2015-06-24 14:24

Pozostało 580 znaków

2015-06-24 14:36

Rejestracja: 10 lat temu

Ostatnio: 5 godzin temu

0

A mógłbyś zamieścić jeszcze klasę:
AuthenticationFilter i metodę isUrlForAuthentication z AuthenticationTokenProcessingFilter?

edytowany 1x, ostatnio: wojtkuju, 2015-06-24 14:40
isUrlForAuthentication nie ma znaczenia, tam było sprawdzenie czy request leci pod właściwy adres - NoZi 2015-06-24 14:43

Pozostało 580 znaków

2015-06-24 14:45

Rejestracja: 11 lat temu

Ostatnio: 1 dzień temu

0
public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private static final Logger logger = LoggerFactory.getLogger(AuthenticationFilter.class);

    private static final String DEFAULT_FILTER_PROCESSES_URL = "/authenticate";
    private static final String POST = "POST";

    public AuthenticationFilter () {
        super(DEFAULT_FILTER_PROCESSES_URL);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (!request.getMethod().equals(POST)) {
            throw new AuthenticationServiceException("Authentication method not supported with HTTP " + request.getMethod());
        }

        AuthRequest authRequest = this.getAuthRequest(request); // klasa która wyciagała z body requesta potrzebny stuff

        UsernamePasswordAuthenticationToken loginRequest = new UsernamePasswordAuthenticationToken( authRequest
                .getEmail(), authRequest.getPassword());

        loginRequest.setDetails(authenticationDetailsSource.buildDetails(request));
        return this.getAuthenticationManager().authenticate(loginRequest);
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;
        if(request.getMethod().equals(POST)) {
            super.doFilter(request, response, chain);
        } else {
            chain.doFilter(request, response);
        }
    }

Hate the sin, love the sinner
edytowany 2x, ostatnio: NoZi, 2015-06-24 14:47

Pozostało 580 znaków

2015-06-24 14:51

Rejestracja: 10 lat temu

Ostatnio: 5 godzin temu

0

Czyli działa to w taki sposób że za każdym responsem wysyłany jest nowy token?Jak można podpiąć bazę do sprawdzania loginu, hasła i tokena?Czy nie jest potrzebne sprawdzanie token w bazie - token jest zapisywany i pobierany tutaj:
SecurityContextHolder.getContext().setAuthentication(authentication)
?

Pozostało 580 znaków

2015-06-24 14:56

Rejestracja: 11 lat temu

Ostatnio: 1 dzień temu

0

Generalnie to działało bez zapisywania/odczytywania tokenu z bazy. Token był ważny przez jakiś czas, i sam w sobie zawierał informacje jak długo.
Generalnie chciałem Ci tylko pokazać, jak można zrobić to bez @autowired, bo nie jestem całkiem pewny czy jest możliwe wstrzykniecie tam czegokolwiek, ale moge się mylić.
Możesz stworzyc sobie filtr, który w konstruktorze będzie przyjmował serwis, który będzie pobierał dane z bazy.


Hate the sin, love the sinner
edytowany 1x, ostatnio: NoZi, 2015-06-24 14:59

Pozostało 580 znaków

2015-06-24 14:59

Rejestracja: 10 lat temu

Ostatnio: 5 godzin temu

0

Ok, a w którym miejscu dodać sprawdzanie emaila i hasła w bazie?Co robi ta metoda getAuthRequest bo jej nie ma.

nie ma znaczenia co robi, tam powinna być Twoja logika - NoZi 2015-06-24 15:00

Pozostało 580 znaków

2015-06-24 15:02

Rejestracja: 11 lat temu

Ostatnio: 1 dzień temu

1

możesz zaimplementować UserDetailsService i przekazać tego beana do AuthenticationManagerBuilder'a + np. do Filtra który ma za zadanie sprawdzić token dla usera


Hate the sin, love the sinner

Pozostało 580 znaków

Odpowiedz

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0