Spring Security中的UsernamePasswordAuthenticationFilter不会被调用

UsernamePasswordAuthenticationFilter in spring Security doesn't get invoke

我想在登录时传递JSON而不是使用参数。所以我要做的是创建一个过滤器,但是,奇怪的是,过滤器本身根本不会被调用(或者基本上是当我尝试时登录,通过传递请求,完全忽略我的过滤器)。该请求直接进入我的AuthenticationHandler。我已经浏览了许多文章,但仍然不知道为什么会发生这种情况,特别是当我在Java中复制相同的代码结构但它按预期的方式完美工作时...

我错过了明显的事情吗?这是UsernamePasswordAuthenticationFilter和我的安全性配置。我的Java版本工作正常,但我的Kotlin版本完全忽略了过滤器。

它也不返回404,它返回我的AuthenticationFailureHandler。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.ObjectMapper
import lombok.Getter
import org.apache.commons.io.IOUtils
import org.springframework.http.HttpMethod
import org.springframework.security.authentication.AuthenticationServiceException
import org.springframework.security.authentication.InternalAuthenticationServiceException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import java.io.IOException
import java.nio.charset.Charset

class JsonLoginFilter : UsernamePasswordAuthenticationFilter() {

     @Throws(AuthenticationException::class)
     override fun attemptAuthentication(request: HttpServletRequest, response: HttpServletResponse?): Authentication {
         if (!HttpMethod.POST.matches(request.method)) {
            throw AuthenticationServiceException("Authentication method not supported:" + request.method)
         }

         val payload: String
         try {
              payload = IOUtils.toString(request.inputStream, Charset.defaultCharset())
              val auth = ObjectMapper().readValue(payload, JsonAuthenticationParser::class.java)
             // println(auth.username)
             // println(auth.password)
              val authRequest = UsernamePasswordAuthenticationToken(auth.username, auth.password)
              return this.authenticationManager.authenticate(authRequest)
         } catch (e: IOException) {
              throw InternalAuthenticationServiceException("Could not parse authentication payload")
         }

    }

    @Getter
    data class JsonAuthenticationParser @JsonCreator
    constructor(@param:JsonProperty("username")
                 val username: String,
                @param:JsonProperty("password")
                 val password: String)
    }

我在Kotlin中的安全性配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler

@EnableWebSecurity
class WebSecurity: WebSecurityConfigurerAdapter() {

    @Autowired
    private lateinit var entryConfig: EntryConfig

    @Autowired
    private lateinit var failAuth: FailAuthentication

    @Autowired
    private lateinit var successAuthentication: SuccessAuthentication

    @Autowired
    private lateinit var authenticationHandler: AuthenticationHandler

    @Throws(Exception::class)
    override fun configure(http: HttpSecurity) {

        http
                .authorizeRequests()
                .antMatchers("/api/v1/traveller/add","/api/v1/symptoms","/api/v1/flights","/api/v1/user/login","/api/v1/user/logout").permitAll()
                .antMatchers("/api/v1/user/**","/api/v1/traveller/**").hasRole("ADMIN")
                .antMatchers("/**").authenticated()
                .and()
                .addFilterAt(authenFilter(), UsernamePasswordAuthenticationFilter::class.java)
                .formLogin().loginProcessingUrl("/api/v1/user/login")
                .successHandler(successAuthentication).failureHandler(failAuth)
                .and()
                .exceptionHandling().authenticationEntryPoint(entryConfig)
                .and()
                .cors()
                .and()
                .logout().logoutUrl("/api/v1/user/logout")
                .clearAuthentication(true)
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .logoutSuccessHandler(HttpStatusReturningLogoutSuccessHandler())
                .permitAll()


        //

        http
                .csrf()
                .disable()
    }


    @Throws(Exception::class)
    override fun configure(auth: AuthenticationManagerBuilder) {
        auth.authenticationProvider(authenticationHandler)
    }

    @Bean
    @Throws(Exception::class)
    fun authenFilter(): JsonLoginFilter {
        var filter : JsonLoginFilter = JsonLoginFilter()
        filter.setAuthenticationManager(authenticationManagerBean())
        filter.setAuthenticationSuccessHandler(successAuthentication)
        filter.setAuthenticationFailureHandler(failAuth)
        return filter
    }


    @Bean
    fun passwordEncoder(): BCryptPasswordEncoder {
        return BCryptPasswordEncoder()
    }

}

我的Java版本略有不同,但我认为它应该具有相同的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;

public class JsonAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        System.out.println("hello");
        if (! HttpMethod.POST.matches(request.getMethod())) {
            throw new AuthenticationServiceException("Authentication method not supported:" + request.getMethod());
        }

        String payload;
        try {
            payload = IOUtils.toString(request.getInputStream(), Charset.defaultCharset());
            JsonAuthenticationParser auth = new ObjectMapper().readValue(payload, JsonAuthenticationParser.class);
            System.out.println(auth.username);
            System.out.println(auth.password);
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(auth.username, auth.password);

            return this.getAuthenticationManager().authenticate(authRequest);
        } catch (IOException e) {
            throw new InternalAuthenticationServiceException("Could not parse authentication payload");
        }
    }

    @Getter
    static class JsonAuthenticationParser {
        private final String username;
        private final String password;

        @JsonCreator
        public JsonAuthenticationParser(@JsonProperty("username") String username, @JsonProperty("password") String password) {
            this.username = username;
            this.password = password;
        }
    }
}

Java中的安全性配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import hard.string.security.AuthenticationHandler;
import hard.string.security.EntryConfig;
import hard.string.security.FailAuthhentication;
import hard.string.security.SuccessAuthentication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    @Autowired
    private EntryConfig entryConfig;

    @Autowired
    private FailAuthhentication failAuth;

    @Autowired
    private SuccessAuthentication successAuthentication;

    @Autowired
    private AuthenticationHandler authenticationHandler;

    @Bean
    public JsonAuthenticationFilter authenticationFilter() throws Exception {
        JsonAuthenticationFilter filter = new JsonAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());
//        filter.setContinueChainBeforeSuccessfulAuthentication(true);
        filter.setAuthenticationSuccessHandler(successAuthentication);
        filter.setAuthenticationFailureHandler(failAuth);
        return filter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http://stackoverflow.com/questions/19500332/spring-security-and-json-authentication

        http
                .authorizeRequests()
                .antMatchers("/login","/logout","/register",
                       "/debug/**").permitAll()
                .antMatchers("/**").authenticated()
                .and()
                .addFilterAt(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .formLogin().loginProcessingUrl("/login")
                .successHandler(successAuthentication).failureHandler(failAuth)
                .and()
                .exceptionHandling().authenticationEntryPoint(entryConfig)
                .and()
                .cors()
                .and()
                .logout().logoutUrl("/logout")
                .clearAuthentication(true)
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
                .permitAll();


        //

        http
                .csrf()
                .disable();
    }

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

    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

}

感谢您的帮助


好吧,花了几天的时间找到bug之后。我发现过滤器不会自动与loginProcessingUrl链接。您需要指定要在其上进行过滤的url,否则它将仅将过滤器仅应用于localhost:xxxx / login

我只是将这个问题留在这里,以防万一有人像我一样遇到这个愚蠢的问题。

1
2
3
4
5
6
7
8
fun authenFilter(): JsonLoginFilter {
        var filter : JsonLoginFilter = JsonLoginFilter()
        filter.setAuthenticationManager(authenticationManagerBean())
        filter.setAuthenticationSuccessHandler(successAuthentication)
        filter.setAuthenticationFailureHandler(failAuth)
        filter.setFilterProcessesUrl("/api/v1/user/login") //HERE
        return filter
    }