#spring #spring-security
#java #spring #spring-безопасность
Вопрос:
Я пытаюсь использовать Spring Security в своем проекте, вот код:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
//super.configure(auth);
//auth.inMemoryAuthentication().withUser("admin").password("1111").roles("USER");
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username, password, 1 from users where username=?")
.authoritiesByUsernameQuery("select users_username, roles_id from roles_users where users_username=?")
.rolePrefix("ROLE_");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
http
.httpBasic();
http
.authorizeRequests()
.anyRequest().authenticated();
http
.authorizeRequests()
.antMatchers("/users/all").hasRole("admin")
.and()
.formLogin();
http
.exceptionHandling().accessDeniedPage("/403");
}
Вот в чем проблема:
Представьте, что у нас есть два пользователя (один с user
ролью, а другой с admin
ролью) в нашей базе данных, один администратор, а второй пользователь, проблема в том, что когда я подключаюсь как пользователь (у которого есть только user
роль), он может получить доступ к ресурсам администратора (и это не ожидаемое поведение).
Я думаю, что проблема в этом запросе:
"select username, password, 1 from users where username=?"
Согласно этому username
является первичным ключом?
Если у кого-нибудь есть идея, как я могу решить эту проблему?
Ответ №1:
Ваш первый сопоставитель anyRequest()
всегда применяется, потому что порядок сопоставлений важен, см. HttpSecurity#authorizeRequests
:
Обратите внимание, что сопоставители рассматриваются по порядку. Следовательно, следующее недопустимо, потому что первый сопоставитель соответствует каждому запросу и никогда не перейдет ко второму сопоставлению:
http.authorizeRequests().antMatchers("/**").hasRole("USER").antMatchers("/admin/**") .hasRole("ADMIN")
Ваша измененная и упрощенная конфигурация:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/users/all").hasRole("admin")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.exceptionHandling().accessDeniedPage("/403");
}
Комментарии:
1. Спасибо за добавление ссылки на исходную документацию, этого нет в справочных руководствах с такой же ясностью, ИМХО.
Ответ №2:
Проблема заключается в упорядочении ваших правил при настройке вашего HttpSecurity
. Что происходит, когда приходит запрос и попадает в
authorizeRequests().anyRequest().authenticated()
и поскольку пользователь аутентифицирован, он никогда не попадает в
.antMatchers("/users/all").hasRole("admin")
Вот пример того, как вы можете ее настроить:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/public").permitAll()
.antMatchers("/user").hasRole("USER")
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.exceptionHandling().accessDeniedPage("/403");
}
Он использует шаблон цепочки ответственности. Он будет проходить через цепочку правил, пока не найдет подходящее правило. Любые правила, которые следуют за правилом, которое соответствует, никогда не достигаются. Как правило, при написании правил для аутентифицированных запросов более конкретное правило будет первым.