【问题标题】:SecurityContextHolder returns null after I call setAuthentication with the custom authentication token使用自定义身份验证令牌调用 setAuthentication 后,SecurityContextHolder 返回 null
【发布时间】:2020-07-20 19:49:11
【问题描述】:

我有一个小型 Spring Boot 项目,它具有用户名/密码身份验证。我在我们的 LDAP 连接中使用 spring-security 登录。我想要并且我确实设法在几个项目中做的是扩展 AbstractAuthenticationToken 类以添加我自己的字段。

在我的自定义 GenericFilterBean 类中,我想创建自己的 authentication 对象并设置到 SecurityContextHolder 中,如下所示:

KfsMsgToken kfsMsgToken = new KfsMsgToken(
                    kfsInMessageInfo.getObjId(),
                    new ArrayList<>());

SecurityContextHolder.getContext().setAuthentication(kfsMsgToken);

而且,这是我自定义的 Authentication 类:

public class KfsMsgToken extends AbstractAuthenticationToken {

    String kfsInMsgOid;

    public KfsMsgToken(String kfsInMsgOid, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.kfsInMsgOid = kfsInMsgOid;
    }

    /**
     * 
     * @return
     */
    @Override
    public Object getCredentials() {
        return null;
    }

    /**
     * 
     * @return
     */
    @Override
    public Object getPrincipal() {
        return null;
    }

    public String getKfsInMsgOid() {
        return kfsInMsgOid;
    }

    public void setKfsInMsgOid(String kfsInMsgOid) {
        this.kfsInMsgOid = kfsInMsgOid;
    }

}

问题是,登录成功后,我看到 UsernamePasswordAuthenticationToken 已经设置好了。我使用自定义令牌对象重置身份验证字段,在服务层返回 null。不知道是什么原因。

感谢所有建议!

我的安全配置:

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Value("${ldap.urls}")
    private String ldapUrl;
    @Value("${ldap.domain}")
    private String ldapDomain;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/css/**", "/fonts/**", "/img/**", "/js/**", "/pdf/**").permitAll()
                .and().formLogin().defaultSuccessUrl("/index", true).loginProcessingUrl("/login").permitAll().and()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .csrf().disable();
    }

    @Bean
    public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
        ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider = new
                ActiveDirectoryLdapAuthenticationProvider(ldapDomain, ldapUrl);

        return activeDirectoryLdapAuthenticationProvider;
    }
}

我如何注册我的过滤器:

    @Bean
    public FilterRegistrationBean jwtFilter() {
        final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new KfsInMsgFilter(kfsInMessageService, kfsMsgToken, messageChannelService));

        registrationBean.setOrder(0);
        registrationBean.addUrlPatterns(SECURE);
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST);

        return registrationBean;
    }

这就是我在过滤器中所做的:

public class KfsInMsgFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

       ...
            // at this point, Authentication holds UserNamePasswordAuthenticationToken
            KfsMsgToken kfsMsgToken = new KfsMsgToken(
                    kfsInMessageInfo.getObjId(),
                    new ArrayList<>());

            SecurityContextHolder.getContext().setAuthentication(kfsMsgToken);

       ...
    } 
}

【问题讨论】:

  • 你的自定义GenericFilterBean是在UsernamePasswordAuthenticationProcessingFilter之前还是之后?
  • 由于我没有实现 UsernamePasswordAuthenticationProcessingFilter 并且在更改之前我在 SecurityContextHolder 中看到了 UsernamePasswordAuthenticationToken ,所以似乎之前。
  • 请发布您的 spring 安全配置。
  • 我已经按照您的要求添加了配置文件。
  • 您的自定义 GenericFilterBean 不可见?您是如何将其连接到安全过滤器链的?

标签: spring-boot spring-security


【解决方案1】:

您需要扩展 AbstractAuthenticationProcessingFilter 并在其中进行自定义 LDAP 身份验证并注册并最终使用

http.addFilterAfter(
          new CustomFilter(), UsernamePasswordAuthenticationProcessingFilter.class);

【讨论】:

  • 感谢您的回复。但我不明白 manuel ldap 身份验证对我有什么帮助?我的自定义过滤器在身份验证后已经工作。即使我这样做了,我将如何完成将自定义授权令牌设置为 SecurityContextHolder ?
【解决方案2】:

在我的自定义令牌上设置 authenticated 字段,

kfsMsgToken.setAuthenticated(Boolean.TRUE.booleanValue());

解决了我的问题。

但是,

我认为有更好的方法。使用作用域 bean 更加有效和实用。

我将自定义过滤器更改为请求范围的 bean:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@Scope(value="request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class KfsMsgToken {
    String kfsInMsgOid;
}

通过自动装配,我可以获取并读取我需要的值。

【讨论】:

    猜你喜欢
    • 2017-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-03
    • 2011-03-18
    • 2023-03-12
    • 1970-01-01
    • 2019-10-25
    相关资源
    最近更新 更多