为可信空间定制Spring Security [英] Customize Spring Security for trusted space

查看:384
本文介绍了为可信空间定制Spring Security的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

服务在可信空间中的网关之后工作(gateWay验证OAuth令牌并仅向服务提供唯一的用户ID,其他情况下重定向以验证服务)。

Service works after gateway in trusted space (gateWay verifies OAuth token and gives to the service only unique user ID other case it redirects to authenticate service).

我想要在服务中使用spring security可以验证userId的权限。

I want use spring security in the service to be able validate permissions for userId.

所以我添加了 CustomUserDetailsS​​ervice


@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和 grantedAuthorityService.getGrantedAuthoritiesForCurrentUser()返回权限。

Where contextSsoActiveProfileIdProvider.getSsoActiveProfileId() returns uniqueUserId and grantedAuthorityService.getGrantedAuthoritiesForCurrentUser() returns authorities.

该服务在受信任区域中启动,因此我已通过下一步方式配置安全性:

The service starts in trusted zone so I have configured security in next way:


@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

I need provide free access for all users (without triggering login offer) for all URIs (http.authorizeRequests().antMatchers("/**").permitAll();) but it seems suppressed triggering handlers for next annotations @PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter.

我想我在这里误以为 http.authorizeRequests()。antMatchers(/ **)。permitAll( ); 或与其他配置部分。

I suppose I mistook here with http.authorizeRequests().antMatchers("/**").permitAll(); or with other configuration part.

更多问题症状:


  • CustomUserDetailsS​​ervice.loadUserByUsername永远不会调用(..);

  • 在REST API部分 @AuthenticationPrincipal用户activeUser 为空

  • 在REST API部分 Principal principal 为null

  • CustomUserDetailsService.loadUserByUsername(..) is never called;
  • On REST API part @AuthenticationPrincipal User activeUser is null
  • On REST API part Principal principal is null also

推荐答案

可信空间问题与匿名用户识别有类似的解决方案(我在处理它时已经做了这个结论。)

Trusted space issue has similar solution to anonymous user identification (I've done this conclusion when I was working on it.)

可信空间不需要授权,但不会调用 UserDetailsS​​ervice ,因为只使用 AnonymousAuthenticationProvider AnonymousAuthenticationFilter 默认情况下。基于 AnonymousAuthenticationFilter 重写 createAuthentication 并替换默认值( AnonymousAuthenticationFilter )自定义一个( CustomAnonymousAuthenticationFilter ):

Trusted space does not need authorization, but no UserDetailsService will be called, because of using only AnonymousAuthenticationProvider and AnonymousAuthenticationFilter by default. It is good enough implement custom filter based on AnonymousAuthenticationFilter overriding createAuthentication and replace default (AnonymousAuthenticationFilter) with custom one (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();
        }
    }



完整答案



I发现如果用户未获得授权,将永远不会调用 CustomUserDetailsS​​ervice 。持续研究关注 AnonymousAuthenticationFilter ,它负责创建匿名用户信息。因此,目的是用我的 IdentifiableAnonymousAuthenticationFilter 替换 AnonymousAuthenticationFilter ,其中应该覆盖某些方法:

Full answer

I found out that CustomUserDetailsService will never be called if user is not authorized. Continuing research pay attention on the AnonymousAuthenticationFilter which is responsible for creating anonymous user info. So in the very and purpose is to replace the AnonymousAuthenticationFilter with my IdentifiableAnonymousAuthenticationFilter where some methods should be overridden:


@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

Now it seems much better, because identifiableAnonymousAuthenticationFilter is injected in AnonymousConfigurer. Pay your attention to your configurations based on WebSecurityConfigurerAdapter. If you have few ones and one of them will not set customAnonymousAuthenticationFilter but configured earlier than custom.. you'll get default instance of AnonymousAuthenticationFilter (configured in WebSecurityConfigurerAdapter by default):


  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 调用。并且 doFilter 放入 SecurityContextHolder 不正确身份验证。

I would not care about it if application fixed, but AnonymousAuthenticationFilter called earlier than IdentifiableAnonymousAuthenticationFilter. And doFilter puts into SecurityContextHolder incorrect Authentication.

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

因此,当下次 doFilter 被调用 IdentifiableAnonymousAuthenticationFilter 由于条件 if(SecurityContextHolder.getContext()。getAuthentication()== null),它不会替换身份验证 (参见之前的方法)。

So when next time doFilter is called for IdentifiableAnonymousAuthenticationFilter it does not replace the Authentication because of condition if(SecurityContextHolder.getContext().getAuthentication() == null) (see method before).

结果非常适合提供修复 WebSecurityConfigurerAdapter 使用魔术注释配置 @Order 来管理配置加载顺序。

As result would be really good to provide configuration where fix for WebSecurityConfigurerAdapter configuration using magic annotation @Order to manage configuration loading order.

或者有人会想 - 在没有条件的情况下添加 doFilter 覆盖 IdentifiableAnonymousAuthenticationFilter hack ):

Or someone could think - add doFilter overriding in IdentifiableAnonymousAuthenticationFilter without condition (it is 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安全性来处理授权/认证用户,这是不可接受的,但在某些情况下它就足够了。

It is not acceptable if you need spring security with handling authorized/authenticated user but in some cases it is enough.

解决方案的某些部分可以改进,但我希望这个想法很清楚。

Some parts of the solution could be improved but I hope that idea is clear in general.

这篇关于为可信空间定制Spring Security的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆