Разделение клиентской и серверной части отличается от традиционных веб-сервисов, которые не могут использовать сеансы. Поэтому для генерации токенов мы используем механизм без сохранения состояния, такой как JWT. Общая идея заключается в следующем:
Чен использует Spring Boot рамка,Демо-проект имеет два новых модуля,Они естьcommon-base
、security-authentication-jwt
。
1. модуль общей базы
Это абстрактный общедоступный модуль. Этот модуль в основном содержит несколько общедоступных классов. Каталог выглядит следующим образом:
2. модуль безопасности-аутентификации-jwt
Некоторые классы, которые необходимо настроить, такие как класс глобальной конфигурации безопасности и класс конфигурации фильтра входа в систему Jwt, следующие:
3. Пять столов
Дизайн «Разрешения» часто имеет разные варианты дизайна в зависимости от потребностей бизнеса.,Используется ЧеномRBACспецификация,В основном включает в себя пять таблиц,Они естьПользовательская таблица、лист персонажа、таблица разрешений、пользователь<->лист персонажа、Роль<->таблица разрешений,Как показано ниже:
SQL-код приведенных выше таблиц будет помещен в исходный код дела (поля этих таблиц не полностью предназначены для избежания хлопот, вы можете постепенно расширять их в соответствии с вашим бизнесом)
Существует множество способов написания логики интерфейса входа. Сегодня Чен представляет интерфейс входа, использующий определение фильтра.
Spring Securityформа по умолчанию Авторизоваться Сертифицированный фильтрUsernamePasswordAuthenticationFilter
,Этот фильтр не применяется к Архитектуре по Разделению передней и задней части.,Итак, нам нужно настроить фильтр.
Логика очень проста,ссылкаUsernamePasswordAuthenticationFilter
Изменить этот фильтр,Код выглядит следующим образом:
После успешной аутентификации вышеуказанного интерфейса фильтра,будет вызванAuthenticationSuccessHandlerПроцесс,Таким образом, мы можем настроить обработчик успешной аутентификации для выполнения нашей собственной бизнес-обработки.,Код выглядит следующим образом:
Чен только что вернулсяaccessToken、refreshToken,Прочую обработку бизнес-логики можно улучшить самостоятельно.
Такой же,один раз Авторизоватьсянеудача,Например, ошибки имени пользователя или пароля и т. д.,будет вызванAuthenticationFailureHandlerПроцесс,Поэтому нам нужно настроить процессор на случай сбоя аутентификации.,Возвращает определенное значение на основе информации об исключении.JSONДанные клиенту,Код выглядит следующим образом:
Логика очень проста,AuthenticationExceptionСуществуют разные классы реализации,Просто верните конкретную подсказку в соответствии с типом исключения.
AuthenticationEntryPointЭтот интерфейс должен бытьПользователь не прошел аутентификацию для доступа к защищенным ресурсамчас,будет вызванcommence()
метод Процесс,Например, токен, хранящийся у клиента, подделан.,Поэтому нам необходимо настроитьAuthenticationEntryPointВозврат конкретной подсказки,Код выглядит следующим образом:
AccessDeniedHandlerКогда этот процессор успешно аутентифицированизпользовательдоступзащищенныйизресурс,ноНедостаточно разрешений,войдет в этот процессор для обработки,Мы можем реализовать этот процессор для возврата клиенту конкретной оперативной информации.,Код выглядит следующим образом:
UserDetailsServiceЭтот класс используется для загрузкипользовательинформация,включатьимя пользователя、пароль、Разрешения、Рольсобирать….Существует методследующее:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
В логике аутентификации Spring Security вызовет этот метод для загрузки подробной информации о пользователе на основе имени пользователя, переданного клиентом. Логика, которую необходимо выполнить с помощью этого метода, следующая:
Нам нужно реализовать этот интерфейс,отбаза данныхнагрузкапользовательинформация,Код выглядит следующим образом:
из которыхLoginServiceоснован наимя пользователяотбаза данных Запроситьпароль、Роль、Разрешения,Код выглядит следующим образом:
UserDetailsЭто тоже интерфейс,Определено несколько методов,все вокругимя пользователя、пароль、Разрешения+РольсобиратьЭти три свойства,Таким образом, мы можем реализовать расширение этих полей этого класса.,SecurityUserКод выглядит следующим образом:
расширять:UserDetailsServiceРеализация этого класса обычно включает в себя5Чжан Бяо,Они естьПользовательская таблица、лист персонажа、таблица разрешений、пользователь<->Роль Таблица соответствия、Роль<->Разрешения Таблица соответствия,Внедрения на предприятии должны следоватьRBACправила дизайна。Чен подробно представит это правило позже.。
Заголовок запроса клиента содержит токен, и сервер должен анализировать и проверять токен для каждого запроса, поэтому необходимо определить фильтр токенов. Основная логика этого фильтра следующая:
Вышеупомянутое — это лишь часть самой базовой логики. В реальной разработке существуют определенные процессы, такие как помещение подробной информации пользователя в атрибут Request и кэш Redis, чтобы можно было достичь эффекта имитации ретрансляции токена.
Код для проверки фильтра выглядит следующим образом:
accessTokenодин раз Истекший,Клиент должен нестиrefreshTokenПолучить токен,Традиционные веб-сервисы размещаются в файлах cookie.,Только сервер должен завершить обновление,Совершенно неосведомленное обновление токена,но Разделение передней и задней части Архитектурадолжен находиться у клиентаrefreshTokenОбновление интерфейса вручную。
Код выглядит следующим образом:
Основная логика очень проста и заключается в следующем:
Уведомление:В реальном производствеrefreshTokenКак генерируются токены、шифрование Алгоритм можно комбинировать сaccessTokenдругой。
Вышеупомянутое определяет фильтр аутентификации.JwtAuthenticationLoginFilter,Это фильтр для Авторизоваться,ноне вводится вSpring В цепочке фильтров Безопасности необходимо определить конфигурацию, Код выглядит следующим образом:
/**
* @author Публичный аккаунт: Колонка технологий Code Ape
* Класс конфигурации для фильтра Авторизоваться
*/
@Configuration
public class JwtAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
/**
* userDetailService
*/
@Qualifier("jwtTokenUserDetailsService")
@Autowired
private UserDetailsService userDetailsService;
/**
* Авторизоваться процессор успеха
*/
@Autowired
private LoginAuthenticationSuccessHandler loginAuthenticationSuccessHandler;
/**
* Авторизоваться обработчик ошибок
*/
@Autowired
private LoginAuthenticationFailureHandler loginAuthenticationFailureHandler;
/**
* шифрование
*/
@Autowired
private PasswordEncoder passwordEncoder;
/**
* Настройте фильтр интерфейса Авторизоваться в цепочку фильтров.
* 1. Настроить Авторизоваться обработчики успеха и неудачи
* 2. Настройте пользовательский userDetailService (из базы Получить данные пользователя в data)
* 3. Настройте пользовательские фильтры для весны В цепочке фильтров безопасности настройте его перед UsernamePasswordAuthenticationFilter.
* @param http
*/
@Override
public void configure(HttpSecurity http) {
JwtAuthenticationLoginFilter filter = new JwtAuthenticationLoginFilter();
filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
//Обработчик успешной аутентификации
filter.setAuthenticationSuccessHandler(loginAuthenticationSuccessHandler);
//Обработчик ошибки аутентификации
filter.setAuthenticationFailureHandler(loginAuthenticationFailureHandler);
//Используем DaoAuthenticationProvider напрямую
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
//Устанавливаем userDetailService
provider.setUserDetailsService(userDetailsService);
//Устанавливаем алгоритм шифрования
provider.setPasswordEncoder(passwordEncoder);
http.authenticationProvider(provider);
//Добавьте этот фильтр в UsernamePasswordAuthenticationFilter перед выполнением
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
}
}
Вся логика естьpublic void configure(HttpSecurity http)
В этом методе,следующее:
Вышеуказанное настраивает только фильтр входа в систему, и вам также необходимо выполнить некоторые настройки в классе глобальной конфигурации, а именно:
Полная конфигурация выглядит следующим образом:
/**
* @author Публичный аккаунт: Колонка технологий Code Ape
* @EnableGlobalMethodSecurity Включите аннотацию для проверки Разрешения
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationSecurityConfig jwtAuthenticationSecurityConfig;
@Autowired
private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler;
@Autowired
private RequestAccessDeniedHandler requestAccessDeniedHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
//Запрещатьформа Авторизоваться,Разделение передней и задней часть бесполезна
.disable()
//Применяем настройку фильтра Авторизоваться, настраиваем разделение
.apply(jwtAuthenticationSecurityConfig)
.and()
// Установить авторизацию для URL
.authorizeRequests()
// Здесь нужно разрешить странице Авторизоваться, PermitAll() означает отсутствие перехвата, /login Авторизоватьсяизurl,/refreshTokenобновитьtokenизurl //TODO Здесь есть много URL-адресов, выпущенных в обычных проектах, таких как URL-адреса, связанные с чванством, URL-адреса серверной части друида и некоторые статические ресурсы.
.antMatchers( "/login","/refreshToken")
.permitAll()
//hasRole() указывает, что для доступа к ресурсу необходима указанная Роль
.antMatchers("/hello").hasRole("ADMIN")
// anyRequest() Все запросы authenticated() должен быть аутентифицирован
.anyRequest()
.authenticated()
//Обработка исключений: сбой аутентификации и недостаточное разрешение
.and()
.exceptionHandling()
//Аутентификация не удалась, доступ к обработчику исключений запрещен
.authenticationEntryPoint(entryPointUnauthorizedHandler)
//Сертификация пройдена, но процессора Разрешения нет
.accessDeniedHandler(requestAccessDeniedHandler)
.and()
//Отключаем сеанс, проверка JWT не требует сеанса
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
//Настройте фильтр проверки TOKEN в цепочке фильтров, иначе он не вступит в силу и будет помещен перед UsernamePasswordAuthenticationFilter.
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
// закрыть csrf
.csrf().disable();
}
// Пользовательский JWT Фильтр проверки токена
@Bean
public TokenAuthenticationFilter authenticationTokenFilterBean() {
return new TokenAuthenticationFilter();
}
/**
* шифрованиеалгоритм
* @return
*/
@Bean
public PasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
Аннотации очень подробные. Если вы не поняли, пожалуйста, прочтите внимательно.
Исходный код кейса загружен на GitHub, обратите внимание на Официальный аккаунт:Колонка технологий Code Ape,Ключевые слова ответа:9529 Возьми!
1. Сначала тест Авторизовать интерфейс,postmanдоступhttp://localhost:2001/security-jwt/login следующим образом:
Как видите, два токена были успешно возвращены.
2. Заголовок запроса не содержит токена.,прямой запросhttp://localhost:2001/security-jwt/hello, следующим образом:
можно увидеть,Введено напрямуюEntryPointUnauthorizedHandlerэтот процессор。
3、нестиtokenдоступhttp://localhost:2001/security-jwt/hello, следующим образом:
После успешного доступа токен действителен.
4、Обновить интерфейс токенатест,нести一个Истекшийизжетондоступследующее:
5、Обновить интерфейс токенатест,Проведение токена с истекшим сроком действия,следующее:
Как видите, два новых токена были успешно возвращены.
Вышеупомянутая серияиз Конфигурация ПолностьюссылкаUsernamePasswordAuthenticationFilterэтот фильтр,Это способ формы веб-сервиса Авторизоваться.
Принцип Spring Security состоит из серии фильтров.,То же самое касается процесса Авторизации.,сначала вorg.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#doFilter()
метод,Выполнить сопоставление аутентификации,следующее:
attemptAuthentication()
этотметод Основная функция – получение клиентской доставки.изusername、password,упакованный вUsernamePasswordAuthenticationToken
сдаватьProviderManager
из进行Сертификация,Исходный кодследующее:
ProviderManagerОсновной процесс — вызов абстрактного классаAbstractUserDetailsAuthenticationProvider#authenticate()
метод,Как показано ниже:
retrieveUser()
метод就是вызовuserDetailServiceЗапроспользовательинформация。Затем аутентифицируйте,После успешной или неудачной аутентификации,Для обработки будут вызваны соответствующие обработчики ошибок и успехов.
Хотя Spring Security относительно тяжелый, его действительно легко использовать, особенно реализацию спецификации Oauth2.0, которая очень проста и удобна.
Исходный код кейса загружен на GitHub, обратите внимание на Официальный аккаунт:Колонка технологий Code Ape,Ключевые слова ответа:9529 Возьми!