Basic Auth - Lekkie niezrozumienie

Basic Auth - Lekkie niezrozumienie
B9
  • Rejestracja:ponad 7 lat
  • Ostatnio:2 dni
  • Postów:65
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=?

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
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?

"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 1x, ostatnio: Shalom
B9
  • Rejestracja:ponad 7 lat
  • Ostatnio:2 dni
  • Postów:65
0
Kopiuj
    @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:

Kopiuj
@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?

edytowany 1x, ostatnio: brus97
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
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


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 1x, ostatnio: Shalom
B9
  • Rejestracja:ponad 7 lat
  • Ostatnio:2 dni
  • Postów:65
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:

Kopiuj
    @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.

edytowany 1x, ostatnio: brus97
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
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).


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 6x, ostatnio: Shalom
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
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:

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

Kopiuj
@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:

Kopiuj
@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.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 4x, ostatnio: Shalom
B9
  • Rejestracja:ponad 7 lat
  • Ostatnio:2 dni
  • Postów:65
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

Zobacz pozostałe 5 komentarzy
B9
Sorka, miałem źle skopiowany kod takie coś mi wywala: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'projectingArgumentResolverBeanPostProcessor' defined in class path resource [org/springframework/data/web/config/ProjectingArgumentResolverRegistrar.class] Znaczy logowanie jako takie jest, bo jak podawałem złe dane to nie przechodziło
Shalom
Pokaż cały log, a szczególnie KONIEC stack trace a nie jego początek ;)
B9
https://pastebin.com/mDkSD6nu wrzucam na paste bin bo dosc dlugi
Shalom
Ok to mały błąd z mojej strony w configu wyżej, ale nadal ten twój kod jest bez sensu bo z clienta strzelasz GETami a w security masz POSTa... No i jeszcze zrobiłeś to tak ze nie da sie łatwo odpalić od ręki bo jakieś mysqle... zaraz to poprawie, sek ;]
B9
Jak już będzie działać wszystko to zmienię też gety :P. Chodzi mi o samo potestowanie na /admin/register bo tam najłatwiej :d
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
1
Kopiuj
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();
    }
}
Kopiuj
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)
Kopiuj
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
Kopiuj
        <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.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 3x, ostatnio: Shalom
B9
Dzięki wielkie, a jeszcze jak odświeżę stronę będąc zalogowanym na koncie to tracę uprawnienia. Da się jakoś to zrobić od strony springa? Bo w reacie mam to tak rozwiązanie: localStorage.setItem("auth-token", this.createBasicAuthToken(username, password)) localStorage.setItem("jsession", document.cookie) A po refreshu przypisuje je znowu i śmiga. Jeszcze tylko na /logout dostaje to cors policy ale to coś wykombinuje
Shalom
Nie dla basic auth. Jak chcesz mieć jakieś session cookie czy JWT token to robi sie to inną konfiguracją w ogóle. Nie będę komentować wkładania credentiali do localstorage bo aż smutno patrzeć na taki gwałt na security. Nie ma czegoś takiego jak /logout po stronie backendu, bo nie ma to sensu żadnego. Po prostu w kliencie wywalasz ten header i tyle, nie musisz NIC robić z backendem.
B9
Oke, kolejna apka jak będę tworzył to popróbuje na OAuth2 albo jakiś inny system tokenów, bo widze że to basic auth to o kant d**y rozbić jest :p
Shalom
No basic to basic, zresztą nie jest taki zły, ale używa się go w konkretnych zastosowaniach i nie bardzo sie to jakoś ma do tego co ty próbujesz zrobić :D Ty byś chciał raczej coś w stylu https://www.baeldung.com/spring-security-remember-me że ktos sie loguje i dostaje session cookie i potem ono automatycznie lata razem z requestami.
B9
Najbardziej to by mnie zadowoliło takie coś jak na tej stronie jest i każdej innej xd Loguje się, trzyma mi sesję i klikne wyloguj to wylogowywuje. Nie mam dostępu do treści jak jestem niezalogowany itd. Sądzę że troche mi zajmie dojście do takich rzeczy
Shalom
@brus97: no można coś takiego zrobić, ale to wymaga trochę innej konfiguracji spring security, przykład masz w linku wyżej ;)
WeiXiao
Nie ma czegoś takiego jak /logout po stronie backendu, bo nie ma to sensu żadnego. Po prostu w kliencie wywalasz ten header i tyle, nie musisz NIC robić z backendem. mówisz o jwt?
Shalom
Mówię o tym co OP tutaj robił, czyli o Basic, ale z jwt jest to samo
WeiXiao
@Shalom: Przy zastosowaniu jwt możliwym jest i ma sens zrobienie opcji logout (server side), ba, jak inaczej stworzyłbyś multi-device logout aka wyloguj konto ze wszystkich urządzeń?
Shalom
Wszystko jest możliwe, ale wcale nie takie proste. Musiałbyś celowo wrzucić do tokena jakiś identyfikator a potem zrobić customowy filtr który odrzuca taki invalidowany token.
WeiXiao
@Shalom: albo po prostu mieć black listę tokenów i z poziomu np. middleware odrzucać takie requesty(jeżeli końcówka wymaga zalogowania), bo masz dostęp do headerów http. Jeszcze pytanie jak sensownie synchronizować tę black listę pomiędzy instancje aplikacji
Julian_
  • Rejestracja:około 8 lat
  • Ostatnio:ponad 4 lata
  • Postów:1703
0

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

WeiXiao
zacząłbym od poprawnego tłumaczenia authentication :P
Julian_
autentykuje się czyli poświadczam, że jestem autentycznym uzytkownikiem
Charles_Ray
  • Rejestracja:około 17 lat
  • Ostatnio:około 3 godziny
  • Postów:1873
2

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

Dokumentacja + rozpoznanie bojem.


”Engineering is easy. People are hard.” Bill Coughran
Julian_
  • Rejestracja:około 8 lat
  • Ostatnio:ponad 4 lata
  • Postów:1703
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?

Julian_
nie ma nic o trustorach, keystorach, SSL, kluczach, NTLM, Basic, tokenach, szyfrowaniu itd.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)