Java Spring 安全自定义令牌过滤器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22361365/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:14:56  来源:igfitidea点击:

Spring security custom token filter

javaspringspring-securityspring-java-config

提问by Federico

I'm trying to perform a custom filter to get a token and validate it. I'm following the approach in this response.

我正在尝试执行自定义过滤器以获取令牌并对其进行验证。我正在遵循此响应中的方法。

This is the relevant configuration:

这是相关的配置:

SecurityConfig:

安全配置:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = {"com.company.app"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Inject
AuthenticationTokenFilter authenticationTokenFilter;

@Inject
TokenAuthenticationProvider tokenAuthenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(authenticationTokenFilter, BasicAuthenticationFilter.class)
                .antMatcher("/*")
                .authenticationProvider(tokenAuthenticationProvider)
                .authorizeRequests()
                    .anyRequest().authenticated();
    }

}

AuthenticationTokenFilter:

身份验证令牌过滤器:

@Component
public class AuthenticationTokenFilter implements Filter {

private static final Logger logger = LoggerFactory.getLogger(AuthenticationTokenFilter.class);

@Override
public void init(FilterConfig fc) throws ServletException {
    logger.info("Init AuthenticationTokenFilter");
}

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException {
    SecurityContext context = SecurityContextHolder.getContext();
    if (context.getAuthentication() != null && context.getAuthentication().isAuthenticated()) {
        // do nothing
    } else {
        Map<String,String[]> params = req.getParameterMap();
        if (!params.isEmpty() && params.containsKey("auth_token")) {
            String token = params.get("auth_token")[0];
            if (token != null) {
                Authentication auth = new TokenAuthentication(token);
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }
    }

    fc.doFilter(req, res);
}

@Override
public void destroy() {

}
}

TokenAuthentication:

令牌认证:

public class TokenAuthentication implements Authentication {
private String token;

public TokenAuthentication(String token) {
    this.token = token;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    return new ArrayList<GrantedAuthority>(0);
}
@Override
public Object getCredentials() {
    return token;
}
@Override
public Object getDetails() {
    return null;
}
@Override
public Object getPrincipal() {
    return null;
}
@Override
public boolean isAuthenticated() {
    return false;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
    return null;
}
}

TokenAuthenticationProvider:

TokenAuthenticationProvider:

@Component
public class TokenAuthenticationProvider implements AuthenticationProvider {

private static final Logger logger = LoggerFactory.getLogger(TokenAuthenticationProvider.class);

@Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {
    if (auth.isAuthenticated())
        return auth;

    String token = auth.getCredentials().toString();
    User user = userSvc.validateApiAuthenticationToken(token);
    if (user != null) {
        auth = new PreAuthenticatedAuthenticationToken(user, token);
        auth.setAuthenticated(true);
        logger.debug("Token authentication. Token: ");
    } else
        throw new BadCredentialsException("Invalid token " + token);
    return auth;
}

@Override
public boolean supports(Class<?> aClass) {
    return true;
}

}

But it's like the AuthenticationTokenFilter is not being added to the chain. Debugging I can see that when I do a call it enters to the SecurityConfig and configure method but not to the filter. What is missing?

但这就像 AuthenticationTokenFilter 没有被添加到链中。调试我可以看到,当我进行调用时,它会进入 SecurityConfig 和 configure 方法,但不会进入过滤器。有什么不见了?

回答by Eugen Halca

try to disable anonymousauthentication and change to fullyauthentication to your security rule.

尝试禁用anonymous身份验证并更改fully为您的安全规则的身份验证。

something like this :

像这样:

http
    .addFilterBefore(authenticationTokenFilter, BasicAuthenticationFilter.class)
                    .antMatcher("/token")
                    .authenticationProvider(tokenAuthenticationProvider)
                    .authorizeUrls().anyRequest().fullyAuthenticated()
    .and()
                    .anonymous().disable()  

回答by kedzi

What you are missing is

你缺少的是

<filter>
       <filter-name>springSecurityFilterChain</filter-name>
       <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
       <filter-name>springSecurityFilterChain</filter-name>
       <url-pattern>/*</url-pattern>
</filter-mapping>

in your web.xml or equivalent for intializers on your classpath:

在您的 web.xml 或类路径上的初始化程序的等效项中:

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

@Order(value = 1)
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}

This is separate from your WebApplicationInitializer. Note that:

这与您的 WebApplicationInitializer 是分开的。注意:

  • your SecurityConfig (or anything annotated with @EnableWebSecurity) must be defined in the root context (not the dispatcher context)
  • you probably should understand order of initialization(I am not sure if I do):
  • 您的 SecurityConfig(或任何用 @EnableWebSecurity 注释的东西)必须在根上下文(而不是调度程序上下文)中定义
  • 您可能应该了解初始化的顺序(我不确定我是否了解):

"Ordering of WebApplicationInitializer"
If any servlet Filter mappings are added after AbstractSecurityWebApplicationInitializer is invoked, they might be accidentally added before springSecurityFilterChain. Unless an application contains Filter instances that do not need to be secured, springSecurityFilterChain should be before any other Filter mappings. The @Order annotation can be used to help ensure that any WebApplicationInitializer is loaded in a deterministic order.

“WebApplicationInitializer 的排序”
如果在调用 AbstractSecurityWebApplicationInitializer 之后添加了任何 servlet Filter 映射,它们可能会被意外添加到 springSecurityFilterChain 之前。除非应用程序包含不需要保护的 Filter 实例,否则 springSecurityFilterChain 应该在任何其他 Filter 映射之前。@Order 注释可用于帮助确保以确定性顺序加载任何 WebApplicationInitializer。

Example:

例子:

@Order(value = 10)
public class AppWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override
  protected Class<?>[] getRootConfigClasses() {
      return new Class<?>[] { AppConfig.class, SecurityConfig.class };
  }

  @Override
  protected Class<?>[] getServletConfigClasses() {
      return new Class<?>[] { RestConfig.class };
  }

  @Override
  protected String[] getServletMappings() {
      return new String[] { "/rest/*"};
  }
}


To summarize, from Spring documentation:


总而言之,来自 Spring 文档:

When using servlet filters, you obviously need to declare them in your web.xml, or they will be ignored by the servlet container. In Spring Security, the filter classes are also Spring beans defined in the application context and thus able to take advantage of Spring's rich dependency-injection facilities and lifecycle interfaces. Spring's DelegatingFilterProxy provides the link between web.xml and the application context.

使用 servlet 过滤器时,您显然需要在 web.xml 中声明它们,否则它们将被 servlet 容器忽略。在 Spring Security 中,过滤器类也是在应用程序上下文中定义的 Spring bean,因此能够利用 Spring 丰富的依赖注入设施和生命周期接口。Spring 的 DelegatingFilterProxy 提供了 web.xml 和应用程序上下文之间的链接。

The Security Filter Chain

安全过滤器链

回答by Dexter

Old post, but I think authenticationProvider() needs to come BEFORE "addBeforeFilter". Not sure if it will make a difference today, but it might be important. It may not matter as much.

旧帖子,但我认为 authenticationProvider() 需要在“addBeforeFilter”之前出现。不确定它今天是否会有所作为,但它可能很重要。可能没有那么重要。

Also try add this on your configuration class to solve the issue:

还可以尝试在您的配置类中添加它以解决问题:

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

回答by stelios.anastasakis

FYI: using both the @Componenton the Filter and @Injectwith addFilterBeforewill apply the filter twice! In your case it is just more processing time, so you wont see any errors. But if you are injecting lets say a metrics filter, then you will be getting wrong metrics.

仅供参考:@Component在过滤器上使用 和@InjectwithaddFilterBefore将应用过滤器两次!在你的情况下,它只是更多的处理时间,所以你不会看到任何错误。但是,如果您正在注入指标过滤器,那么您将获得错误的指标。