Basic Auth - Lekkie niezrozumienie

1

Hej,

Wszędzie czytam że autoryzacja ta wymaga wysłania headera Authorization:"Basic username:password" oczywiście dane mają być base64. W spring boocie mam .http.basic() itd.
Wysyłam sobie posta z takim headerem przy pomocy reacta na /login którego ustawiłem że zwraca principal. Dostaje 403 mimo że użytkownik jest w bazie. Teraz (dla mnie niezrozumiałego), wysyłam posta w json {"username":"xxx", "password":"xxx"} na /login, dostaje code 20x, a następnie dopiero ustawiam dla każdego requesta nagłówek z authorization z 'basic...' i już latam jako zalogowany.

O co chodzi i czy również dla tych samych danych zawsze powinien być taki sam token, załóżmy dla admin:admin zawsze dostaje Basic YWRtaW46YWRtaW4=?

3

Ok faktycznie niewiele rozumiesz :)

  1. YWRtaW46YWRtaW4= to jest admin:admin enkodowane jako base64. To NIE JEST żaden token tylko username i hasło! Po prostu! Dlatego zawsze wygląda tak samo.
  2. W spring boocie mam .http.basic() itd. nie wiedząc jak wygląda twoje security config trudno cokolwiek powiedzieć, tak samo nie wiedząc jak wyglądają twoje kontrolery. Generalnie ten twój POST raczej niczego nie robi bo i co miałby niby robić? Ale trudno powiedzieć póki nie pokażesz kontrolera który łapie to /login, może on tworzy jakiś wpis w bazie danych?
0
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .cors().and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/admin/**").hasAuthority(ADMIN)
                .anyRequest().permitAll()
                .and().httpBasic()
                .and().logout();
    }

A kontroler jest najprostszy możliwy:

@PostMapping(path = "/login")
    public Principal authenticate(Principal principal) {
       return principal
    }

Tak tak, pomyliłem się otrzymuję token np. "XSRF-TOKEN=6d6de0ad-5a12-41e1-957c-461099e82f33". Który jest w ciastku.

Głównie pytanie rozchodzi się własnie o to, czemu muszę wysłać najpierw posta w zasadzie do pustego kontrolera i dopiero wtedy wysylac requesty juz z headerem. Ten post do kontrolera to jest po to żeby dostać token?

0

Ten twój post do controllera nie ma żadnego znaczenia, nie musisz go wysyłać z tego co widzę, on nic nie robi, nawet nie patrzy na te wysłane dane. Ba, on w ogóle zawsze zwróci ci nulla bo w ogóle nie obejmuje go security config. Skąd te bzdury skopiowałes? :D

0

Dzięki wielkie to wiele wyjaśniło. Że tak powiem złe zrozumienie tematu z mojej strony.

Ten controller nie wzraca nulla. Jeżeli zapisałbym go tak:

    @PostMapping(path = "/login")
    public List<String> authenticate(Authentication authentication) {
        List <String> roleList = new ArrayList<>();
        for (GrantedAuthority authority : authentication.getAuthorities()) {
            roleList.add(authority.toString());
        }
        System.out.println(roleList);
        return roleList;

    }

To otrzymam listę ról przypisanych do użytkownika. Teraz jednak też zauważyłem że mogę odczytać bezpośrednio do consol.loga przy pomocy .then po requeście od strony reacta. Oczywiście część od strony reacta skopiowałęm z tutoriali, w dużym stopniu stąd te niejasności + jak próbowałem poprzerabiać ten kod to własnie natknąłem się na tą bezsensowną rzecz, aż musiałem zapytać.

Jeszcze mam jedno pytanie. Po odświeżeniu strony tracę ten token i nie mam uprawnień do api. Czy jeżeli zapisuję go sobie w local storage po zalogowaniu i podczas odświeżania strony sprawdzam czy użytkownik dalej jest zalogowany po stronie reacta i przywracam token do ciastek to jest to dobre rozwiązanie? Gdzieś wyczytałem że sprawdza się.

@Edit

Tylko dalej zastanawia mnie to czemu jak wyśle zwykłego posta json z username i password to otrzymam tą role, mimo że chyba nie powinienem.

0

Ten controller nie wzraca nulla.

O RLY? To znaczy ze masz tam @Controller("/admin") czyż nie? I wchodzisz na endpoint /admin/login a nie na /login. Albo masz jeszcze jakiś inny security config, albo do tego twojego POSTa też dodajesz basic auth header, innej opcji nie ma. Pokaż jakiegoś githuba z CAŁYM KODEM, bo to co piszesz nijak się ma do kodu który pokazujesz.

XSRF/CSRF token to jest zupełnie coś innego i NIE MA NIC wspólnego z logowaniem gdziekolwiek. To jest jednorazowy token związany Cross-Origin requests i zresztą z konfiguracji wynika że w ogóle go olewasz (ale pewności nie ma, bo znów pewnie pokazałeś tylko kawałek konfiguracji i nadal nic nie wiadomo).

1

https://github.com/PKondzierski/4prog

Sorki za rozgrzebanie w projekcie, jest to mój pierwszy większy i staram się jak mogę. AuthentiactionService.js i login.js to są pliki gdzie obsługuje logowanie. Komentarzami wyrzuciłem jak było przez naszą rozmową, w pakiecie security jest reszta javy.

Z tym corsem miałem dużo problemów gdyż nic nie przepuszczało i znalazłem takie rozwiązanie. Nie mam jeszcze za bardzo doświadczenia w springu, ale robie co mogę ;d

Masz tam jakieś bardzo dziwne rzeczy. Powinno być coś w stylu:

            http
                    .cors()
                    .and()
                    .csrf()
                    .disable()
                    .authorizeRequests()
                    .antMatchers(HttpMethod.POST, "/admin/**").hasAuthority(ADMIN)
                    .anyRequest().permitAll();

Ten cały logout nie bardzo ma sens dla basic auth i dziwi mnie trochę że /admin ma tylko POSTowe endpointy, bo ja myśle że będzie miał też GETowe, wiec w ogóle wyrzuciłbym to z tego matchera.
Reszta wygląda w miare ok tak na oko, nie chce mi się teraz tego odpalać zeby się upewnić.

Ale ja bym dla czytelnosci zrobił jeszcze inaczej:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Configuration
    @Order(1)
    public static class BasicSecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .cors()
                    .and()
                    .csrf()
                    .disable()
                    .antMatcher("admin/**")
                    .httpBasic()
                    .and()
                    .authorizeRequests()
                    .antMatchers(HttpMethod.POST, "/admin/**").hasAuthority(ADMIN);
        }
    }

    @Configuration
    @Order(10)
    public static class NonAuthenticatedPaths extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .cors()
                    .and()
                    .csrf()
                    .disable()
                    .authorizeRequests()
                    .antMatchers("/**")
                    .permitAll();
        }
    }
}

Jeśli chodzi o cors to ja zwykle robie:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowCredentials(true);
    }
}

Ale bardzo możliwe że ta twoja metoda też działa.

Moja rada: NIE rób wszędzie tych @Service bo bardzo ciezko coś w takim projekcie znaleźć, szczególnie bez użycia IntelliJ. Zrób sobie klasę @Configuration gdzie poskładasz sobie obiekty i ewentualnie podeklarujesz jakies @Bean. Dzięki temu od razu wiadomo co, gdzie i skąd się wzięło.

0

Wiesz, to wszystko jeszcze jest w do poukładania bo ciągle testuję to spring security. Mimo wszystko będę musiał to jeszcze posprawdzać, bo nie do końca mi się wszystko zgadza jak to działa. Dzięki za pomoc i naprowadzenie na trop ;d

1
package pl.l3.bufet.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

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

    public static final String ADMIN = "ROLE_ADMIN";

    @Configuration
    @Order(1)
    public static class BasicSecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .cors()
                    .and()
                    .csrf()
                    .disable()
                    .antMatcher("/admin/**")
                    .httpBasic()
                    .and()
                    .authorizeRequests()
                    .antMatchers("/admin/**").hasAuthority(ADMIN);
        }
    }

    @Configuration
    @Order(10)
    public static class NonAuthenticatedPaths extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .cors()
                    .and()
                    .csrf()
                    .disable()
                    .authorizeRequests()
                    .antMatchers("/**")
                    .permitAll();
        }
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}
package pl.l3.bufet;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowCredentials(true);
    }
}
  1. (to tylko zeby ludzie mogli zachować sanity próbując to odpalić z miejsca)
spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
spring.datasource.url=jdbc:hsqldb:mem:bufet
spring.datasource.username=SA
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.initialization-mode=always
        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
        </dependency>
  1. W data sql nie używaj " bo to niestandardowe, używaj '

Przetestowałem to na szybko i wygląda że działa tak jak powinno bez żadnych cyrków z jakimś /login. Po prostu ustawiasz header i tyle. W kontrolerach które są w ścieżce /admin możesz dodać argument Principal principal i zobaczysz ze dostajesz tam swojego admina faktycznie, jeśli header jest. Jeśli headera nie ma to leci 401.
Tego twojego klienta nie sprawdziłem bo wbrew readme wcale nie nie startuje po nmp start.

0

Jakie książki/materiały polecicie by się nauczyć tego typu zagadanień (autentykacja przy usługach SOAP/REST) od postaw?

2

Jakie książki/materiały polecicie by się nauczyć tego typu zagadanień (autentykacja przy usługach SOAP/REST) od postaw?

Dokumentacja + rozpoznanie bojem.

0
Charles_Ray napisał(a):

Jakie książki/materiały polecicie by się nauczyć tego typu zagadanień (autentykacja przy usługach SOAP/REST) od postaw?

Dokumentacja + rozpoznanie bojem.

Która dokumentacja?

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