пятница, 24 июня 2016 г.

Spring Security. Простая конфигурация

При разработке вэб приложения рано или поздно необходимо решать задачу безопасности. В этом вопросе Spring Security предоставляет комплексные решения безопасности, аутентификации и авторизации. Построенный на основе Spring Framework, Spring Security имеет все преимущества Dependecy Injection и AOP

Конфигурация Spring Security начинается со следующих моментов:


  package spitter.config;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.security.config.annotation.web.
  configuration.WebSecurityConfigurerAdapter;
  import org.springframework.security.config.annotation.web.servlet.
  configuration.EnableWebMvcSecurity;

  @Configuration//1
  @EnableWebMvcSecurity//2
  public class SecurityConfig 
   extends WebSecurityConfigurerAdapter { //3
  }

Важно обратить внимание на

  • 1 - обозначить наш класс как спринг конфигурацию
  • 2 - аннотация @EnableWebMvcSecurity, которая "включает" Spring Security
  • 3 - @EnableWebMvcSecurity не будет работать, если наш класс не наследует WebSecurityConfigurerAdapter или WebSecurityConfigurerAdapter как в нашем примере

Сама конфигурация происходит через переопределение одного из методов configure:

  • configure(WebSecurity)
  • configure(HttpSecurity)
  • configure(AuthenticationManagerBuilder)

Вот пример самой простой конфигурации (через http basic):


  @Override
  protected void configure(HttpSecurity http) throws Exception {
   http
    .authorizeRequests()
     .anyRequest().authenticated()
     .and()
    .httpBasic();
  }

Этот пример нам говорит о том, что все http запросы к нашему приложению должны быть аутентифицированы(authorizeRequests().anyRequest().authenticated()) и логин происходит через преконфигуренную логин форму и логин страницу(formLogin()).

Следующий шаг конфигурации - это подготовка хранилища юзеров для аутентификации, специализировать моменты, где нужна аутентификация, а где - нет, предоставить свою логин пейджу

Самый простой способо воспользоваться "in-memory" хранищем:


      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.inMemoryAuthentication()
                  .withUser("user").password("password").roles("USER").and()
                  .withUser("admin").password("password").roles("USER", "ADMIN");

      }

Можно провести аутентификацию через базу данных:


      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.jdbcAuthentication().dataSource(dataSource)
                  .usersByUsernameQuery(
                          "select name, password_hash, true from users where name=?")
                  .authoritiesByUsernameQuery(
                          "SELECT name, id"
                                  + "  FROM roles where id in "
                                  + "(SELECT role_id FROM jt_roles where user_id in "
                                  + "(SELECT id FROM users where name = ?) )")
                  .passwordEncoder(new BCryptPasswordEncoder());
      }

Конфигурем логин пейджу


 registry.addViewController("/login").setViewName("admin/login");

Eсли у нашего приложения есть админка и мы хотим обезопасить доступ только к этой админке(1, путь - /admin/**) и наша собственная логин страница(2) :


      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http
                  .authorizeRequests()
                  .antMatchers("/admin/**").authenticated()//1
                      .and()
                  .formLogin().loginPage("/login").failureUrl("/login?error"); //2
      }

Если кроме юзеров и ролей вам нужно еще выбрать пермишены и у вас более сложная настройко, то удобно делать конфигурацию через UserDetailsService. Для этого нам нужно реализовать интрефейс с одним методом, который должен вернуть объект типа UserDetails:


public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

В моем примере это выглядит так:


@Component("userDetailsService")
public class PermissionCentricUserDetailsService implements UserDetailsService {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(final String login) {
        log.debug("Authenticating {}", login);
        String lcLogin = login.toLowerCase();
        Optional dbUser = userRepository.findOneByName(lcLogin);
        return dbUser
            .map(u -> {
                List grantedRoles = u.getRoles().stream()
                    .map(role -> new SimpleGrantedAuthority(role.getName()))
                    .collect(Collectors.toList());
                return new PermissionCentricUserDetails(lcLogin, u.getId(), u.getPasswordHash(),
                                                        grantedRoles, u.getRoles());
            })
            .orElseThrow(() -> new UsernameNotFoundException("No such user: " + lcLogin));
    }
}

....

public class PermissionCentricUserDetails extends User {
...
}
//User реализует интерфейс UserDetails

Это все базовые моменты, которые касаются конфигурацией на уровне web. Дальше рассмотрим секьюрити на уровне методов. Базовые настройки:


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)//1
@ComponentScan(basePackages = {"com.getman.security"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}

Мы исользуем аннотацию(1) @EnableGlobalMethodSecurity чтобы секьюрити через методы стало доступным. Указываем какого именно типа аннотоция должна стать доступна. В данном случае @PreAuthorize. Можно использовать следуюшие типы аннотаций:

  • @Secured - базовая спринговая аннотация, если мы просим доступ к аннотированному методу, но проходим аутентификацию, получаем - исключение Spring Security
  • @RolesAllowed - то же самое, что и @Secured, только стандартная java аннотация(JSR-250)
  • @PreAuthorize, @PostAuthorize - выбросим исключение, если не пройдем авторизацию, но в первом случае метод не будет вызван, во втором будет(на основе выражения, переданного аннотации)
  • @PostFilter, @PreFilter - в первом случае фильтруем результат, во втором - входные параметры на основе выражения, которое передаем аннотации

Имеем 4 аннотации, которые принимают SpEL(Spring Expression language) выражения - @PreAuthorize, @PostAuthorize, @PostFilter, @PreFilter. В моем примере можно увидеть:


    @PreAuthorize("@securityUtils.hasPermission('VIEW_CUSTOMERS')")
    @RequestMapping(value = "/customerRecord",method = RequestMethod.GET)
    public String viewCustomerRecord(Model model, @RequestParam Optional id) {
        if (id.isPresent()) {
            customer = customerRepository.findOne(id.get());
            Iterable orderByCustomer =
                    customerOrderRepository.findByCustomer(customer);
            Iterator iterator = orderByCustomer.iterator();
            order = iterator.hasNext() ? iterator.next() : null;
        }
        model.addAttribute("customerRecord", customer);
        model.addAttribute("order", order);
        return "admin/index";
    }

    ...

@Component
public final class SecurityUtils {

 ...

    /**
     * Checks currently authenticated user for having specified {@link Permission} among roles.
     *
     * @param permission toString() representation of {@link Permission}
     * @return true if the specified permission is found in any of the user's roles, false otherwise.
     */
    public static boolean hasPermission(String permission) {
        return hasPermission(Permission.valueOf(permission));
    }

    /**
     * Checks currently authenticated user for having specified {@link Permission} among roles.
     *
     * @param permission {@link Permission} instance.
     * @return true if the specified permission is found in any of the user's roles, false otherwise.
     */
    public static boolean hasPermission(Permission permission) {

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (permission != null && authentication != null) {
            Object principal = authentication.getPrincipal();
            if (principal instanceof PermissionCentricUserDetails) {
                return
                        ((PermissionCentricUserDetails) principal)
                                .getPermissions()
                                .contains(permission);
            }
        }
        return false;
    }
}    

На мой взгяд это удобно для рест сервиса, а для обычного веб приложения хватит обычное web аутентификация. В целом фреймворк очень удобный как с точки зрения быстрой настройки секьюрити, так и с точки зрения более продвинутых фич. Вот мой код с двумя модулями: initial (без секьюрити) и base-security(защищенное)

4 комментария:

  1. WebSecurityConfigurerAdapter или WebSecurityConfigurerAdapter

    о_О

    ОтветитьУдалить
  2. @Override
    protected void configure(AuthenticationManagerBuilder auth)
    throws Exception {
    auth.inMemoryAuthentication()

    .withUser("user")
    .password("password").
    roles("USER")
    .and()
    .withUser("admin")
    .password("password")
    .roles("USER", "ADMIN");
    }

    Вот это счастье не работает (Spring Boot)

    можете сказать возможную причину

    ОтветитьУдалить
  3. Консоли от корпорации Microsoft не сразу захватили всемирную известность и доверие игроков. Первая консоль под названием Xbox, вышедшая в далеком 2001 году, существенно уступала PlayStation 2 по количеству проданных приставок. Однако все изменилось с выходом Xbox 360 - консоли седьмого поколения, которая стала по-настоящему "народной" для жителей России и стран СНГ - Программы для FreeBoot. Сайт Ru-Xbox.Ru является популярным ресурсом в числе поклонников приставки, поскольку он предлагает игры для Xbox 360, которые поддерживают все имеющиеся версии прошивок - совершенно бесплатно! Зачем играть на оригинальном железе, если есть эмуляторы? Для Xbox 360 игры выходили длительное время и находятся как посредственными проектами, так и хитами, многие из которых даже сегодня остаются эксклюзивными для это консоли. Некие пользователи, желающие сыграть в игры для Xbox 360, могут задать вопрос: для чего нужны игры для прошитых Xbox 360 freeboot либо разными версиями LT, если есть эмулятор? Рабочий эмулятор Xbox 360 хоть и существует, но он требует производительного ПК, для покупки которого потребуется вложить существенную сумму. К тому же, различные артефакты в виде исчезающих текстур, отсутствия некоторых графических эффектов и освещения - смогут значительно испортить впечатления об игре и отбить желание для ее дальнейшего прохождения. Что предлагает этот сайт? Наш интернет-сайт вполне приурочен к играм для приставки Xbox 360. У нас вы можете совершенно бесплатно и без регистрации скачать игры на Xbox 360 через торрент для следующих версий прошивок консоли: - FreeBoot; - LT 3.0; - LT 2.0; - LT 1.9. Каждая прошивка имеет свои особенности обхода встроенной защиты. Потому, для запуска той либо иной игры будет нужно скачать специальную ее версию, которая вполне приспособлена под одну из 4 перечисленных выше прошивок. На нашем интернет-сайте вы можете без усилий подобрать желаемый проект под подходящую прошивку, так как возле каждой игры находится название версии (FreeBoot, LT 3.0/2.0/1.9), под которую она адаптирована. Пользователям данного ресурса доступна особая категория игр для 360-го, созданных для Kinect - специального дополнения, которое считывает все движения одного или нескольких игроков, и позволяет управлять с помощью их компьютерными персонажами. Большой выбор ПО Кроме способности загрузить игры на Xbox 360 Freeboot или LT различных версий, здесь можно найти программное обеспечение для консоли от Майкрософт: - всевозможные версии Dashboard, которые позволяют кастомизировать интерфейс консоли под свои нужды, сделав его более комфортным и нынешним; - браузеры; - просмотрщики файлов; - сохранения для игр; - темы для консоли; - программы, для конвертации образов и записи их на диск. Кроме вышеперечисленного игры на Xbox 360 Freeboot можно запускать не с дисковых, а с USB и прочих носителей, используя программу x360key, которую можно достать на нашем интернет-сайте. Посетителям доступно множество полезных статей, а также форум, где вы можете пообщаться с единомышленниками либо попросить совета у более опытнейших хозяев консоли.

    ОтветитьУдалить