繁体   English   中英

自定义Spring Security以获取受信任的空间

[英]Customize Spring Security for trusted space

服务在网关在受信任空间中运行之后(gateWay验证OAuth令牌,并仅向服务提供唯一的用户ID,否则它将重定向到身份验证服务)。

我想在服务中使用Spring Security来验证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()
        );
    }
}

其中contextSsoActiveProfileIdProvider.getSsoActiveProfileId()返回uniqueUserId, grantAuthorityService.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为null
  • 在REST API部分上, Principal principal也为null

可信空间问题具有与匿名用户标识类似的解决方案(在研究时,我已经得出了这个结论。)

简短答案

可信空间不需要授权,但是不会调用UserDetailsS​​ervice ,因为默认情况下仅使用AnonymousAuthenticationProviderAnonymousAuthenticationFilter 足以实现基于AnonymousAuthenticationFilter自定义过滤器,该过滤器将覆盖createAuthentication并用自定义项( CustomAnonymousAuthenticationFilter )替换默认值( AnonymousAuthenticationFilter ):

@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();
        }
    }

完整答案

我发现,如果未授权用户,则永远不会调用CustomUserDetailsS​​ervice 继续进行的研究集中在负责创建匿名用户信息的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调用。 并且doFilterincorrect身份验证放入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

因此,提供配置非常好,其中使用魔术批注@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);
    }

如果您需要通过处理授权的/已认证的用户来确保弹簧安全性,那么这是不可接受的,但是在某些情况下,这就足够了。

PS

解决方案的某些部分可以改进,但我希望这个想法总体上是明确的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM