До весенней безопасности
я использовалInterceptor
Были реализованы перехват входа в систему и обработка сеанса простого веб-сайта Demo.Хотя соответствующие функции могут быть достигнуты, нет сомнений в том, что метод настройки, предоставляемый Spring Security, проще и понятнее и может лучше защитить веб-приложение.
Соответствующая структура Spring Security
Здесь вы можете обратиться к официальному вводному документу Spring Security:spring-security-architecture
Проще говоря:
- Spring Security является единственным
Filter
, чей конкретный типFilterChainProxy
, то есть как@Bean
существуетApplicationContext
настроен в. - С точки зрения контейнера, Spring Security является одним фильтром, но в нем много дополнительных фильтров, каждый из которых играет свою роль, как показано на следующем рисунке:
- Аутентификация Spring Security, в основном
AuthenticationManager
Этот интерфейс завершен, и основным методом его проверки являетсяauthenticate()
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
- Этот метод делает три вещи:
- Если он может проверить, что ввод представляет собой действительного принципала, верните
Authentication
(обычно содержитauthenticated=true
) - Бросьте a, если он может подтвердить, что ввод представляет недопустимый принципал
AuthenticationException
- Если он не может решить, вернуться
null
- Если он может проверить, что ввод представляет собой действительного принципала, верните
- чаще всего используется
AuthicationManager
РеализацияProviderManager
, который делегирует егоAuthticationProvider
Этот экземпляр,AuthenticationProvider
иAuthenticationManager
Аналогично, но с некоторыми дополнительными методами, позволяющими вызывающей стороне запрашивать,Authenticaion
форма.
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
supports()
в методеClass<?>
параметрClass<? extends Authentication>
, он только спросит, поддерживает ли он переход кauthenticate()
метод.
-
В этой же программе А.
ProviderManager
поручив сериюAuthenticaitonProviders
Чтобы поддерживать несколько различных механизмов аутентификации, еслиProviderManager
Невозможно определить конкретныйAuthentication
тип экземпляра, он пропускается. -
Часто программа содержит несколько логических групп защиты ресурсов, каждая группа имеет свою уникальную
AuthenticationManager
, обычно они имеют общего родителя, тогда родитель становится"global"资源
, это всеprovider
вернулся.
-
Spring Security предоставляет некоторую конфигурацию, чтобы помочь нам быстро включить функцию проверки, наиболее часто используемый
AuthenticationManagerBuiler
, это in-memory (в памяти), JDBC, LDAP или пользовательскийUserDetailService
Эти районы очень хорошие.
Доступ и контроль разрешений с помощью Spring Security
Примечание. Этот последующий код реализован с использованием SpringBoot в качестве фреймворка и его DEMO Git:Spring-Security-Demo
- В основном путем перегрузки метода configure WebSecurityConfigurerAdapter для управления доступом и разрешениями.
метод | описывать |
---|---|
configure(WebSecurity) | Настройте цепочку фильтров Spring Security, перегрузив |
configure(HttpSecurity) | Путем перегрузки настройте, как перехватчик защищает запрос |
configure(AuthenticationManagerBuilder) | Настройте службу сведений о пользователе, перегрузив |
- Перепишем метод следующим образом:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/index").hasAnyAuthority("ROLE_USER","ROLE_ADMIN")
.antMatchers("/oss").hasAuthority("ROLE_ADMIN")
.antMatchers(HttpMethod.GET, "/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()//.successHandler(successHandler)
.and()
.logout()
.logoutSuccessUrl("/")
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("root").password(new BCryptPasswordEncoder().encode("root")).roles("USER","ADMIN").and()
.withUser("normal").password(new BCryptPasswordEncoder().encode("normal")).roles("USER");
//auth.authenticationProvider(userProvider);
//auth.authenticationProvider(afterProvider);
}
- 通过`antMatchers()`进行URL匹配,再进行相应的处理,比如见上代码,我们将**/index**和**/oss**两个链接进行了拦截,并分别要求拥有`ROLE_USER`或`ROLE_ADMIN`、`ROLE_ADMIN`这两个身份才能访问。
- `anyRequest().authenticated()`指其他请求都会需要验证
- `formLogin()`使其有了登录页面,如果没有后面的`loginPage()`,则会默认生成一个Spring Security的页面,而后面注释掉的`successHandler`则是后续会讲到的。
- `permitAll()`则表示当前连接不需要认证。
- `logout()`会拦截所以的**\logout**请求,完成登出操作,`logoutSuccessUrl()`则是登出后的重定向地址。
- `and()`在其中起连接作用。
-
Некоторые распространенные методы настройки пути защиты
- authentication() : разрешить аутентифицированным пользователям доступ
- denyAll() : безоговорочно запретить любой доступ
- fullAuthenticated() : если пользователь полностью аутентифицирован (не через Remeber me)
- hasIpAdress(String): если бык исходит с данного IP-адреса, к нему можно получить доступ.
- hasAnyAuthority(String ...): если используется какая-либо из заданных ролей, к ней можно получить доступ
- hasAnthority(String): если у пользователя есть данная роль, он может получить доступ
- allowAl() : безоговорочно разрешить метод
- запомнить меня(): если пользователь аутентифицирован с помощью функции «Запомнить меня», он может получить доступ
- Кроме того, есть Роль, соответствующая Полномочию. Эти два понятия являются концепцией. Полномочия должны начинаться с "ROLE_", а Роли это не нужно. См. код выше.
-
В настоящее время наша учетная запись root может получить доступ как к индексу, так и к oss, в то время как обычная учетная запись может получить доступ только к index и не может получить доступ к oss.Если вы получите доступ к oss, это появится:
There was an unexpected error (type=Forbidden, status=403). -
Выше мы сгенерировали двух пользователей памяти root и normal, перегрузив configure (аутентификация AuthenticationManagerBuilder), и мы также можем добиться этого с помощью jdbc и других методов.
Реализуйте обработку после успешной аутентификации через AuthenticationSuccessHandler.
- Реализуя интерфейс AuthenticationSuccessHandler, мы можем выполнить соответствующий код после успешной аутентификации, такой как
Token
настройки и так далее, например, я сейчас печатаю данные для входа и перенаправляю запрос на домашнюю страницу
@Component
public class SuccessHandler implements AuthenticationSuccessHandler{
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
System.out.println(authentication.getName()+" is loging , role is"+authentication.getAuthorities());
response.sendRedirect("/");
}
- и добавить его в
formLogin()
после этого, а именно:
.formLogin()
.loginPage("/login")
.permitAll().successHandler(successHandler)
- Войдите снова в учетную запись root, и вы увидите в консоли:root is loging , role is[ROLE_ADMIN, ROLE_USER]
Персонализированная аутентификация через AuthenticationProvider
- мы строим
UserAuthProvider
, и сделать этоAuthenticationProvider
интерфейс:
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
System.out.println("-----------------------------------------------------------------------");
System.out.println("This is UserAuthProvider");
System.out.println("starting authenticate ... ...");
System.out.println("Credentials:"+authentication.getCredentials());
System.out.println("Name:"+authentication.getName());
System.out.println("Class:"+authentication.getClass());
System.out.println("Details:"+authentication.getDetails());
System.out.println("Principal:"+authentication.getPrincipal());
System.out.println("-----------------------------------------------------------------------");
UsernamePasswordAuthenticationToken auth=new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials());
return auth;
}
@Override
public boolean supports(Class<?> authentication) {
System.out.println("This is UserAuthProvider");
System.out.println("starting supports");
System.out.println(authentication.getClass());
return false;
}
- В то же время мы прокомментировали предыдущий
auth.inMemoryAuthentication()
, Присоединяйтесь к UserAuthProviderAuthenticationManagerBuilder
, это:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
// .withUser("root").password(new BCryptPasswordEncoder().encode("root")).roles("USER","ADMIN").and()
// .withUser("normal").password(new BCryptPasswordEncoder().encode("normal")).roles("USER");
auth.authenticationProvider(userProvider);
auth.authenticationProvider(afterProvider);
}
- В этот момент, когда мы снова войдем в систему, мы обнаружим, что консоль выводит
This is UserAuthProvider
starting supports
java.lang. Class
- Причина этого в том, что мы переписываем
supports()
метод, всегда возвращает false, и когда он возвращает false, он не будет вызываться сноваauthenticate()
Чтобы выполнить операцию аутентификации (как описано выше), мы будемsupports()
Возвращаемое значение становится истинным, снова войдите в систему (имя пользователя: пароль root: 1234), консоль выдаст
This is UserAuthProvider
starting supports
class java.lang.Class
-----------------------------------------------------------------------
This is UserAuthProvider
starting authenticate ... ...
Credentials:1234
Name:root
Class:class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
Details:org.springframework.security.web.authentication.WebAuthenticationDetails@166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node04v47liue6knt1oghnzgiqb9dx0
Principal:root
-----------------------------------------------------------------------
root is loging , role is[]
-
успешно вошел в систему, потому что мы находимся в
authenticate()
Метод напрямую объявляетAuthentication
экземплярUsernamePasswordAuthenticationToken
, и возвращается, как сказано выше, при возвратеAuthentication
Например, по умолчанию авторизация прошла успешно, и если мы вернемnull
, это означает, что это не может быть оценено, и вход в систему не будет успешным. -
В этот момент мы создаем еще один объект
UserAfterProvider
, который также реализуетAuthenticationProvider
интерфейс иUserAfterProvider
иUserAuthProvider
изauthenticate()
возвращаемые значения установлены наnull
, мы используем вышеуказанные данные для повторного входа в систему, вывод консоли выглядит следующим образом:
This is UserAuthProvider
starting supports
class java.lang.Class
-----------------------------------------------------------------------
This is UserAuthProvider
starting authenticate ... ...
Credentials:1234
Name:root
Class:class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
Details:org.springframework.security.web.authentication.WebAuthenticationDetails@43458: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node01m47f3t6xq5a470fu07jaipzb0
Principal:root
-----------------------------------------------------------------------
This is UserAfterProvider
starting supports
class java.lang.Class
-----------------------------------------------------------------------
This is UserAfterProvider
starting authenticate ... ...
Credentials:1234
Name:root
Class:class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
Details:org.springframework.security.web.authentication.WebAuthenticationDetails@43458: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node01m47f3t6xq5a470fu07jaipzb0
Principal:root
-----------------------------------------------------------------------
- То есть оба Porvider были проверены, и ни один из них не прошел (возвращает null), что указывает на то, что все соединения
AuthenticationManagerBuilder
Проверка будет выполнена снова, тогда, если мы поставь один из провайдераauthenticate()
Возвращаемое значение возвращается кAuthentication
Например, войдите снова, консоль выдаст следующие результаты:
This is UserAuthProvider
starting supports
class java.lang.Class
-----------------------------------------------------------------------
This is UserAuthProvider
starting authenticate ... ...
Credentials:1234
Name:root
Class:class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
Details:org.springframework.security.web.authentication.WebAuthenticationDetails@166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node04v47liue6knt1oghnzgiqb9dx0
Principal:root
-----------------------------------------------------------------------
root is loging , role is[]
This is UserAuthProvider
starting supports
class java.lang.Class
-----------------------------------------------------------------------
This is UserAuthProvider
starting authenticate ... ...
Credentials:null
Name:root
Class:class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
Details:org.springframework.security.web.authentication.WebAuthenticationDetails@166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: node04v47liue6knt1oghnzgiqb9dx0
Principal:root
-----------------------------------------------------------------------
-
потому что мы переписываем
AuthenticationSuccessHandler
, так что проверка прошла успешно, жаль перенаправить на **/, и мой контроллер прав/сделал еще один перенаправить/Индекс **, значит было две проверки, но в этот раз мы выяснили, что т.к.UserAuthProvider
прошло, значитUserAfterProvider
Проверка отсутствует, поэтому мы можем знать, что если один провайдер проходит проверку, мы можем считать, что он прошел проверку. -
Поэтому мы можем добиться
AuthenticationProvider
Чтобы написать свою собственную логику аутентификации, вы можете даже использовать службы, связанные с @Autowire, чтобы помочь в реализации.