Реализация Spring Security для отдыха Api - PullRequest
0 голосов
/ 01 сентября 2018

Я использую этот код для аутентификации Rest API:

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        Optional<String> basicToken = Optional.ofNullable(request.getHeader(HttpHeaders.AUTHORIZATION))
                .filter(v -> v.startsWith("Basic"))
                .map(v -> v.split("\\s+")).filter(a -> a.length == 2).map(a -> a[1]);
        if (!basicToken.isPresent()) {
            return sendAuthError(response);
        }

        byte[] bytes = Base64Utils.decodeFromString(basicToken.get());
        String namePassword = new String(bytes, StandardCharsets.UTF_8);
        int i = namePassword.indexOf(':');
        if (i < 0) {
            return sendAuthError(response);
        }
        String name = namePassword.substring(0, i);
        String password = namePassword.substring(i + 1);
//        Optional<String> clientId = authenticationService.authenticate(name, password, request.getRemoteAddr());
        Merchants merchant = authenticationService.authenticateMerchant(name, password, request.getRemoteAddr());
        if (merchant == null) {
            return sendAuthError(response);
        }
        request.setAttribute(CURRENT_CLIENT_ID_ATTRIBUTE, merchant.getId());
        return true;
    }

Как я могу переписать код с помощью Spring Security, чтобы получить тот же результат, но для разных ссылок для аутентификации? Например:

localhost:8080/v1/notification - requests should NOT be authenticated.
localhost:8080/v1/request - requests should be authenticated.

Ответы [ 2 ]

0 голосов
/ 04 сентября 2018

Вы можете использовать стандартную конфигурацию Spring-Security, описанную на различных веб-сайтах, например baeldung.com или mkyong.com . Уловка в вашем образце, похоже, заключается в том, чтобы получить Merchant. В зависимости от сложности объектов authenticationService и Merchant вы можете использовать следующий код или реализовать фасад для получения аналогичного поведения.

@Autowired
public void authenticationManager(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(new AuthenticationProvider() {
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            Merchants merchant = authenticationService.authenticateMerchant(name, password, request.getRemoteAddr());
            if(merchant == null) {
                throw new AuthenticationException("No Merchant found.");
            }
            return new UsernamePasswordAuthenticationToken(name, password, merchant.getAuthorities());
        }

        @Override
        public boolean supports(Class<?> authentication) {
            return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
        }
    });
}

Установка атрибута в запросе, при необходимости может быть выполнена отдельным фильтром, который берет Principal из SecurityContext и помещает его в запрос в качестве атрибута.

0 голосов
/ 04 сентября 2018

Здесь вы можете найти рабочий проект https://github.com/angeloimm/springbasicauth

Я знаю, что в файле pom.xml много бесполезных зависимостей, но я начал с уже существующего проекта, и у меня не было времени его удалить

В основном вы должны:

  • настройка безопасности пружины
  • Настроить пружину MVC
  • реализует ваш собственный провайдер аутентификации в соответствии с Spring Security. Обратите внимание, что я использовал inMemoryAuthentication. Пожалуйста, измените его в соответствии с вашими пожеланиями

Позвольте мне объяснить код.

Конфигурация Spring MVC :

@Configuration
@EnableWebMvc
@ComponentScan(basePackages= {"it.olegna.test.basic"})
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
        converters.add(new MappingJackson2HttpMessageConverter());
    }
}

Здесь мы не делаем ничего другого, кроме настройки Spring MVC, сообщая ему, где найти контроллеры и т. Д., И используем конвертер одного сообщения; MappingJackson2HttpMessageConverter для получения ответов JSON

Spring Security Configuration :

@Configuration
@EnableWebSecurity
@Import(value= {WebMvcConfig.class})
public class WebSecConfig extends WebSecurityConfigurerAdapter {
     @Autowired private RestAuthEntryPoint authenticationEntryPoint;

        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth
              .inMemoryAuthentication()
              .withUser("test")
              .password(passwordEncoder().encode("testpwd"))
              .authorities("ROLE_USER");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
              .authorizeRequests()
              .antMatchers("/securityNone")
              .permitAll()
              .anyRequest()
              .authenticated()
              .and()
              .httpBasic()
              .authenticationEntryPoint(authenticationEntryPoint);
        }
        @Bean
        public PasswordEncoder passwordEncoder() {
            return NoOpPasswordEncoder.getInstance();
        }
}

Здесь мы настраиваем Spring Security для использования базовой аутентификации HTTP для всех запросов, кроме тех, которые начинаются с securityNone. Мы используем NoOpPasswordEncoder для кодирования предоставленного пароля; этот PasswrodEncoder абсолютно ничего не делает ... он оставляет passwrod без изменений.

RestEntryPoint

@Component
public class RestAuthEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,  AuthenticationException authException) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
}

Эта точка входа отключает все запросы, не связанные с Аутентификация заголовок

SimpleDto : очень простой DTO, представляющий JSON-ответ от контроллера

public class SimpleDto implements Serializable {

    private static final long serialVersionUID = 1616554176392794288L;
    private String simpleDtoName;

    public SimpleDto() {
        super();
    }
    public SimpleDto(String simpleDtoName) {
        super();
        this.simpleDtoName = simpleDtoName;
    }
    public String getSimpleDtoName() {
        return simpleDtoName;
    }
    public void setSimpleDtoName(String simpleDtoName) {
        this.simpleDtoName = simpleDtoName;
    }

}

TestBasicController : очень простой контроллер

@RestController
@RequestMapping(value= {"/rest"})
public class TestBasicController {
    @RequestMapping(value= {"/simple"}, method= {RequestMethod.GET}, produces= {MediaType.APPLICATION_JSON_UTF8_VALUE})
    public ResponseEntity<List<SimpleDto>> getSimpleAnswer()
    {
        List<SimpleDto> payload = new ArrayList<>();
        for(int i= 0; i < 5; i++)
        {
            payload.add(new SimpleDto(UUID.randomUUID().toString()));
        }
        return ResponseEntity.ok().body(payload);
    }
}

Так что если вы попробуете этот проект с помощью почтальона или любого другого тестера, у вас может быть 2 сценария:

  • требуется аутентификация
  • все ок

Предположим, вы хотите вызвать URL http://localhost:8080/test_basic/rest/simple без передачи заголовка аутентификации. Код статуса HTTP будет 401 Unauthorized

Это означает, что требуется заголовок аутентификации

Добавив этот заголовок к запросу Authorization Basic dGVzdDp0ZXN0cHdk все работает довольно хорошо Обратите внимание, что String dGVzdDp0ZXN0cHdk - это кодировка Base64 строки username:password; в нашем случае это кодировка Base64 test:testpwd, определенная в inMemoryAuthentication

Надеюсь, это полезно

Angelo

ОБСЛУЖИВАНИЕ ДАННЫХ ПОЛЬЗОВАТЕЛЯ ВЕБ-БЕЗОПАСНОСТИ

Чтобы настроить Spring Security для получения сведений о пользователе из БД, вы должны сделать следующее:

создайте реализацию org.springframework.security.core.userdetails.UserDetailsService следующим образом:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private BasicService svc;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        BasicUser result = svc.findByUsername(username);
        if( result == null )
        {
            throw new UsernameNotFoundException("No user found with username "+username);
        }
        return result;
    }

}

Вставьте его в конфигурацию безопасности Spring и используйте его так:

public class WebSecConfig extends WebSecurityConfigurerAdapter {
    @Autowired private RestAuthEntryPoint authenticationEntryPoint;
    @Autowired
    UserDetailsService userDetailsService;
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//      auth
//      .inMemoryAuthentication()
//      .withUser("test")
//      .password(passwordEncoder().encode("testpwd"))
//      .authorities("ROLE_USER");
        auth.userDetailsService(userDetailsService);
        auth.authenticationProvider(authenticationProvider());
    }
    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
        .antMatchers("/securityNone")
        .permitAll()
        .anyRequest()
        .authenticated()
        .and()
        .httpBasic()
        .authenticationEntryPoint(authenticationEntryPoint);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}

Я нажал код на предоставленной мной ссылке на github. Там вы можете найти полный рабочий пример, основанный на:

  • пружина 5
  • пружина безопасности 5
  • спящий режим
  • h2 DB

Не стесняйтесь адаптировать его к вашему собственному сценарию

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...