Spring Security, REST Api и DataSource

167
31 июля 2018, 23:20

пишу REST Api сервер, хочу добавить авторизацию. Для теста добавил следующие ресурсы

  1. GET /api/test/getting, который возвращает privet всем без авторизации
  2. GET /api/test/secret, который возвращает SECREEEET только авторизованым пользователем.

Сейчас Spring Security настраивается следующим образом:

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery(
                        "select username,password, enabled from users where username=?")
                .authoritiesByUsernameQuery(
                        "select username, role from user_roles where username=?")
                .passwordEncoder(new BCryptPasswordEncoder());
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/test/getting").permitAll()
                .antMatchers("/api/**").authenticated()
                .and()
                .httpBasic()
                .authenticationEntryPoint(new Http401UnauthorizedEntryPoint())
                .and()
                .formLogin()
                .usernameParameter("username").passwordParameter("password")
                .and()
                .logout().logoutSuccessUrl("/login?logout")
                .and()
                .exceptionHandling();
    }

Через браузер и curl, первый ресурс выдает всем privet. А при попытке обратится ко второму через браузер перекидывает на форму логина, а потом отправляет POST /login с параметрами username=XXX&password=XXX&submit=Login, после чего в запросы добавляются куки. Я попробовал повторить такое поведение через curl с сохранением куки в файл, но меня сразу выкидывает с ошибкой 403.

Хочу узнать, как явно задать настройки логина, задание куков и убрать эту перессылку на логин, чтобы кидало ошибку 401.

Заранее спасибо за ответ.

Answer 1

Отлуп неавторизованных запросов с кодом ошибки в xml-конфигурации делается так

<beans:bean id="authEntryPoint"
  class="org.springframework.security.web.authentication.HttpStatusEntryPoint">
    <constructor-arg name="httpStatus" 
                     value="#{T(org.springframework.http.HttpStatus).UNAUTHORIZED}" />
</beans:bean>
<security:http use-expressions="true" request-matcher="regex" entry-point-ref="authEntryPoint">
    <security:intercept-url pattern="/api/test/getting" access="permitAll" />
    <security:intercept-url pattern="/api/.*" access="isAuthenticated()" />
</security:http>

Правда, я бы лучше использовал org.springframework.security.web.authentication.Http403ForbiddenEntryPoint.

И для API лучше подойдёт аутентификация не через форму и куки, а передачей в заголовках jwt-токена или basic-credentials.

Answer 2

Как всегда пришлось решать все самим, напишу сам ответ, вдруг кому пригодится.

Решилась эта проблема на самом деле очень просто, необходимо было реализовать свой authenticationEntryPoint.

Итоговый код конфигурационного класса наследуемого от WebSecurityConfigurerAdapter.

@Autowired
private DataSource dataSource;
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.jdbcAuthentication().dataSource(dataSource)
            .usersByUsernameQuery(
                    "select username,password, enabled from users where username=?")
            .authoritiesByUsernameQuery(
                    "select username, role from user_roles where username=?")
            .passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/api/test/getting").permitAll()
            .anyRequest().authenticated()
            .and()
            .httpBasic()
            .authenticationEntryPoint(authenticationEntryPoint);
}

И вот сам код AuthenticationEntryPoint, который наследуется от BasicAuthenticationEntryPoint.

@Override
public void commence
        (HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
        throws IOException {
    response.addHeader("WWW-Authenticate", "Basic realm='" + getRealmName() + "'");
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    PrintWriter writer = response.getWriter();
    writer.println("HTTP Status 401 - " + authEx.getMessage());
}
@Override
public void afterPropertiesSet() throws Exception {
    setRealmName("Watch Counter");
    super.afterPropertiesSet();
}

В итоге открыт доступ без авторизации к /api/test/getting, но ко всему остальному нужна авторизация и без нее возвращается ошибка 401 с сообщением HTTP Status 401 - Full authentication is required to access this resource.

READ ALSO
Как из List (jsp) получить определенный id?

Как из List (jsp) получить определенный id?

Есть jsp страница, которая показывает несколько квестовКаждый можно посмотреть и прокомментировать

219
Преобразовать Map в Map через stream api

Преобразовать Map в Map через stream api

Есть объект, и есть view для этого объектаЕсть Map<String, List<MyObject>>

178
Возможно ли из одного класса получить картинку из другого класса, а именно из другого layout, относящегося ко второму классу

Возможно ли из одного класса получить картинку из другого класса, а именно из другого layout, относящегося ко второму классу

Мне необходимо из одного класса получить картинку из другого класса, а именно из другого layout, относящегося ко второму классу

155
Настройка Spring Boot OAuth2

Настройка Spring Boot OAuth2

Я хочу сделать следующее приложение: Есть 2 конечные токи API: 1) /hello/user - возвращает строку Hello ${username}, где username имя пользователя 2) /hello/anonymous - возвращает...

229