#spring-boot #spring-security #keycloak
#spring-boot #spring-безопасность #keycloak
Вопрос:
У меня есть приложение springboot, которое использует Keycloak для обработки аутентификации JWT. Если я использую @PreAuthorize в своем методе контроллера, все работает, как ожидалось, но HttpSecurity, основанный на шаблоне URL antMatcher, не является. Из того, что я могу сказать, Spring применяет фильтр безопасности ПЕРЕД созданием пользователя-участника. В журналах я вижу, что он тестируется на анонимность, даже если был передан действительный токен-носитель, и я могу видеть AuthenticationPrincipal внутри метода контроллера.
По сути, HttpSecurity выполняет свои правила против анонимных, хотя позже создается действительный участник, который может использоваться проверками @PreAuthorize.
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(
AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider
= keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(
new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakConfigResolver() {
@Override
public KeycloakDeployment resolve(HttpFacade.Request request) {
KeycloakDeployment deployment = null;
AdapterConfig adapterConfig = new AdapterConfig();
adapterConfig.setAuthServerUrl(System.getProperty("keycloak.auth-server-url"));
adapterConfig.setRealm(System.getProperty("keycloak.realm"));
adapterConfig.setResource(System.getProperty("keycloak.resource"));
// adapterConfig.setUseResourceRoleMappings(true);
adapterConfig.setSslRequired("external");
adapterConfig.setPublicClient(true);
deployment = KeycloakDeploymentBuilder.build(adapterConfig);
return deployment;
}
};
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(
new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
.and().csrf().disable()
.authorizeRequests()
.antMatchers("/api/public/*").permitAll()
.antMatchers("/api/admin/*").hasRole("admin")
.antMatchers("/api/*").authenticated()
;
}
}
Журналы безопасности Spring выглядят следующим образом
2020-11-28 10:00:45.659 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/condition at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-11-28 10:00:45.659 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/condition at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-11-28 10:00:45.660 DEBUG 25655 --- [nio-8180-exec-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-11-28 10:00:45.660 DEBUG 25655 --- [nio-8180-exec-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-11-28 10:00:45.662 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/medical-condition at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-11-28 10:00:45.663 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/medical-condition at position 4 of 11 in additional filter chain; firing Filter: 'CorsFilter'
2020-11-28 10:00:45.664 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/medical-condition at position 5 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
2020-11-28 10:00:45.664 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', GET]
2020-11-28 10:00:45.664 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/admin/condition' doesn't match 'GET /logout'
2020-11-28 10:00:45.664 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', POST]
2020-11-28 10:00:45.664 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/admin/condition'; against '/logout'
2020-11-28 10:00:45.664 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', PUT]
2020-11-28 10:00:45.664 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/admin/condition' doesn't match 'PUT /logout'
2020-11-28 10:00:45.664 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', DELETE]
2020-11-28 10:00:45.665 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'POST /api/admin/condition' doesn't match 'DELETE /logout'
2020-11-28 10:00:45.665 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
2020-11-28 10:00:45.665 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/condition at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2020-11-28 10:00:45.665 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.s.HttpSessionRequestCache : saved request doesn't match
2020-11-28 10:00:45.665 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/condition at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2020-11-28 10:00:45.666 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/condition at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2020-11-28 10:00:45.667 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@2aa3a4a: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2020-11-28 10:00:45.667 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/medical-condition at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
2020-11-28 10:00:45.668 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.session.SessionManagementFilter : Requested session ID 8C6524CDA3CD92F69B885542B2E5DF1C is invalid.
2020-11-28 10:00:45.668 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/condition at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2020-11-28 10:00:45.668 DEBUG 25655 --- [nio-8180-exec-1] o.s.security.web.FilterChainProxy : /api/admin/condition at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2020-11-28 10:00:45.669 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/admin/condition'; against '/api/public/*'
2020-11-28 10:00:45.669 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/admin/condition'; against '/api/admin/*'
2020-11-28 10:00:45.669 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /api/admin/condition; Attributes: [hasRole('ROLE_admin')]
2020-11-28 10:00:45.669 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@2aa3a4a: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2020-11-28 10:00:45.673 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@4e7d07d7, returned: -1
2020-11-28 10:00:45.679 DEBUG 25655 --- [nio-8180-exec-1] o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is anonymous); redirecting to authentication entry point
Ответ №1:
Прежде чем настраивать свою собственную конкретную конфигурацию, вам необходимо вызвать Keycloak-configuration
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http); // <----
http.... //
}
Ответ №2:
Метод configure должен вызываться первым, и лучшим вариантом user principal было бы добавить перехватчик, а не фильтры .. и, пожалуйста, добавьте super.configure(http); Спасибо!