Spring Boot Security - konfiguracja z pliku

0

Backend napisany w mikroserwisach (spring boot cloud). W mikroserwisie Gateway który jest odpowiedzialny za api routing i sprawdzanie czy odpytujący ma odpowiednie uprawnienia mam obecnie zahardkodowana testową konfiguracje:

@Override
	public void configure(WebSecurity webSecurity) {
		webSecurity.ignoring()
		.antMatchers("/user-service/customer/login/*")
		.antMatchers("/user-service/user/login");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
		.antMatchers("/info").hasAuthority("ADMIN")
		//.antMatchers("/user-service/login").hasAuthority("ADMIN") 
		.antMatchers("/test3").hasRole("ADMIN")
		.and()
		.addFilter(new JwtFilter(authenticationManager()))
		.csrf().disable();
	}

W jaki sposób przenieść to do pliku? w jakiej lokalizacji taki plik powinien być?

2

Chcesz przenieść cała konfigurację do application.properties?

1

Do głowy mi przychodzi:

@Value("${path.to.cośtam}")

Ale to i tak tylko przeniesienie stringów.
Możesz też zrobić różne klasy konfiguracyjne i nadać im adnotacje @profile tak żeby odpalało się to co chcesz.

1

Kiedyś robiłem coś takiego, w application.yml

security:
  unprotectedEndpoints: /api/auth/**, /swagger-ui/**, /swagger-resources/**, /v2/api-docs

I w samym SecurityConfiguration

    private final String[] unprotectedEndpoints;

    public SecurityConfiguration(@Value("${security.unprotectedEndpoints}") String unprotectedEndpoints) {
        this.unprotectedEndpoints = unprotectedEndpoints;
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers(unprotectedEndpoints);
    }

No ale z hasRole() nigdy nie widziałem takiej konfiguracji w pliku

4

Nic nie musisz robić. Już jest w pliku.

0

@Aleksander32: Tak do pliku .xml lub .properties.

Druga kwestia czy to dobra praktyka tego typu rzeczy miec w konfgiu

2

Nie jest, chyba że lubisz dreszcz emocji pt. "Ciekawe czy ktoś zhakuje API bo ktoś zapomniał endpointu w produkcyjnej konfiguracji"

1

Moim zdaniem źle do tego podchodzisz jeśli chcesz to zrobić TYLKO tutaj. Bo przecież to musi być spójne z kontrolerami i pewnie też z programmatic clientem w tym serwisie, więc wszystkie te wartości powinny pochodzić z jednego miejsca! Bo co ci da ze tutaj masz hardkodowanego stringa /api1 kiedy w kontrolerze ktoś zmieni na /api2 a w kliencie ktoś przypadkiem da /api3? Tylko że nie bardzo masz jak taką wartość wstrzyknąć z konfiga, szczególnie w przypadku tego klienta, który przecież może być wciagnięty jako zależność w zupełnie innym projekcie. W efekcie moim zdaniem jedyna dobra opcja to:

  1. Robisz public static String w kliencie, gdzie hardkodujesz te stringi odpowiedzialne za endpointy
  2. W kontrolerze importujesz sobie te stringi i definiujesz endpointy
  3. W security konfig importujesz te stringi i definiujesz security rules

Dzięki temu w kodzie każdy z tych stringów występuje tylko jeden raz.

Shameless plug ale:

0

Da się ale wtedy nie korzystasz z magii typu Spring security :) a implementujesz to co ci potrzebne sam

0

W takim razie czy w ogóle dobrym podejściem jest sprawdzanie autentykacji i autoryzacji na poziomie api gateway czy czasem nie lepiej ta logike przenieść do każdego z serwisów i wtedy każdy z nich sprawdzałby uprawnienia tylko dla swoich endpointow? Chciałem uniknąć walidacji na każdym z serwisów poniżej gateway'a i wtedy api gateway by odrzucał już na wstępie te odpytania które nie maja tokena czy tez uprawnień do odpytywanego endpointa. (wyciagajac adresy do jakeis klasy statycznej jak podsunął pomysł Shalom byłby w tym wypadku dobrym pomysłem)

2

Nawet jak to będzie wyciągnięte do jakiejś klasy jako static, musisz w jakiś sposób zapewnić, że zmiany zostaną wszędzie zaciągnięte w postaci wdrożenia z nową wersją biblioteki. Więc IMHO to o czym pisze Shalom albo można zapewnić zrzucając odpowiedzialność za rejestrację route na zespół opiekujący się danym serwisem, albo jakiś mechanizm automatycznej aktualizacji routy w api-gateway.

Odnośnie sprawdzania uprawnień, skalowalnym podejściem jest autoryzacja na poziomie określonej domeny, której dotyczą te uprawnienia. Inaczej w api-gateway zrobi się śmietnik.

1

Moim zdaniem dobrą praktyką jest kontrola uprawnień jak najbliżej kodu wykonującego opreację lub danych (przydaje się w dużych projektach). Jeżeli będziesz sprawdzać uprawnienia na poziomie api gateway, to zawsze ktoś się może wstrzelić z boku. Często też wersje samych serwisów mogą sie rozjechać i nagle API jest dostępne dla wszystkich po niefortunnym wdrożeniu.

Jeśli chodzi o sprawdzanie uprawnień użytkownika to polecam użyć @EnableGlobalMethodSecurity(prePostEnabled=true) i pisać kod tak:

@Controller
public UserManagementController {

  @RequestMapping(method = RequestMethod.GET)
  @PreAuthorize("hasAuthority('CAN_LIST_USERS')")
  public List<RestUser> listUsers() {
    // ...
  }

  @RequestMapping(method = RequestMethod.POST)
  @PreAuthorize("hasAuthority('CAN_SUSPEND_USER')")
  public void suspendUser(@RequestParam UUID userId) {
    // ...
  }

}

Dodatkowo polecam używać ról jako kontenerów na uprawnienia (authorities). I w samym kodzie sprawdzać authorities. Przykładowo:

  • ROLE_USER_MANAGEMENT zawiera
  • CAN_LIST_USERS
  • CAN_SUSPEND_USER
  • CAN_CHANGE_USER_ROLES
  • etc.

Dzięki temu zyskujesz elastyczność. Jeżeli trzeba dodać nową rolę to nie trzeba przerabiać wszystkich aplikacji, tylko definiujesz nową rolę i przypisane do niej uprawnienia.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.