Я изучаю SpringBoot для проекта приложения микросервиса. Я использую маршрут аутентификации клиента Keycloak с ролью учетных записей служб и добавил конфигурацию keycloak в приложение application.properties службы шлюза API. Вот конфигурация безопасности шлюза API:
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity serverHttpSecurity){
serverHttpSecurity
.authorizeExchange(exchange -> exchange
.pathMatchers(
"/api/**",
"/eureka/**"
)
.permitAll()
.anyExchange()
.authenticated())
.csrf()
.disable()
.oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt);
return serverHttpSecurity.build();
}
}
Теперь я создаю приложение службы идентификации, которое обрабатывает регистрацию и вход пользователей отдельно от службы пользователей для управления пользователями. Эта служба использует аутентификацию JWT с Spring Security AuthenticationProvider, JwtFilter и UserNamePasswordAuthenticationFilter. Ниже приведена конфигурация безопасности службы идентификации.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig {
private final JwtFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
httpSecurity
.cors(withDefaults())
.csrf(AbstractHttpConfigurer::disable)
.authorizeRequests(request -> request.regexMatchers(
"/api/auth/*",
"/v2/api-docs",
"/v3/api-docs",
"/v3/api-docs/*",
"/swagger-resources/",
"/swagger-resources/*",
"/configuration/ui",
"/configuration/security",
"/swagger-ui/",
"/webjars/*",
"/swagger-ui.html"
)
.permitAll()
.anyRequest()
.authenticated()
)
.sessionManagement(
session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
}
Зарегистрироваться / Localhost Это конечная точка: при выполнении POST-запроса почтальона с использованием OAuth2.0 с токеном JWT я получаю в ответ 403: запрещено. Ниже приведен журнал службы шлюза API:
2024-04-12 20:15:02.924 TRACE [xpense-gateway,,] 35685 --- [ parallel-8] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "[/eureka/web]" does not match against value "/api/auth/register"
2024-04-12 20:15:02.924 TRACE [xpense-gateway,,] 35685 --- [ parallel-8] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "/api/auth/**" matches against value "/api/auth/register"
2024-04-12 20:15:02.924 DEBUG [xpense-gateway,,] 35685 --- [ parallel-8] o.s.c.g.h.RoutePredicateHandlerMapping : Route matched: identity-service
2024-04-12 20:15:02.924 DEBUG [xpense-gateway,,] 35685 --- [ parallel-8] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: POST http://localhost:8080/api/auth/register] to Route{id='identity-service', uri=lb://identity-service, order=0, predicate=Paths: [/api/auth/**], match trailing slash: true, gatewayFilters=[[[SpringCloudCircuitBreakerResilience4JFilterFactory name = 'resilience', fallback = /identity-fallback], order = 0]], metadata = {}}
2024-04-12 20:15:02.924 DEBUG [xpense-gateway,,] 35685 --- [ parallel-8] o.s.c.g.h.RoutePredicateHandlerMapping : [ade0fee6-4] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@56568494
2024-04-12 20:15:02.924 DEBUG [xpense-gateway,,] 35685 --- [ parallel-8] o.s.c.g.handler.FilteringWebHandler : Sorted gatewayFilterFactories: [[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter@7a3a49e5}, order = -2147483648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@305881b8}, order = -2147482648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@6dbb3d7d}, order = -1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter@3ea9a091}, order = 0], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.GatewayMetricsFilter@26495639}, order = 0], [[SpringCloudCircuitBreakerResilience4JFilterFactory name = 'resilience', fallback = /identity-fallback], order = 0], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@6c1b82cd}, order = 10000], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter@54687fd0}, order = 10150], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.LoadBalancerServiceInstanceCookieFilter@6eaf030c}, order = 10151], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@16f4a3c0}, order = 2147483646], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter@b2da3a5}, order = 2147483647], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter@acd3460}, order = 2147483647]]
2024-04-12 20:15:02.924 TRACE [xpense-gateway,ffa6cb10b03275d4,5984f380554abaa0] 35685 --- [ parallel-8] o.s.c.g.filter.RouteToRequestUrlFilter : RouteToRequestUrlFilter start
2024-04-12 20:15:02.924 TRACE [xpense-gateway,ffa6cb10b03275d4,5984f380554abaa0] 35685 --- [ parallel-8] s.c.g.f.ReactiveLoadBalancerClientFilter : ReactiveLoadBalancerClientFilter url before: lb://identity-service/api/auth/register
2024-04-12 20:15:02.925 TRACE [xpense-gateway,ffa6cb10b03275d4,5984f380554abaa0] 35685 --- [ parallel-8] s.c.g.f.ReactiveLoadBalancerClientFilter : LoadBalancerClientFilter url chosen: http://VFIEVOX3.Router:35185/api/auth/register
2024-04-12 20:15:02.945 TRACE [xpense-gateway,,] 35685 --- [r-http-epoll-11] o.s.c.gateway.filter.NettyRoutingFilter : outbound route: dc99dbca, inbound: [ade0fee6-4]
2024-04-12 20:15:02.951 TRACE [xpense-gateway,,] 35685 --- [r-http-epoll-11] o.s.c.g.filter.NettyWriteResponseFilter : NettyWriteResponseFilter start inbound: dc99dbca, outbound: [ade0fee6-4]
2024-04-12 20:15:02.952 TRACE [xpense-gateway,,] 35685 --- [r-http-epoll-11] o.s.c.g.filter.GatewayMetricsFilter : spring.cloud.gateway.requests tags: [tag(httpMethod=POST),tag(httpStatusCode=403),tag(outcome=CLIENT_ERROR),tag(routeId=identity-service),tag(routeUri=lb://identity-service),tag(status=FORBIDDEN)]
Я знаю, что механизм KeyCloak может обрабатывать аутентификацию пользователей, но я пытаюсь создать в службе с нуля собственный механизм для аутентификации и авторизации пользователей.
Я пытался просмотреть документацию по Spring Security, но теперь я запутался, так как не понимаю, как именно SecurityFilterChain и SecurityWebFilterChain. SecurityWebFilterChain предоставляет Oauth2ResourceServer с ServerHttpSecurity, но не обрабатывает SessionManagement и sessionCreation, которые я с нетерпением жду реализации.
Заранее спасибо.
Я почти уверен, что мой собственный фильтр не лучше, чем oauth2resourceserver. Я пытаюсь создать собственную реализацию, которая включает в себя генерацию токена наряду с другими механизмами, такими как активация учетной записи электронной почты с использованием кода активации и так далее.
Тогда я бы предложил вместо этого использовать перехватчик onAuthentication, ваш поток очень неясен, вы используете аутентификацию учетных данных клиента, то есть аутентификацию службы для службы (микросервис для микросервиса, а не браузер), именно для этого создан oauth2ResourceServer
. Но тогда вы говорите об управлении сеансами и создании сеансов. Здесь вам следует прочитать о шаблоне BFF, и чтобы не показаться грубым, я бы посоветовал вам прочитать больше об oauth и особенно об open id Connect, если вы хотите учиться. И не пишите собственную безопасность, используйте инструменты, встроенные в Spring Security.
например, есть отличные ресурсы, которые бесплатны Spring.academy
Хорошо, я посмотрю на это. Я только начал изучать Springboot, и меня просто ошеломляет его необъятность. Причина, по которой я внедрил Keycloak, заключалась в том, чтобы обрабатывать аутентификацию на уровне приложения. С другой стороны, JWT с пружинной безопасностью для механизма аутентификации на уровне пользователя. Я могу отправить вам репозиторий на git, если вы хотите, возможно, тем временем взглянуть на кодовую базу.
Нет, я не буду просматривать какой-либо код на GitHub и т. д. У меня, как и у многих других, есть работа, и я трачу немного свободного времени, отвечая на четко определенные вопросы, которые требуют надлежащего исследования и отдельных ответов. Stack Overflow — это сайт вопросов и ответов, а не форум, если вы ищете обзоры, обсуждения и т. д. Я бы посоветовал вам посетить другие сайты или серверы разногласий и т. д. Безопасность — это сложная тема, и вам действительно следует подружиться с официальные документы. Прочтите главы об архитектуре. И не используйте JWT, начните с простого FormLogin и узнайте, как он работает. Удачи
Вы пытаетесь построить предприятие, не зная, как работают такие вещи, как BASIC-безопасность, FormLogin (аутентификация на основе сеанса с использованием файлов cookie), аутентификация на основе сертификатов или такие вещи, как SAML и т. д. Начните с простого (базового), затем реализуйте промежуточный (FormLogin), затем изучите oauth.
Я понял проблему и почему она зависает. Спасибо за вашу помощь и время,
Проблема в конфигурации кода заключалась в порядке механизма цепочки веб-фильтров.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests(request -> request.regexMatchers(
"/api/auth/*",
"/v2/api-docs",
"/v3/api-docs",
"/v3/api-docs/*",
"/swagger-resources/",
"/swagger-resources/*",
"/configuration/ui",
"/configuration/security",
"/swagger-ui/",
"/webjars/*",
"/swagger-ui.html"
)
.permitAll()
.anyRequest()
.authenticated()
)
.cors(withDefaults())
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(
session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
Поскольку authorizeRequests
не был настроен должным образом и вместо этого настройщик cors(withDefaults())
инициализировался в исходной конфигурации с помощью csrf()
.
Это привело к разрешению аутентификации и разрешениям для запросов, соответствующих шаблону.
Почему вы создали собственный JWTFilter? Почему вы не используете фильтр Spring oauth2resourceserver? Является ли ваш самодельный фильтр более безопасным, чем тот, который предусмотрен каркасом?