Spring Security:匿名和登录用户可访问的页面上的空主体

问题描述

我需要匿名用户登录用户都可以访问我的应用程序中的页面。 该应用程序是 SSO 上下文的一部分,其中 Keycloak 负责身份验证,而访问控制由 Spring Security 管理。 每个应用程序都有自己在 Keycloak 中配置的客户端。

我需要:

  1. 匿名用户可以不受限制地访问此页面
  2. 应该立即识别已经在 SSO 上下文中的其他应用程序上进行身份验证的用户,以便我可以打印登录用户名称

到目前为止,当我尝试检索 Principal 时,我总是得到 null:

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
User = (User) authentication.getPrincipal();

奇怪的是,如果我浏览一个受保护的页面,那么 Principal 就会被填充;否则,它始终为空。

同样遵循 https://spring.io/guides/topicals/spring-security-architecture 的指示(在“创建和自定义过滤器链”段落)我继续得到一个空的主体:似乎没有后备过滤器拦截所有不匹配的 URL我的配置。

以下是我的安全配置:

@Configuration
@Order(1)
public class MyConfigurationAdapter extends WebSecurityConfigurerAdapter {

  @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http
        .authorizeRequests()
        .antMatchers("/secure/**")
        .authenticated()
        .and()
        .csrf().disable();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/js/**");
        web.ignoring().antMatchers("/actuator/**");
        web.ignoring().antMatchers("/robot**");
    }
}

我还尝试设置回退配置,但始终获得相同的结果。

@Configuration
@Order(2)
public class BasicAuthConfigurationAdapter extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
        .antMatchers("/**")
        .permitAll();
    }
}

有什么想法吗?

预先感谢您的建议。

>>> 更新 1

super.configure(http) 在我的安全文件调用KeycloakWebSecurityConfigurerAdapter

的这个方法
protected void configure(HttpSecurity http) throws Exception {
        http
        .csrf().requireCsrfProtectionMatcher(keycloakCsrfRequestMatcher())
        .and()
        .sessionManagement()
        .sessionAuthenticationStrategy(sessionAuthenticationStrategy())
        .and()
        .addFilterBefore(keycloakPreAuthActionsFilter(),logoutFilter.class)
        .addFilterBefore(keycloakAuthenticationProcessingFilter(),BasicAuthenticationFilter.class)
        .addFilterBefore(keycloakAuthenticatedActionsFilter(),BasicAuthenticationFilter.class)
        .addFilterafter(keycloakSecurityContextRequestFilter(),SecurityContextHolderAwareRequestFilter.class)
        .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
        .and()
        .logout()
        .addlogoutHandler(keycloaklogoutHandler())
        .logoutUrl("/sso/logout").permitAll()
        .logoutSuccessUrl("/");
    }

>>> 更新 2

我报告了从更深入的分析中得出的一些细节。

a) 与创建过滤器链相关的日志(在启动时打印):

Creating filter chain: Ant [pattern='/js/**'],[]
Creating filter chain: Ant [pattern='/actuator/**'],[]
Adding web access control expression 'permitAll',for OrRequestMatcher [requestMatchers=[Ant [pattern='/sso/logout',GET],Ant [pattern='/sso/logout',POST],PUT],DELETE]]]
Adding web access control expression 'permitAll',for ExactUrl [processUrl='/']
Adding web access control expression 'authenticated',for Ant [pattern='/secure/**']
Creating filter chain: any request,[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@4c4cc360,org.springframework.security.web.context.SecurityContextPersistenceFilter@6710a0e3,org.springframework.security.web.header.HeaderWriterFilter@685b095c,org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter@5aec45a0,org.springframework.security.web.authentication.logout.logoutFilter@a766049,org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter@3ac44e13,org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticatedActionsFilter@3829182c,org.springframework.security.web.savedrequest.RequestCacheAwareFilter@3f20b0d8,org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@715bfed,org.keycloak.adapters.springsecurity.filter.KeycloakSecurityContextRequestFilter@67b72f63,org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2719a5c3,org.springframework.security.web.session.SessionManagementFilter@508bfcfe,org.springframework.security.web.access.ExceptionTranslationFilter@46aab01d,org.springframework.security.web.access.intercept.FilterSecurityInterceptor@152f7bc5]

b) 调用非安全页面时出现的日志

o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/edizione/4745'; against '/js/**'
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/edizione/4745'; against '/actuator/**'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 1 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 2 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_Security_CONTEXT
w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@d99b189. A new one will be created.
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 3 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 4 of 14 in additional filter chain; firing Filter: 'KeycloakPreAuthActionsFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 5 of 14 in additional filter chain; firing Filter: 'logoutFilter'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/sso/logout',GET]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/edizione/4745'; against '/sso/logout'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/sso/logout',POST]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /edizione/4745' doesn't match 'POST /sso/logout'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/sso/logout',PUT]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /edizione/4745' doesn't match 'PUT /sso/logout'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/sso/logout',DELETE]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /edizione/4745' doesn't match 'DELETE /sso/logout'
o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 6 of 14 in additional filter chain; firing Filter: 'KeycloakAuthenticationProcessingFilter'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/sso/login']
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/edizione/4745'; against '/sso/login'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=Authorization,expectedHeaderValue=null]
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using org.keycloak.adapters.springsecurity.filter.QueryParamPresenceRequestMatcher@594f1699
o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 7 of 14 in additional filter chain; firing Filter: 'KeycloakAuthenticatedActionsFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 8 of 14 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
o.s.s.w.s.HttpSessionRequestCache        : saved request doesn't match
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 9 of 14 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 10 of 14 in additional filter chain; firing Filter: 'KeycloakSecurityContextRequestFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 11 of 14 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@e8fcd0e2: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff6a82: RemoteIpAddress: 127.0.0.1; SessionId: 968df9fb-cf20-4f16-9962-df78ee71a960; Granted Authorities: ROLE_ANONYMOUS'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 12 of 14 in additional filter chain; firing Filter: 'SessionManagementFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 13 of 14 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 14 of 14 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/sso/logout',DELETE]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /edizione/4745' doesn't match 'DELETE /sso/logout'
o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/edizione/4745'; against '/secure/**'
o.s.s.w.a.i.FilterSecurityInterceptor    : Public object - authentication not attempted
o.s.security.web.FilterChainProxy        : /edizione/4745 reached end of additional filter chain; proceeding with original chain
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/sso/login']
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/edizione/4745'; against '/sso/login'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=Authorization,expectedHeaderValue=null]
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using org.keycloak.adapters.springsecurity.filter.QueryParamPresenceRequestMatcher@594f1699
o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder Now cleared,as request processing completed

c) 调用安全页面时出现的日志

o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/edizione/4745'; against '/js/**'
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/edizione/4745'; against '/actuator/**'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 1 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 2 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
w.c.HttpSessionSecurityContextRepository : Obtained a valid SecurityContext from SPRING_Security_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@93b05922: Authentication: org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken@93b05922: Principal: f:891de36f-d7ef-48aa-b7eb-1089417e8a81:7A1062D9-3101-4CB7-84BC-81D2080263DC; Credentials: [PROTECTED]; Authenticated: true; Details: org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount@15a225c4; Granted Authorities: ROLE_offline_access,ROLE_uma_authorization'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 3 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 4 of 14 in additional filter chain; firing Filter: 'KeycloakPreAuthActionsFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 5 of 14 in additional filter chain; firing Filter: 'logoutFilter'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/sso/logout',expectedHeaderValue=null]
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using org.keycloak.adapters.springsecurity.filter.QueryParamPresenceRequestMatcher@594f1699
o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 7 of 14 in additional filter chain; firing Filter: 'KeycloakAuthenticatedActionsFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 8 of 14 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
o.s.s.w.s.HttpSessionRequestCache        : saved request doesn't match
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 9 of 14 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 10 of 14 in additional filter chain; firing Filter: 'KeycloakSecurityContextRequestFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 11 of 14 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
o.s.s.w.a.AnonymousAuthenticationFilter  : SecurityContextHolder not populated with anonymous token,as it already contained: 'org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken@93b05922: Principal: f:891de36f-d7ef-48aa-b7eb-1089417e8a81:7A1062D9-3101-4CB7-84BC-81D2080263DC; Credentials: [PROTECTED]; Authenticated: true; Details: org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount@15a225c4; Granted Authorities: ROLE_offline_access,ROLE_uma_authorization'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 12 of 14 in additional filter chain; firing Filter: 'SessionManagementFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 13 of 14 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
o.s.security.web.FilterChainProxy        : /edizione/4745 at position 14 of 14 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/sso/logout',expectedHeaderValue=null]
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using org.keycloak.adapters.springsecurity.filter.QueryParamPresenceRequestMatcher@594f1699
o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder Now cleared,as request processing completed

一些注意事项

就我所看到的调试类 AbstractAuthenticationProcessingFilter.doFilter(...) 而言,在第一种情况下(非安全页面),传入请求从不尝试身份验证,因为以下检查

if (!requiresAuthentication(request,response)) {
    chain.doFilter(request,response);
    return;
}

总是返回 TRUE(对于那个页面,实际上,身份验证不是强制性的),因此执行流程继续到下一个过滤器,跳过上下文的填充。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)