Spring Security过滤器未启动

时间:2018-08-04 15:12:33

标签: spring-boot spring-security

我的身份验证过滤器未根据请求启动。

我有2种安全配置,一种仅用于登录端点,通过AuthenticationFromCredentialsFilter过滤器进行身份验证,另一种用于其他端点,通过AuthenticationFromTokenFilter过滤器进行身份验证。

我希望调用过滤器的attemptAuthentication方法,但不会。

是否更愿意在过滤器中而不是在登录控制器中对凭据进行身份验证并创建令牌?

目前存在登录控制器,但是它不应该存在,因为它的工作应该由过滤器完成。

我将它们分别设置为安全配置:

@EnvProd
@EnableWebSecurity
@ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.user.rest.security", "com.thalasoft.user.rest.filter" })
public class WebSecurityConfiguration {

    @Order(1)
    @Configuration
    public class CredentialsConfiguration extends WebSecurityConfigurerAdapter {

        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }

        public AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter() throws Exception {
            AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter = new AuthenticationFromCredentialsFilter();
            authenticationFromCredentialsFilter.setAuthenticationManager(authenticationManagerBean());
            return authenticationFromCredentialsFilter;
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/api/users/login")
            .addFilterBefore(authenticationFromCredentialsFilter(), UsernamePasswordAuthenticationFilter.class)
            .authorizeRequests()
            .antMatchers("/api/users/login").permitAll()
            .anyRequest().authenticated();
        }
    }

    @Order(2)
    @Configuration
    public class TokenConfiguration extends WebSecurityConfigurerAdapter {

        @Autowired
        private AuthenticationFromTokenFilter authenticationFromTokenFilter;

        @Autowired
        private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;

        @Autowired
        private SimpleCORSFilter simpleCORSFilter;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .csrf().disable();

            http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

            http
                .headers()
                .cacheControl().disable()
                .frameOptions().disable();

            http
                .httpBasic()
                .authenticationEntryPoint(restAuthenticationEntryPoint);

            http 
                .addFilterBefore(simpleCORSFilter, UsernamePasswordAuthenticationFilter.class);

            http
                .addFilterBefore(authenticationFromTokenFilter, UsernamePasswordAuthenticationFilter.class);

            http.antMatcher("/api/**")
                .addFilterBefore(authenticationFromTokenFilter(), UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/error").permitAll()
                .antMatchers("/admin/**").hasRole(UserDomainConstants.ROLE_ADMIN)
                .anyRequest().authenticated();    
        }
    }
}

这是两个过滤器:

public class AuthenticationFromCredentialsFilter extends UsernamePasswordAuthenticationFilter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenAuthenticationService tokenAuthenticationService;

    @Autowired
    CredentialsService credentialsService;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
            throws AuthenticationException {
        try {
            CredentialsResource credentialsResource = new ObjectMapper().readValue(req.getInputStream(),
                    CredentialsResource.class);
            return authenticationManager.authenticate(credentialsService.authenticate(credentialsResource));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            Authentication authentication) throws IOException, ServletException {
        tokenAuthenticationService.addTokenToResponseHeader(response, authentication);
    }

}

public class AuthenticationFromTokenFilter extends UsernamePasswordAuthenticationFilter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenAuthenticationService tokenAuthenticationService;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        tokenAuthenticationService.authenticate(request);
        return authenticationManager.authenticate(tokenAuthenticationService.authenticate(request));
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            Authentication authentication) throws IOException, ServletException {
    }

}

这是一个登录请求的示例,该登录请求应由安全配置中的AuthenticationFromCredentialsFilter过滤器捕获,但不是,因此允许其进入控制器并给出一个{{1 }}状态:

201

我是否可以期望登录请求启动$ curl -i -H "Accept:application/json" -H "Content-Type: application/json" "http://localhost:8080/api/users/login" -X POST -d "{ \"email\" : \"xxxxxx@yahoo.se\", \"password\" : \"xxxxx\" }" HTTP/1.1 201 Cache-Control: no-store Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzQwNTE5MjYsInN1YiI6Im1pdHRpcHJvdmVuY2VAeWFob28uc2UifQ.LOJvr5jWouWsLN_Pinlr_F5dntON45hwpUFVmXD2Xqo Location: http://localhost:8080/api/users/1 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 05 Aug 2018 05:32:07 GMT {"firstname":"Stephane","lastname":"Eybert","email":"xxxxx@yahoo.se","confirmedEmail":false,"password":"bWl0dGlwcm92ZW5jZUB5YWhvby5zZTptaWduZXQxYjE4ZDQ5MS00ZGRhLTQxZWYtYWM5ZS04N2Y5ODk=","workPhone":null,"userRoles":[{"role":"ROLE_ADMIN","id":1}],"_links":{"self":{"href":"http://localhost:8080/api/users/1"},"roles":{"href":"http://localhost:8080/api/users/1/roles"}},"id":1}[stephane@stephane-ThinkPad-X201 user-rest (master)] 过滤器?使用过滤器进行身份验证并使用令牌响应吗?而且根本没有调用登录控制器吗?

这是密码更改请求的另一个示例,应该由安全配置中的AuthenticationFromCredentialsFilter过滤器捕获,但是没有,因此允许其继续向控制器发送并以{{ 1}}状态:

AuthenticationFromTokenFilter

对于登录请求使用200而不是$ curl -i -H "Accept:application/json" -H "Content-Type: application/json" "http://localhost:8080/api/users/1/password" -X PUT -d "\"xxxxx\"" HTTP/1.1 200 Cache-Control: no-store Location: http://localhost:8080/api/users/1 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sat, 04 Aug 2018 20:23:17 GMT {"firstname":"Stephane","lastname":"Eybert","email":"xxxx@yahoo.se","confirmedEmail":false,"password":"bWl0dGlwcm92ZW5jZUB5YWhvby5zZTptaWduZXRhYTA4OTNiZS0yMzZlLTQ3ZjktOTE2Ny0zOTU0NTY=","workPhone":null,"userRoles":[{"role":"ROLE_ADMIN","id":1}],"_links":{"self":{"href":"http://localhost:8080/api/users/1"},"roles":{"href":"http://localhost:8080/api/users/1/roles"}},"id":1} 过滤器怎么办?在CustomAuthenticationProvider implements AuthenticationProvider下仍然可行吗?

我在想类似的东西:

AuthenticationFromCredentialsFilter

身份验证提供者为:

Spring Boot 2.0.3

更新:我也尝试了这种配置,但是它并没有改变问题:

@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(customAuthenticationProvider);
}

2 个答案:

答案 0 :(得分:0)

您的过滤器扩展了var fe = []; var obj = []; homeShapeShift.forEach(function(element) { element.forEach(function(element) { if(element.text !== 'ZEC'){ obj.push(element); } }); fe.push(obj); obj = []; }); ,默认情况下,此过滤器仅适用于URL UsernamePasswordAuthenticationFilter,请参见UsernamePasswordAuthenticationFilter

  

此过滤器默认情况下会响应URL /login

如果要将默认设置更改为另一个URL,请参见AbstractAuthenticationProcessingFilter#setFilterProcessesUrl

  

设置用于确定是否需要身份验证的URL

您修改的代码:

/login

如果要使用模式,请参见AbstractAuthenticationProcessingFilter

  

如果请求与public AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter() throws Exception { AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter = new AuthenticationFromCredentialsFilter(); authenticationFromCredentialsFilter.setAuthenticationManager(authenticationManagerBean()); authenticationFromCredentialsFilter.setFilterProcessesUrl("/api/users/login"); return authenticationFromCredentialsFilter; } 相匹配,此过滤器将拦截请求并尝试从该请求执行身份验证。

您修改的代码:

setRequiresAuthenticationRequestMatcher(RequestMatcher)

答案 1 :(得分:0)

我最终可以在Spring Boot 2环境下启动过滤器,并针对各自的请求启动过滤器。

凭据过滤器会根据对/users/login端点的登录请求触发。

令牌过滤器会在登录请求以外的任何请求上触发。

要将URL模式分配给过滤器,过滤器必须扩展AbstractAuthenticationProcessingFilter类。这两个过滤器扩展了该类。模式是在其构造函数中指定的。

凭据过滤器为:

public class AuthenticationFromCredentialsFilter extends AbstractAuthenticationProcessingFilter {

    @Autowired
    private TokenAuthenticationService tokenAuthenticationService;

    @Autowired
    CredentialsService credentialsService;

    public AuthenticationFromCredentialsFilter(final RequestMatcher requestMatcher) {
        super(requestMatcher);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
            throws AuthenticationException {
        try {
            CredentialsResource credentialsResource = new ObjectMapper().readValue(req.getInputStream(),
                    CredentialsResource.class);
            return credentialsService.authenticate(credentialsResource);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            Authentication authentication) throws IOException, ServletException {
        tokenAuthenticationService.addTokenToResponseHeader(response, authentication);
    }

}

请注意,在成功进行身份验证后,不会继续使用过滤器链,即没有对filterChain.doFilter(httpRequest, httpResponse);的调用,因为不需要命中控制器端点,因为响应已经与令牌一起发送回了

使用@Bean注释显式实例化它以指定匹配器模式:

@Bean
public AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter() throws Exception {
    AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter = new AuthenticationFromCredentialsFilter(new AntPathRequestMatcher("/users/login", RequestMethod.POST.name()));
    authenticationFromCredentialsFilter.setAuthenticationManager(authenticationManagerBean());
    return authenticationFromCredentialsFilter;
}

令牌过滤器为:

public class AuthenticationFromTokenFilter extends AbstractAuthenticationProcessingFilter {

    @Autowired
    private TokenAuthenticationService tokenAuthenticationService;

    public AuthenticationFromTokenFilter(final RequestMatcher requestMatcher) {
        super(requestMatcher);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        return tokenAuthenticationService.authenticate(request);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
            FilterChain filterChain, Authentication authResult) throws IOException, ServletException {
        filterChain.doFilter(httpRequest, httpResponse);
    }

}

使用@Bean注释显式实例化它以指定匹配器模式:

@Bean
public AuthenticationFromTokenFilter authenticationFromTokenFilter() throws Exception {
    AuthenticationFromTokenFilter authenticationFromTokenFilter = new AuthenticationFromTokenFilter(new NegatedRequestMatcher(new AntPathRequestMatcher("/users/login", RequestMethod.POST.name())));
    authenticationFromTokenFilter.setAuthenticationManager(authenticationManagerBean());
    return authenticationFromTokenFilter;
}

与先前的凭据过滤器相反,此过滤器需要在配置中明确指定:

http
.addFilterBefore(authenticationFromTokenFilter, UsernamePasswordAuthenticationFilter.class)

为避免此过滤器在过滤器链中启动两次,bean指示Spring Boot不要自动注入它:

@Bean
FilterRegistrationBean<AuthenticationFromTokenFilter> disableAutoRegistration(final AuthenticationFromTokenFilter filter) {
    final FilterRegistrationBean<AuthenticationFromTokenFilter> registration = new FilterRegistrationBean<AuthenticationFromTokenFilter>(filter);
    registration.setEnabled(false);
    return registration;
}

请注意,在成功通过身份验证后,将继续执行过滤器链,即调用filterChain.doFilter(httpRequest, httpResponse);,因为在成功通过身份验证后需要命中控制器端点。

完整配置为:

@ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.user.rest.security",
"com.thalasoft.user.rest.service", "com.thalasoft.user.rest.filter" })
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    private SimpleCORSFilter simpleCORSFilter;

    @Autowired
    AuthenticationFromTokenFilter authenticationFromTokenFilter;

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter() throws Exception {
        AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter = new AuthenticationFromCredentialsFilter(new AntPathRequestMatcher("/users/login", RequestMethod.POST.name()));
        authenticationFromCredentialsFilter.setAuthenticationManager(authenticationManagerBean());
        return authenticationFromCredentialsFilter;
    }

    @Bean
    public AuthenticationFromTokenFilter authenticationFromTokenFilter() throws Exception {
        AuthenticationFromTokenFilter authenticationFromTokenFilter = new AuthenticationFromTokenFilter(new NegatedRequestMatcher(new AntPathRequestMatcher("/users/login", RequestMethod.POST.name())));
        authenticationFromTokenFilter.setAuthenticationManager(authenticationManagerBean());
        return authenticationFromTokenFilter;
    }

    @Bean
    FilterRegistrationBean<AuthenticationFromTokenFilter> disableAutoRegistration(final AuthenticationFromTokenFilter filter) {
        final FilterRegistrationBean<AuthenticationFromTokenFilter> registration = new FilterRegistrationBean<AuthenticationFromTokenFilter>(filter);
        registration.setEnabled(false);
        return registration;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors();

        http
        .csrf().disable()
        .formLogin().disable()
        .httpBasic().disable()
        .logout().disable();

        http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(simpleCORSFilter, ChannelProcessingFilter.class);

        http
        .addFilterBefore(authenticationFromTokenFilter, UsernamePasswordAuthenticationFilter.class)
        .authorizeRequests()
        .antMatchers("/", "/error").permitAll()
        .antMatchers("/users/login").permitAll()
        .antMatchers("/admin/**").hasRole(UserDomainConstants.ROLE_ADMIN)
        .anyRequest().authenticated();
    }

}

另外,请注意,在此Spring Boot配置中根本没有使用:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    CredentialsService credentialsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        return credentialsService.authenticate(authentication);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }

}

我想知道它适合什么地方以及如何替换凭据过滤器...但这是另一回事了。