spring-security 为受信任空间自定义Spring Security

pw9qyyiw  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(171)

服务在可信空间中的网关之后工作(gateWay验证OAuth令牌,并仅向服务提供唯一的用户ID,否则它将重定向以验证服务)。
我希望在服务中使用Spring安全性,以便能够验证userId的权限。
所以我把CustomUserDetailsService

@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired(required = false)
    private ContextSsoActiveProfileIdProvider contextSsoActiveProfileIdProvider;
    @Autowired
    private GrantedAuthorityService grantedAuthorityService;

    @Override
    public User loadUserByUsername(final String username) throws UsernameNotFoundException {
        // verify it with authentication service, but there is not token, userId only, so trust to gateway service.
        return new User(
                String.valueOf(contextSsoActiveProfileIdProvider.getSsoActiveProfileId()),
                "authenticatedWithGateWay",
                grantedAuthorityService.getGrantedAuthoritiesForCurrentUser()
        );
    }
}

其中,* contextSsoActiveProfileId提供者。getSsoActiveProfileId()* 返回唯一的用户ID,grantedAuthorityService。getGrantedAuthoritiesForCurrentUser() 返回授权。
该服务在受信任区域中启动,因此我已按以下方式配置了安全性:

@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

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

    @Override
    protected UserDetailsService userDetailsService() {
        return userDetailsService;
    }
}

我需要为所有用户提供免费访问所有URI(http.authorizeRequests().antMatchers("/**").permitAll();)(不触发登录提供),但它似乎抑制了下一个注解@PreAuthorize@PreFilter@PostAuthorize@PostFilter的触发处理程序。
我想我在这里把http.authorizeRequests().antMatchers("/**").permitAll();或其他配置部分搞错了。
更多问题症状:

  • 从不调用CustomUserDetailsService.loadUserByUsername(..);
  • 在REST API部分@AuthenticationPrincipal User activeUser为空
  • 在REST API上,Principal principal部分也为空
xiozqbni

xiozqbni1#

可信空间问题的解决方案与匿名用户身份识别类似(我在研究它时得出了这个结论)。

简短回答

信任空间不需要授权,但不会调用 UserDetailsService,因为默认情况下只使用AnonymousAuthenticationProviderAnonymousAuthenticationFilter。可以实现基于AnonymousAuthenticationFilter覆盖createAuthentication的自定义过滤器,并将默认过滤器(AnonymousAuthenticationFilter)替换为自定义过滤器(CustomAnonymousAuthenticationFilter):

@Configuration
    public static class NoAuthConfigurationAdapter extends WebSecurityConfigurerAdapter {
        @Autowired
        private UserDetailsService userDetailsService;
        @Autowired
        private IdentifiableAnonymousAuthenticationFilter identifiableAnonymousAuthenticationFilter;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.anonymous().authenticationFilter(identifiableAnonymousAuthenticationFilter);
            http.antMatcher("/**").authorizeRequests()
                    .anyRequest().permitAll();
        }
    }

完整答案

我发现如果用户未授权,CustomUserDetailsService将永远不会被调用。继续研究时请注意AnonymousAuthenticationFilter,它负责创建匿名用户信息。因此,在非常和目的是用我的IdentifiableAnonymousAuthenticationFilter替换AnonymousAuthenticationFilter,其中一些方法应被覆盖:

@Component
public class IdentifiableAnonymousAuthenticationFilter extends AnonymousAuthenticationFilter {
    public static final String KEY_IDENTIFIABLE_ANONYMOUS_AUTHENTICATION_FILTER
            = "Key.IdentifiableAnonymousAuthenticationFilter";
    @Autowired
    private CustomUserDetailsService userDetailsService;
    @Autowired
    private GrantedAuthorityService grantedAuthorityService;
    private AuthenticationDetailsSource authenticationDetailsSource
            = new WebAuthenticationDetailsSource();

    public IdentifiableAnonymousAuthenticationFilter() {
        this(KEY_IDENTIFIABLE_ANONYMOUS_AUTHENTICATION_FILTER);
    }

    public IdentifiableAnonymousAuthenticationFilter(String key) {
        super(key);
    }

    @Override
    protected Authentication createAuthentication(HttpServletRequest request) {
        AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(
                KEY_IDENTIFIABLE_ANONYMOUS_AUTHENTICATION_FILTER,
                userDetailsService.loadCurrentUser(request),
                grantedAuthorityService.getGrantedAuthoritiesForCurrentUser());
        auth.setDetails(authenticationDetailsSource.buildDetails(request));
        return auth;
    }
}

将其注入到配置中

@Configuration
public class IdentifyAnonymousConfigurationAdapter extends WebSecurityConfigurerAdapter {
    @Autowired
    private IdentifiableAnonymousAuthenticationFilter identifiableAnonymousAuthenticationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.anonymous().authenticationFilter(identifiableAnonymousAuthenticationFilter);
    // ... some other configurations
    }
}

现在看起来好多了,因为 identifiableAnonymousAuthenticationFilter 被注入到了AnonymousConfigurer中。请注意您基于WebSecurityConfigurerAdapter的配置。如果您有几个配置,其中一个不会设置 customAnonymousAuthenticationFilter,而是在 *custom.. * 之前配置,您将获得 AnonymousAuthenticationFilter 的默认示例(默认情况下在WebSecurityConfigurerAdapter中配置):

protected final HttpSecurity getHttp() throws Exception {
      //...
      http
        .csrf().and()
        .addFilter(new WebAsyncManagerIntegrationFilter())
        .exceptionHandling().and()
        .headers().and()
        .sessionManagement().and()
        .securityContext().and()
        .requestCache().and()
        .anonymous().and()
      // ...

如果应用程序已修复,我不会关心它,但AnonymousAuthenticationFilter调用时间早于IdentifiableAnonymousAuthenticationFilter,并且 doFilterSecurityContextHolderincorrect身份验证放入SecurityContextHolder

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    if(SecurityContextHolder.getContext().getAuthentication() == null) {
        SecurityContextHolder.getContext().setAuthentication(this.createAuthentication((HttpServletRequest)req));
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
        }
    } else if(this.logger.isDebugEnabled()) {
        this.logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
    }

    chain.doFilter(req, res);
}

因此,当下次为IdentifiableAnonymousAuthenticationFilter调用 doFilter 时,由于条件if(SecurityContextHolder.getContext().getAuthentication() == null)(请参见前面的方法),它不会替换Authentication
因此,如果能够提供配置,其中使用magic annotation**@Order**修复WebSecurityConfigurerAdapter配置,以管理配置加载顺序,效果会非常好。

警告

或者有人可能会想-在IdentifiableAnonymousAuthenticationFilter中添加doFilter覆盖而不带条件(它是hack):

@Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req));
        if (logger.isDebugEnabled()) {
            logger.debug("Populated SecurityContextHolder with anonymous token: '"
                    + SecurityContextHolder.getContext().getAuthentication() + "'");
        }
        chain.doFilter(req, res);
    }

如果您需要处理授权/已验证用户的Spring安全性,则这是不可接受的,但在某些情况下,这已经足够了。
附言
解决方案的某些部分可以改进,但我希望这个想法总体上是清楚的。

相关问题