12 Aralık 2019 Perşembe

SpringSecurity @EnableGlobalMethodSecurity Anotasyonu - Kullanmayın

Giriş
Not : Spring 3 ile bu anotasyon yerine @EnableMethodSecurity anotasyonu geldi
Açıklaması şöyle. Method level security sağlar. Security için @Secured, @RoleAllowed, @PreAuthorize anotasyonları kullanılır
Spring Security ships with a number of nifty annotations that allow you to control access to methods. You can use @Secured, @RoleAllowed, and @PreAuthorize to name a few. To enable method-level security, you just need to add the following annotation to a configuration class.
Diğer Seçenekler
Bu açıklama Keycloak için ancak genel anlamda da geçerli. Açıklama şöyle
Rather than using @RolesAllowed annotation, the same configuration can be made in KeycloakSecurityConfig class as below.
Yani @RoleAllowed yerine şöyle yaparız. Bu yöntem daha kolay olabilir
@Override
protected void configure(HttpSecurity http) throws Exception {
  super.configure(http);
  http.authorizeRequests()
    .antMatchers("/test/anonymous").permitAll()
    .antMatchers("/test/user").hasAnyRole("user")
    .antMatchers("/test/admin").hasAnyRole("admin")
    .antMatchers("/test/all-user").hasAnyRole("user","admin")
    .anyRequest()
    .permitAll();
  http.csrf().disable();
}
Ayarlar
jsr250Enabled Alanı

Açıklaması şöyle.
The jsr250Enabled property allows us to use the @RoleAllowed annotation.
Örnek
Şöyle yaparız
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {...}
mode Alanı
AspectJ kullanmak için şöyle yaparız.
@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ, securedEnabled = true,
  prePostEnabled = true)
prePostEnabled Alanı
Açıklaması şöyle.
enables Spring Security’s @PreAuthorize and @PostAuthorize annotations
securedEnabled Alanı
Açıklaması şöyle.
controls the @Secured annotation
Unit Test
Örnek
Elimizde şöyle bir service olsun
@Service
public class ItemRetriever {
  List<Item> items = List.of(new Item(123, "item one"));

  @PreAuthorize("denyAll()")
  public void deny(int id) {
  }

  @PreAuthorize("hasRole('LIST_ITEMS')")
  public List<Item> getItems() {
    return items;
  }

  @PreAuthorize("hasPermission(#id, @itemPermission.getTarget(), @itemPermission.read())")
  public Optional<Item> getItem(int id) {
    return items.stream().filter((item) -> item.getId() == id).findFirst();
  }
}
Açıklaması şöyle
We’re using the PreAuthorize annotation to authorize the functions:

- deny will always throw an AccessDeniedException due to the use of denyAll. It’s included here for demonstration.
- getItems uses hasRole to check to see whether the user has the LIST_ITEMS role. If not, the function will also throw an AccessDeniedException.
- getItem uses hasPermission. This requires specifying a class that implements the PermissionEvaluator interface. An example of this is Spring Security’s AclPermissionEvaluator. We implement our own PermissionEvaluator. For the purpose of this project, we’ve defined PermissionEvaluatorProxy.
PermissionEvaluator  kullanan GlobalMethodSecurityConfiguration  şöyle olsun
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
  private PermissionEvaluatorProxy permissionEvaluator;
  private ApplicationContext applicationContext;

  @Autowired
  public MethodSecurityConfiguration(
      PermissionEvaluatorProxy permissionEvaluator, 
      ApplicationContext applicationContext) {
    this.permissionEvaluator = permissionEvaluator;
    this.applicationContext = applicationContext;
  }

  @Override
  protected MethodSecurityExpressionHandler createExpressionHandler() {
    DefaultMethodSecurityExpressionHandler expressionHandler =
        new DefaultMethodSecurityExpressionHandler();

    expressionHandler.setPermissionEvaluator(permissionEvaluator);
    expressionHandler.setApplicationContext(applicationContext);

    return expressionHandler;
  }
}
PermissionEvaluator şöyle bir kod olsun
@Bean
public class PermissionEvaluatorProxy implements PermissionEvaluator {

  @Override
  public boolean hasPermission(Authentication authentication, Object o, Object o1) {
    return true;
  }

  @Override
  public boolean hasPermission(Authentication authentication, 
    Serializable serializable, String s, Object o) {
    return true;
  }
}
Açıklaması şöyle
To use hasPermission , we need to configure the application to define an instance of PermissionEvaluator. We annotate the configuration class with EnableGlobalSecurity(prePostEnabled = true) to ensure that PreAuthorize and PostAuthorize annotations are invoked.
Buraya kadar normal kodu vardı. Test şöyledir. Tek yapılan mock bir PermissionEvaluator enjekte etmek. Normal yazıda test için farklı bir TestMethodSecurityConfiguration sınıfı da yaratılıyordu ama bence gerek yok.
@Import({TestMethodSecurityConfiguration.class})
@SpringBootTest(classes= {ItemRetriever.class, ItemPermission.class})
@WithMockUser("test")
class ItemRetrieverTest {

  @Autowired
  ItemRetriever itemRetriever;

  @MockBean
  PermissionEvaluator proxyTest;
  @BeforeEach()
  public void setup() {
    when(proxyTest.hasPermission(any(), any(),    any())).thenReturn(true);
    when(proxyTest.hasPermission(any(), any(), any(),  any())).thenReturn(true);
  } 
  @Test
  public void shouldThrowAccessDeniedException() {
    assertThrows(AccessDeniedException.class, () -> itemRetriever.deny(234));
  }
  @Test
  public void shouldThrowExceptionIfUserDoesNotHaveRole() {
    assertThrows(AccessDeniedException.class, () -> itemRetriever.getItems());
  }
  @Test
  @WithMockUser(roles = {"LIST_ITEMS"})
  public void shouldReturnItemsIfUserHasRole() {
    itemRetriever.getItems();
  }
  @Test
  public void shouldCallHasPermission() {
    itemRetriever.getItem(123);
    verify(proxyTest).hasPermission(any(),
      eq(123), eq(new ItemPermission().getTarget()), eq(new ItemPermission().read()));
  }
}





Hiç yorum yok:

Yorum Gönder